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 PressureCoefficientCalculatorWhenDualTiltAnd96CellTest(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.dualTilt.value,
            module_type=ModuleType.Cell96.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(35.777)
        numpy.testing.assert_almost_equal(result, [45.6, 90.37, 100.12, 298.59], 2)

    def test_c_p_in_wind_zone_A(self):
        assert_almost_equal(self.subject.c_p(597.17, 'A', 1, 1), 0.22, 2)
        assert_almost_equal(self.subject.c_p(597.17, 'A', 0.8, 1), 0.18, 2)
        assert_almost_equal(self.subject.c_p(1194.34, 'A', 1, 1), 0.15, 2)
        assert_almost_equal(self.subject.c_p(1194.34, 'A', 0.8, 1), 0.12, 2)

    def test_c_p_in_wind_zone_B(self):
        assert_almost_equal(self.subject.c_p(182.39, 'B', 1, 1), 0.27, 2)
        assert_almost_equal(self.subject.c_p(182.39, 'B', 0.8, 1), 0.22, 2)
        assert_almost_equal(self.subject.c_p(659.25, 'B', 1, 1), 0.13, 2)
        assert_almost_equal(self.subject.c_p(659.25, 'B', 0.8, 1), 0.11, 2)
        assert_almost_equal(self.subject.c_p(1194.34, 'B', 1, 1), 0.10, 2)
        assert_almost_equal(self.subject.c_p(1194.34, 'B', 0.8, 1), 0.08, 2)

    def test_c_p_in_wind_zone_C(self):
        assert_almost_equal(self.subject.c_p(182.39, 'C', 1, 1), 0.17, 2)
        assert_almost_equal(self.subject.c_p(182.39, 'C', 0.8, 1), 0.13, 2)
        assert_almost_equal(self.subject.c_p(659.25, 'C', 1, 1), 0.09, 2)
        assert_almost_equal(self.subject.c_p(659.25, 'C', 0.8, 1), 0.07, 2)
        assert_almost_equal(self.subject.c_p(1194.34, 'C', 1, 1), 0.08, 2)
        assert_almost_equal(self.subject.c_p(1194.34, 'C', 0.8, 1), 0.06, 2)

    def test_c_p_in_wind_zone_D(self):
        assert_almost_equal(self.subject.c_p(182.39, 'D', 1, 1), 0.10, 2)
        assert_almost_equal(self.subject.c_p(182.39, 'D', 0.8, 1), 0.08, 2)
        assert_almost_equal(self.subject.c_p(659.25, 'D', 1, 1), 0.05, 2)
        assert_almost_equal(self.subject.c_p(659.25, 'D', 0.8, 1), 0.04, 2)
        assert_almost_equal(self.subject.c_p(1194.34, 'D', 1, 1), 0.05, 2)
        assert_almost_equal(self.subject.c_p(1194.34, 'D', 0.8, 1), 0.04, 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.parapet_height = 1.6  # so that parapet factor equals 1

        expected_matrix = [[0.06, 0.05, 0.05, 0.04],
                           [0.06, 0.05, 0.05, 0.04],
                           [0.06, 0.05, 0.05, 0.04],
                           [0.06, 0.05, 0.05, 0.04],
                           [0.06, 0.05, 0.05, 0.04]]

        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 = 10.71428571  # so that parapet factor equals 1

        expected_matrix = [[0.89, 0.79, 0.78, 0.62],
                           [0.68, 0.60, 0.58, 0.46],
                           [0.42, 0.37, 0.36, 0.28],
                           [0.23, 0.21, 0.20, 0.16],
                           [0.06, 0.05, 0.05, 0.04]]

        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_using_parapet_factor(self):
        self.site.building_height = 100
        self.site.building_width = 1000
        self.site.building_length = 1500
        self.site.parapet_height = 20

        expected_matrix = [[1.00, 0.89, 0.87, 0.69],
                           [0.76, 0.67, 0.65, 0.51],
                           [0.47, 0.41, 0.41, 0.32],
                           [0.26, 0.23, 0.23, 0.18],
                           [0.07, 0.06, 0.06, 0.04]]

        numpy.testing.assert_almost_equal(self.subject.c_p_matrix(self.subject.L_B()), expected_matrix, 2)

    def test_minimum_array_cp(self):
        self.site.building_height = 100
        self.site.building_width = 1000
        self.site.building_length = 1500

        expected = [0.663381, 0.491003, 0.303695, 0.172534, 0.042343]

        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):
        self.site.building_height = 100
        self.site.building_width = 1000
        self.site.building_length = 1500

        expected = [103.23, 89.62, 107.24, 91.62, None]

        received = self.subject.minimum_A_n(self.subject.L_B())
        assert_array_almost_equal(received[:-1], expected[:-1], decimal=2)
        eq_(received[-1], expected[-1])

    def test_minimum_array_size(self):
        self.site.building_height = 100
        self.site.building_width = 1000
        self.site.building_length = 1500

        expected = [30, 26, 31, 27, 6]

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