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 PressureCoefficientCalculatorWhenSingleTiltAnd128CellTest(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.Cell128.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.886544, 18.862247, 18.862247, 22.238823], 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.17, 0.15, 0.11, 0.10],
                           [0.17, 0.15, 0.11, 0.10],
                           [0.17, 0.15, 0.11, 0.10],
                           [0.17, 0.15, 0.11, 0.10],
                           [0.17, 0.15, 0.11, 0.10],
                           [0.17, 0.15, 0.11, 0.10],
                           [0.17, 0.15, 0.11, 0.10],
                           [0.17, 0.15, 0.11, 0.10],
                           [0.17, 0.15, 0.11, 0.10],
                           [0.17, 0.15, 0.11, 0.10],
                           [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_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.86, 1.70, 1.41, 1.37],
                                       [1.47, 1.33, 1.11, 1.08],
                                       [0.84, 0.78, 0.56, 0.54],
                                       [0.84, 0.76, 0.54, 0.52],
                                       [0.57, 0.34, 0.51, 0.33],
                                       [0.54, 0.49, 0.38, 0.36],
                                       [0.26, 0.23, 0.23, 0.23],
                                       [0.69, 0.62, 0.62, 0.60],
                                       [1.89, 1.66, 1.66, 1.59],
                                       [1.13, 1.01, 0.50, 0.48],
                                       [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.249994, 0.979863, 0.499699, 0.483089, 0.308544, 0.333895,
                    0.204906, 0.536295, 1.424674, 0.466723, 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 = [141.98, 144.67, 298.73, 849.16, 169.66, 63.78, 394.38, 162.38, 66.49, 15.88, 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 = [61, 63, 129, 365, 73, 28, 170, 70, 29, 7, 30]

        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, 17, 11, 29, 13, 16, 16, 14, 9, 20, 1]
        eq_(self.subject.minimum_array_size(self.subject.L_B()), expected)
