import unittest
from unittest.mock import MagicMock

from nose.tools import eq_

from helix.validators.csv_input_validator import CsvInputValidator
from helix.constants.system_type import SystemType
from helix.constants.module_type import ModuleType
from helix.constants.file_validation_error import FileValidationError, FileValidationMessage


class CsvInputValidatorTest(unittest.TestCase):
    def setUp(self):
        self.user_values = MagicMock()
        self.subject = CsvInputValidator(self.user_values)

    def should_have_error(self, result, message, row):
        eq_(result, FileValidationError(message, row))

    def test_reports_single_tilt_file_is_invalid_when_dual_tilt(self):
        self.user_values.system_type.return_value = SystemType.dualTilt
        with open('test/fixtures/input_single_tilt.csv', 'r', newline='') as csv_file:
            cad_input = csv_file.read()
        self.should_have_error(self.subject.validate(cad_input), FileValidationMessage.DualTiltWindZone, 1)

    def test_reports_single_tilt_file_is_valid_when_single_tilt(self):
        self.user_values.system_type.return_value = SystemType.singleTilt
        with open('test/fixtures/input_single_tilt.csv', 'r', newline='') as csv_file:
            cad_input = csv_file.read()
        eq_(self.subject.validate(cad_input), None)

    def test_reports_dual_tilt_file_is_valid_when_dual_tilt(self):
        self.user_values.system_type.return_value = SystemType.dualTilt
        with open('test/fixtures/input_dual_tilt.csv', 'r', newline='') as csv_file:
            cad_input = csv_file.read()
        eq_(self.subject.validate(cad_input), None)

    def test_reports_dual_tilt_file_is_valid_when_single_tilt(self):  # think about it.
        self.user_values.system_type.return_value = SystemType.singleTilt
        with open('test/fixtures/input_dual_tilt.csv', 'r', newline='') as csv_file:
            cad_input = csv_file.read()
        eq_(self.subject.validate(cad_input), None)

    def test_reports_invalid_wind_zones_when_single_tilt(self):
        self.user_values.system_type.return_value = SystemType.singleTilt
        with open('test/fixtures/invalid_wind_zone.csv', 'r', newline='') as csv_file:
            cad_input = csv_file.read()
        self.should_have_error(self.subject.validate(cad_input), FileValidationMessage.SingleTiltWindZone, 8)

    def test_reports_not_csv_files_as_invalid(self):
        self.should_have_error(self.subject.validate("What a Terrible File"), FileValidationMessage.InvalidHeaders, 0)
        self.should_have_error(self.subject.validate("What a Terrible File\r\nmalformed data!\r\n"), FileValidationMessage.InvalidHeaders, 0)

    def test_reports_no_rows_as_invalid(self):
        self.should_have_error(self.subject.validate("A\tB\tC\tD\tE\t\r\n"), FileValidationMessage.InvalidRowCount, 0)

    def test_reports_wrong_number_of_columns_in_a_row(self):
        self.user_values.system_type.return_value = SystemType.dualTilt
        with open('test/fixtures/invalid_coordinates.csv', 'r', newline='') as csv_file:
            cad_input = csv_file.read()
        self.should_have_error(self.subject.validate(cad_input), FileValidationMessage.Generic, 1)

    def test_reports_invalid_panel_types_when_finds_panel_type_less_than_1(self):
        self.user_values.system_type.return_value = SystemType.singleTilt
        with open('test/fixtures/invalid_panel_types_not_in_1-4.csv', 'r', newline='') as csv_file:
            cad_input = csv_file.read()
        self.should_have_error(self.subject.validate(cad_input), FileValidationMessage.PanelTypeOutOfBounds, 13)

    def test_reports_invalid_panel_types_when_finds_panel_type_greater_than_4(self):
        self.user_values.system_type.return_value = SystemType.singleTilt
        with open('test/fixtures/invalid_panel_types_more_than_4.csv', 'r', newline='') as csv_file:
            cad_input = csv_file.read()
        self.should_have_error(self.subject.validate(cad_input), FileValidationMessage.PanelTypeOutOfBounds, 14)

    def test_reports_invalid_panel_types_when_finds_less_than_4_corner_panel_types_per_subarray_in_single_tilt(self):
        self.user_values.system_type.return_value = SystemType.singleTilt
        with open('test/fixtures/invalid_panel_types_less_than_4_corner_panels.csv', 'r', newline='') as csv_file:
            cad_input = csv_file.read()
        self.should_have_error(self.subject.validate(cad_input), FileValidationMessage.PanelTypeTooFewCornersSingleTilt, None)

    def test_reports_invalid_panel_types_when_finds_less_than_2_corner_panel_types_per_subarray_in_dual_tilt(self):
        self.user_values.system_type.return_value = SystemType.dualTilt
        with open('test/fixtures/invalid_dual_tilt_panel_types_less_than_2_corner_panels.csv', 'r', newline='') as csv_file:
            cad_input = csv_file.read()
        self.should_have_error(self.subject.validate(cad_input), FileValidationMessage.PanelTypeTooFewCornersDualTilt, None)

    def test_reports_valid_when_finds_at_least_2_corner_panels_per_subarray_in_dual_tilt(self):
        self.user_values.system_type.return_value = SystemType.dualTilt
        with open('test/fixtures/input_small_dual_tilt.csv', 'r',
                  newline='') as csv_file:
            cad_input = csv_file.read()
        eq_(self.subject.validate(cad_input), None)

    def test_reports_invalid_panel_types_when_finds_no_corner_panel_types_in_subarray(self):
        self.user_values.system_type.return_value = SystemType.dualTilt
        cad_input = "A\tB\tC\tD\tE\r\nhandle\tblock\tA\t3\t1\r\n"
        self.should_have_error(self.subject.validate(cad_input), FileValidationMessage.PanelTypeTooFewCornersDualTilt, None)

    def test_reports_too_close_when_finds_panel_too_close_to_another_panel(self):
        self.user_values.system_type.return_value = SystemType.dualTilt
        self.user_values.module_type.return_value = ModuleType.Cell96
        self.user_values.module_system_constants().tolerance = 0.1
        self.user_values.module_system_constants().panel_spacing = (88.24, 62.0)
        with open('test/fixtures/invalid_too_close.txt', 'r', newline='') as csv_file:
            cad_input = csv_file.read()
        #self.should_have_error(self.subject.validate(cad_input), FileValidationMessage.PanelsTooClose , None)
