import unittest

import numpy
from nose.tools import assert_almost_equal, eq_
from numpy.testing import assert_array_almost_equal

from helix.calculators.pressure_coefficient_calculator import PressureCoefficientCalculator
from helix.constants.module_type import ModuleType
from helix.constants.system_type import SystemType
from helix.models.sql.sites import Site
from helix.user_values import UserValues


class PressureCoefficientCalculatorWhenSingleTiltAndPSeriesTest(unittest.TestCase):
    def setUp(self):
        self.site = Site(
            building_height=0,
            building_width=0,
            building_length=0,
            parapet_height=0,
            wind_speed=0,
            exposure_category='C',
            ballast_block_weight=0,
            max_psf=0,
            system_type=SystemType.singleTilt.value,
            module_type=ModuleType.PSeries.value,
            spectral_response=0,
            seismic_importance_factor=1,
        )
        self.subject = PressureCoefficientCalculator(UserValues(None, self.site))

    def test_A_n(self):
        result = self.subject.A_n(63.25)

        numpy.testing.assert_almost_equal(result, [10.386389, 17.995669, 17.995669, 21.217116], 6)

    def test_c_p_in_wind_zone_A(self):
        assert_almost_equal(self.subject.c_p(9.10, 'A', 1, 1), 1.22, 2)
        assert_almost_equal(self.subject.c_p(500.0, 'A', 1, 1), 0.34, 2)
        assert_almost_equal(self.subject.c_p(3000.0, 'A', 1, 1), 0.13, 2)

    def test_c_p_in_wind_zone_B(self):
        assert_almost_equal(self.subject.c_p(9.10, 'B', 1, 1), 0.96, 2)
        assert_almost_equal(self.subject.c_p(500.0, 'B', 1, 1), 0.25, 2)
        assert_almost_equal(self.subject.c_p(3000.0, 'B', 1, 1), 0.13, 2)

    def test_c_p_in_wind_zone_C(self):
        assert_almost_equal(self.subject.c_p(9.10, 'C', 1, 1), 0.48, 2)
        assert_almost_equal(self.subject.c_p(500.0, 'C', 1, 1), 0.19, 2)
        assert_almost_equal(self.subject.c_p(3000.0, 'C', 1, 1), 0.09, 2)

    def test_c_p_in_wind_zone_D(self):
        assert_almost_equal(self.subject.c_p(9.10, 'D', 1, 1), 0.46, 2)
        assert_almost_equal(self.subject.c_p(200, 'D', 1, 1), 0.16, 2)
        assert_almost_equal(self.subject.c_p(500.0, 'D', 1, 1), 0.12, 2)

    def test_c_p_in_wind_zone_E(self):
        assert_almost_equal(self.subject.c_p(9.10, 'E', 1, 1), 0.30, 2)
        assert_almost_equal(self.subject.c_p(200, 'E', 1, 1), 0.12, 2)
        assert_almost_equal(self.subject.c_p(317.22133, 'E', 1, 1), 0.09265, 4)
        assert_almost_equal(self.subject.c_p(500.0, 'E', 1, 1), 0.08, 2)

    def test_c_p_in_wind_zone_F(self):
        assert_almost_equal(self.subject.c_p(9.10, 'F', 1, 1), 0.32, 2)
        assert_almost_equal(self.subject.c_p(200, 'F', 1, 1), 0.12, 2)
        assert_almost_equal(self.subject.c_p(500.0, 'F', 1, 1), 0.08, 2)

    def test_c_p_in_wind_zone_G(self):
        assert_almost_equal(self.subject.c_p(9.10, 'G', 1, 1), 0.20, 2)
        assert_almost_equal(self.subject.c_p(200, 'G', 1, 1), 0.10, 2)
        assert_almost_equal(self.subject.c_p(500.0, 'G', 1, 1), 0.08, 2)

    def test_c_p_in_wind_zone_H(self):
        assert_almost_equal(self.subject.c_p(9.10, 'H', 1, 1), 0.53, 2)
        assert_almost_equal(self.subject.c_p(200, 'H', 1, 1), 0.18, 2)
        assert_almost_equal(self.subject.c_p(500.0, 'H', 1, 1), 0.11, 2)
        assert_almost_equal(self.subject.c_p(3000.0, 'H', 1, 1), 0.05, 2)

    def test_c_p_in_wind_zone_I(self):
        assert_almost_equal(self.subject.c_p(9.10, 'I', 1, 1), 1.41, 2)
        assert_almost_equal(self.subject.c_p(200, 'I', 1, 1), 0.23, 2)
        assert_almost_equal(self.subject.c_p(500.0, 'I', 1, 1), 0.11, 2)
        assert_almost_equal(self.subject.c_p(3000.0, 'I', 1, 1), 0.05, 2)

    def test_c_p_in_wind_zone_J(self):
        assert_almost_equal(self.subject.c_p(9.10, 'J', 1, 1), 0.43, 2)
        assert_almost_equal(self.subject.c_p(200, 'J', 1, 1), 0.12, 2)
        assert_almost_equal(self.subject.c_p(500.0, 'J', 1, 1), 0.08, 2)

    def test_c_p_matrix_when_building_dimensions_are_zero(self):
        self.site.building_height = 0
        self.site.building_width = 0
        self.site.building_length = 0
        self.site.building_parapet_height = 1.6  # so that parapet factor equals 1

        expected_matrix = [[0.18, 0.16, 0.11, 0.09],
                           [0.18, 0.16, 0.11, 0.09],
                           [0.18, 0.16, 0.11, 0.09],
                           [0.18, 0.16, 0.11, 0.09],
                           [0.18, 0.16, 0.11, 0.09],
                           [0.18, 0.16, 0.11, 0.09],
                           [0.18, 0.16, 0.11, 0.09],
                           [0.18, 0.16, 0.11, 0.09],
                           [0.18, 0.16, 0.11, 0.09],
                           [0.18, 0.16, 0.11, 0.09],
                           [0.18, 0.16, 0.11, 0.09]]
        numpy.testing.assert_almost_equal(self.subject.c_p_matrix(self.subject.L_B()), expected_matrix, 2)

    def test_c_p_matrix_compiles_all_the_panel_types_and_wind_zones_correctly(self):
        self.site.building_height = 100
        self.site.building_width = 1000
        self.site.building_length = 1500
        self.site.parapet_height = 20

        expected_matrix = numpy.array([[1.88, 1.71, 1.43, 1.39],
                                       [1.48, 1.34, 1.12, 1.09],
                                       [0.85, 0.78, 0.56, 0.55],
                                       [0.85, 0.77, 0.55, 0.53],
                                       [0.57, 0.35, 0.52, 0.34],
                                       [0.55, 0.49, 0.38, 0.37],
                                       [0.26, 0.24, 0.24, 0.23],
                                       [0.70, 0.62, 0.62, 0.60],
                                       [1.91, 1.68, 1.68, 1.61],
                                       [1.14, 1.02, 0.51, 0.49],
                                       [0.17, 0.15, 0.11, 0.10]])
        numpy.testing.assert_almost_equal(self.subject.c_p_matrix(self.subject.L_B()), expected_matrix, 2)

    def test_minimum_array_cp(self):
        # FIXME: Revisit this after Ian figures out how minimum array sizes change
        self.site.building_height = 100
        self.site.building_width = 1000
        self.site.building_length = 1500

        expected = [1.260703, 0.988535, 0.503255, 0.487961, 0.311363, 0.337041,
                    0.20651, 0.541755, 1.442809, 0.471883, 0.107297]

        assert_array_almost_equal(self.subject.ideal_subarray_average_uplift_c_p(self.subject.L_B()), expected, decimal=6)

    def test_minimum_A_n(self):
        # FIXME: Revisit this after Ian figures out how minimum array sizes change
        self.site.building_height = 100
        self.site.building_width = 1000
        self.site.building_length = 1500

        expected = [138.01, 140.27, 293.83, 834.55, 164.83, 61.01, 385.55,
                    158.9, 64.79, 14.99, 69.08]

        received = self.subject.minimum_A_n(self.subject.L_B())
        assert_array_almost_equal(received, expected, decimal=2)

    def test_minimum_array_size(self):
        # FIXME: Revisit this after Ian figures out how minimum array sizes change
        self.site.building_height = 100
        self.site.building_width = 1000
        self.site.building_length = 1500

        expected = [63, 64, 133, 376, 75, 28, 174, 72, 30, 7, 32]

        eq_(self.subject.minimum_array_size(self.subject.L_B()), expected)

    def test_minimum_array_size_smaller_array(self):
        # FIXME: Revisit this after Ian figures out how minimum array sizes change
        self.site.building_height = 15
        self.site.building_width = 500
        self.site.building_length = 500

        expected = [14, 18, 12, 30, 14, 16, 16, 14, 9, 21, 1]
        eq_(self.subject.minimum_array_size(self.subject.L_B()), expected)
