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 PressureCoefficientCalculatorWhenDualTiltAnd128CellTest(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.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(35.777)
        numpy.testing.assert_almost_equal(result, [58.771147, 124.456547, 118.997927, 246.911600], 6)

    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.0  # so that parapet factor equals

        eq_(self.subject.parapet_factor(), 1)

        expected_matrix = [[0.854519, 0.746475, 0.752934, 0.647825],
                           [0.646303, 0.558517, 0.563765, 0.478364],
                           [0.400592, 0.345819, 0.349094, 0.295809],
                           [0.224301, 0.195039, 0.196788, 0.168321],
                           [0.060000, 0.050000, 0.052000, 0.039000]]

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

    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 = [[0.957062, 0.836052, 0.843286, 0.725564],
                           [0.723860, 0.625540, 0.631417, 0.535768],
                           [0.448663, 0.387318, 0.390985, 0.331306],
                           [0.251217, 0.218444, 0.220403, 0.188520],
                           [0.060000, 0.050000, 0.052000, 0.039000]]

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

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

        expected = [0.675935, 0.501203, 0.310059, 0.175934, 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
        self.site.parapet_height = 5

        expected = [96.531228, 84.051118, 100.871967, 85.479467, None]

        received = self.subject.minimum_A_n(self.subject.L_B())
        assert_array_almost_equal(received[:-1], expected[:-1], decimal=6)
        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
        self.site.parapet_height = 5

        expected = [21, 19, 22, 19, 6]

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