import unittest
from unittest.mock import MagicMock

from numpy import array
from numpy.testing import assert_almost_equal, assert_array_equal

from helix.calculators.coordinates_calculator import CoordinatesCalculator
from helix.calculators.subarray_helper import get_subarray_sizes_and_rows
from helix.constants.module_type_constants.dual_tilt_128_cell_constants import DualTilt128CellConstants
from helix.constants.module_type_constants.dual_tilt_96_cell_constants import DualTilt96CellConstants
from helix.constants.module_type_constants.single_tilt_128_cell_constants import SingleTilt128CellConstants
from helix.models.coordinate import Coordinate
from helix.models.panel import Panel
from helix.models.subarray import Subarray
from test.test_helpers import assert_array_is_close


class CoordinatesCalculatorTest(unittest.TestCase):
    def setUp(self):
        self.values = MagicMock()
        self.subject = CoordinatesCalculator(self.values)

    def test_transform_coordinates_96(self):
        panel_data = [
            Panel(subarray=0, coordinate=Coordinate(1595.70588, 1231.8347999999999, 30.054994934422535)),
            Panel(subarray=0, coordinate=Coordinate(1564.66923, 1285.4784, 30.06673752750664)),
            Panel(subarray=0, coordinate=Coordinate(1533.615, 1339.0778999999998, 30.06584756995154)),
            Panel(subarray=0, coordinate=Coordinate(1672.0707, 1276.0557, 30.066617663124482)),
            Panel(subarray=0, coordinate=Coordinate(1641.0339, 1329.6552, 30.055034861122245)),
            Panel(subarray=0, coordinate=Coordinate(1609.997106, 1383.2988, 30.066609672196513)),
            Panel(subarray=0, coordinate=Coordinate(1748.4354, 1320.2328, 30.054994934422535)),
            Panel(subarray=0, coordinate=Coordinate(1717.3987200000001, 1373.8763999999999, 30.066617663124553)),
            Panel(subarray=0, coordinate=Coordinate(1686.344487, 1427.4765, 30.066613667660015)),

            Panel(subarray=1, coordinate=Coordinate(1471.54131, 1446.3216, 30.06665761782229)),
            Panel(subarray=1, coordinate=Coordinate(1440.5046, 1499.96526, 30.05521279184761)),
            Panel(subarray=1, coordinate=Coordinate(1409.4678, 1553.6091, 30.066519581447444)),
            Panel(subarray=1, coordinate=Coordinate(1378.431, 1607.2090859999998, 30.054950584052726))
        ]

        # List of subarrays and size/origin info about them
        subarray_data = get_subarray_sizes_and_rows(panel_data)

        expected_panels = [
            Panel(subarray=0, coordinate=Coordinate(0, 0)),
            Panel(subarray=0, coordinate=Coordinate(0, 1)),
            Panel(subarray=0, coordinate=Coordinate(0, 2)),
            Panel(subarray=0, coordinate=Coordinate(1, 0)),
            Panel(subarray=0, coordinate=Coordinate(1, 1)),
            Panel(subarray=0, coordinate=Coordinate(1, 2)),
            Panel(subarray=0, coordinate=Coordinate(2, 0)),
            Panel(subarray=0, coordinate=Coordinate(2, 1)),
            Panel(subarray=0, coordinate=Coordinate(2, 2)),

            Panel(subarray=1, coordinate=Coordinate(0, 0)),
            Panel(subarray=1, coordinate=Coordinate(0, 1)),
            Panel(subarray=1, coordinate=Coordinate(0, 2)),
            Panel(subarray=1, coordinate=Coordinate(0, 3)),
        ]

        subarrays = [
            Subarray(origin=Coordinate(0.0007, 0), subarray_number=0, start_row=0, size=9),
            Subarray(origin=Coordinate(0, 3.9971), subarray_number=1, start_row=9, size=4),
        ]

        self.values.module_system_constants.return_value = DualTilt96CellConstants()

        received_panels, received_subarrays, recieved_buildings = self.subject.transform_coordinates(panel_data, subarray_data,[])

        assert_array_is_close(received_panels, expected_panels, decimal=4)
        assert_array_equal(received_subarrays, subarrays)
        assert_array_equal(recieved_buildings, [])

    def test_transform_coordinates_128(self):
        panel_data = [
            Panel(subarray=0, coordinate=Coordinate(19284.676,	12690.022,	345.000)),
            Panel(subarray=0, coordinate=Coordinate(19205.229,	12711.31,	345.000)),
            Panel(subarray=0, coordinate=Coordinate(19125.781,	12732.598,	345.000)),
            Panel(subarray=0, coordinate=Coordinate(19300.205,	12747.978,	345.000)),
            Panel(subarray=0, coordinate=Coordinate(19220.758,	12769.266,	345.000)),
            Panel(subarray=0, coordinate=Coordinate(19141.311,	12790.553,	345.000)),
            Panel(subarray=0, coordinate=Coordinate(19315.734,	12805.933,	345.000)),
            Panel(subarray=0, coordinate=Coordinate(19236.287,	12827.221,	345.000)),
            Panel(subarray=0, coordinate=Coordinate(19156.84,	12848.509,	345.000)),

            Panel(subarray=1, coordinate=Coordinate(19331.264,	12863.889,	345.000)),
            Panel(subarray=1, coordinate=Coordinate(19251.816,	12885.177,	345.000)),
            Panel(subarray=1, coordinate=Coordinate(19172.369,	12906.465,	345.000)),
        ]

        subarray_data = get_subarray_sizes_and_rows(panel_data)

        expected_panels = [
            Panel(subarray=0, coordinate=Coordinate(2, 0)),
            Panel(subarray=0, coordinate=Coordinate(1, 0)),
            Panel(subarray=0, coordinate=Coordinate(0, 0)),
            Panel(subarray=0, coordinate=Coordinate(2, 1)),
            Panel(subarray=0, coordinate=Coordinate(1, 1)),
            Panel(subarray=0, coordinate=Coordinate(0, 1)),
            Panel(subarray=0, coordinate=Coordinate(2, 2)),
            Panel(subarray=0, coordinate=Coordinate(1, 2)),
            Panel(subarray=0, coordinate=Coordinate(0, 2)),

            Panel(subarray=1, coordinate=Coordinate(2, 0)),
            Panel(subarray=1, coordinate=Coordinate(1, 0)),
            Panel(subarray=1, coordinate=Coordinate(0, 0)),
        ]

        subarrays = [
            Subarray(origin=Coordinate(0, 0), subarray_number=0, start_row=0, size=9),
            Subarray(origin=Coordinate(0.000006, 3.000008), subarray_number=1, start_row=9, size=3),
        ]

        self.values.module_system_constants.return_value = SingleTilt128CellConstants()

        received_panels, received_subarrays, _ = self.subject.transform_coordinates(panel_data, subarray_data, [])

        assert_array_is_close(received_panels, expected_panels, decimal=4)
        assert_array_equal(received_subarrays, subarrays)

    def test_transform_coordinates_doesnt_round_subarray_origins(self):
        panel_data = [
            Panel(subarray=7, coordinate=Coordinate(81260236.94, 24512483.07, 346)),
            Panel(subarray=7, coordinate=Coordinate(81260322.53, 24512461.63, 346)),

            Panel(subarray=4, coordinate=Coordinate(81260345.21, 24512649.57, 346)),
            Panel(subarray=4, coordinate=Coordinate(81260259.62, 24512671.01, 346)),
        ]

        # List of subarrays and size/origin info about them
        subarray_data = get_subarray_sizes_and_rows(panel_data)

        expected_panels = [
            Panel(subarray=7, coordinate=Coordinate(0, 0)),
            Panel(subarray=7, coordinate=Coordinate(1, 0)),

            Panel(subarray=4, coordinate=Coordinate(1, 0)),
            Panel(subarray=4, coordinate=Coordinate(0, 0)),
        ]

        subarrays = [
            Subarray(origin=Coordinate(0.265871, 0), subarray_number=7, start_row=0, size=2),
            Subarray(origin=Coordinate(0, 3.029744), subarray_number=4, start_row=2, size=2),
        ]

        self.values.module_system_constants.return_value = DualTilt96CellConstants()

        received_panels, received_subarrays, _ = self.subject.transform_coordinates(panel_data, subarray_data, [])

        assert_array_is_close(received_panels, expected_panels, decimal=4)
        assert_array_equal(received_subarrays, subarrays)

    def test_rotation(self):
        assert_almost_equal(self.subject.rotate(Coordinate(0, 1, 90)),
                            Coordinate(1, 0))
        assert_almost_equal(self.subject.rotate(Coordinate(4, 7, 53)),
                            Coordinate(8.00, 1.02), decimal=2)
        assert_almost_equal(self.subject.rotate(Coordinate(23, 79.5, -22.8)),
                            Coordinate(-9.605, 82.201), decimal=2)

    def test_scale_96(self):
        self.values.module_system_constants.return_value = DualTilt96CellConstants()
        coordinate = Coordinate(3, 4)

        assert_almost_equal(self.subject.scale(coordinate), Coordinate(3/DualTilt96CellConstants.panel_spacing[0], 4/DualTilt96CellConstants.panel_spacing[1]), decimal=6)

    def test_scale_128(self):
        self.values.module_system_constants.return_value = DualTilt128CellConstants()
        coordinate = Coordinate(3, 4)

        assert_almost_equal(self.subject.scale(coordinate), Coordinate(3/DualTilt128CellConstants.panel_spacing[0], 4/DualTilt128CellConstants.panel_spacing[1]), decimal=6)

    def test_find_origin(self):
        input_matrix = array([Coordinate(3, 4), Coordinate(28, 36), Coordinate(-57, 10)])
        assert_almost_equal(self.subject.find_origin(input_matrix), Coordinate(-57, 4))
