from unittest import TestCase
import math

from sohstationviewer.model.mseed.mseed import MSeed
from sohstationviewer.view.util.enums import LogType


class TestCheckGPSStatusFormatQ330(TestCase):
    def setUp(self) -> None:
        self.status_lines = [
            'GPS Status',
            'Time: 18:28:49',
            'Date: 27/08/2018',
            'Fix Type: 3-D',
            'Height: 47.6M',
            'Latitude: 5906.7572N',
            'Longitude: 15651.4038W',
            'On Time: 15min',
            'Sat. Used: 6',
            'In View: 11',
            'Checksum Errors: 0',
            'Last GPS timemark: 2018-08-27 18:28:48',
            'PLL Status'
        ]

    def test_not_enough_status_lines(self):
        self.status_lines.pop()
        with self.assertRaises(ValueError):
            MSeed.check_gps_status_format_q330(self.status_lines)

    def test_too_many_status_lines(self):
        self.status_lines.append('')
        with self.assertRaises(ValueError):
            MSeed.check_gps_status_format_q330(self.status_lines)

    def test_gps_status_not_followed_by_pll_status(self):
        self.status_lines[12] = ''
        with self.assertRaises(ValueError):
            MSeed.check_gps_status_format_q330(self.status_lines)

    def test_good_data(self):
        try:
            MSeed.check_gps_status_format_q330(self.status_lines)
        except ValueError:
            self.fail()

    def test_fix_type_bad_data(self):
        self.status_lines[3] = ''
        with self.assertRaises(ValueError):
            MSeed.check_gps_status_format_q330(self.status_lines)

    def test_height_bad_data(self):
        self.status_lines[4] = ''
        with self.assertRaises(ValueError):
            MSeed.check_gps_status_format_q330(self.status_lines)

    def test_latitude_bad_data(self):
        self.status_lines[5] = ''
        with self.assertRaises(ValueError):
            MSeed.check_gps_status_format_q330(self.status_lines)

    def test_longitude_bad_data(self):
        self.status_lines[6] = ''
        with self.assertRaises(ValueError):
            MSeed.check_gps_status_format_q330(self.status_lines)

    def test_sat_used_bad_data(self):
        self.status_lines[8] = ''
        with self.assertRaises(ValueError):
            MSeed.check_gps_status_format_q330(self.status_lines)

    def test_gps_timemark_bad_data(self):
        self.status_lines[11] = ''
        with self.assertRaises(ValueError):
            MSeed.check_gps_status_format_q330(self.status_lines)


class TestExtractGPSPointQ330(TestCase):
    def setUp(self) -> None:
        self.gps_lines = ['GPS Status',
                          'Time: 03:37:39',
                          'Date: 05/07/2021',
                          'Fix Type: 3-D',
                          'Height: 1000.6M',
                          'Latitude: 3853.9013N',
                          'Longitude: 04610.8865E',
                          'On Time: 65535min',
                          'Sat. Used: 7',
                          'In View: 11',
                          'Checksum Errors: 0',
                          'Last GPS timemark: 2021-07-05 03:37:38']

    def test_not_enough_gps_lines(self):
        self.gps_lines.pop()
        with self.assertRaises(ValueError):
            MSeed.extract_gps_point_q330(self.gps_lines)

    def test_too_many_gps_lines(self):
        self.gps_lines.append('')
        with self.assertRaises(ValueError):
            MSeed.extract_gps_point_q330(self.gps_lines)

    def test_last_timemark_extracted_correctly(self):
        with self.subTest('test_data_from_file'):
            result = MSeed.extract_gps_point_q330(self.gps_lines)
            expected = '2021-07-05 03:37:38'
            self.assertEqual(result.last_timemark, expected)

        with self.subTest('test_made_up_data'):
            self.gps_lines[11] = 'Last GPS timemark: test time mark'
            result = MSeed.extract_gps_point_q330(self.gps_lines)
            expected = 'test time mark'
            self.assertEqual(result.last_timemark, expected)

    def test_fix_type_extracted_correctly(self):
        with self.subTest('test_data_from_file'):
            result = MSeed.extract_gps_point_q330(self.gps_lines)
            expected = '3-D'
            self.assertEqual(result.fix_type, expected)

        with self.subTest('test_made_up_data'):
            self.gps_lines[3] = 'Fix Type: test fix type'
            result = MSeed.extract_gps_point_q330(self.gps_lines)
            expected = 'test fix type'
            self.assertEqual(result.fix_type, expected)

    def test_height_extracted_correctly(self):
        with self.subTest('test_data_from_file'):
            result = MSeed.extract_gps_point_q330(self.gps_lines)
            expected = 1000.6
            self.assertTrue(math.isclose(result.height, expected))

        with self.subTest('test_made_up_data'):
            self.gps_lines[4] = 'Height: 3522362.623623MMMSM'
            result = MSeed.extract_gps_point_q330(self.gps_lines)
            expected = 3522362.623623
            self.assertTrue(math.isclose(result.height, expected))

    def test_height_unit_extracted_correctly(self):
        with self.subTest('test_data_from_file'):
            result = MSeed.extract_gps_point_q330(self.gps_lines)
            expected = 'M'
            self.assertEqual(result.height_unit, expected)

        with self.subTest('test_made_up_data'):
            self.gps_lines[4] = 'Height: 3522362.623623MMMSM'
            result = MSeed.extract_gps_point_q330(self.gps_lines)
            expected = 'MMMSM'
            self.assertEqual(result.height_unit, expected)

    def test_latitude_extracted_correctly(self):
        with self.subTest('test_latitude_in_the_north'):
            result = MSeed.extract_gps_point_q330(self.gps_lines)
            expected = 38.898355
            self.assertTrue(math.isclose(result.latitude, expected))

        with self.subTest('test_latitude_in_the_south'):
            self.gps_lines[5] = 'Latitude: 3853.9013S'
            result = MSeed.extract_gps_point_q330(self.gps_lines)
            expected = -38.898355
            self.assertTrue(math.isclose(result.latitude, expected))

    def test_longitude_extracted_correctly(self):
        with self.subTest('test_longitude_in_the_east'):
            result = MSeed.extract_gps_point_q330(self.gps_lines)
            expected = 46.18144166666666667
            self.assertTrue(math.isclose(result.longitude, expected))

        with self.subTest('test_longitude_in_the_west'):
            self.gps_lines[6] = 'Longitude: 04610.8865W'
            result = MSeed.extract_gps_point_q330(self.gps_lines)
            expected = -46.18144166666666667
            self.assertTrue(math.isclose(result.longitude, expected))

    def test_num_sat_used_extracted_correctly(self):
        with self.subTest('test_longitude_in_the_east'):
            result = MSeed.extract_gps_point_q330(self.gps_lines)
            expected = 7
            self.assertEqual(result.num_satellite_used, expected)

        with self.subTest('test_longitude_in_the_west'):
            self.gps_lines[8] = 'Sat. Used: 53252352'
            result = MSeed.extract_gps_point_q330(self.gps_lines)
            expected = 53252352
            self.assertEqual(result.num_satellite_used, expected)

    def test_no_location_data(self):
        self.gps_lines[4] = 'Height: '
        self.gps_lines[5] = 'Latitude: '
        self.gps_lines[6] = 'Longitude: '
        self.gps_lines[8] = 'Sat. Used: '
        result = MSeed.extract_gps_point_q330(self.gps_lines)
        self.assertEqual(result.last_timemark, '2021-07-05 03:37:38')
        self.assertEqual(result.fix_type, '3-D')
        self.assertEqual(result.height, 0)
        self.assertEqual(result.latitude, 0)
        self.assertEqual(result.longitude, 0)
        self.assertEqual(result.num_satellite_used, 0)


class MockMSeed(MSeed):
    """
    This class mocks out some methods of MSeed that are used in
    MSeed.get_gps_channel_prefix but which would make testing it more
    cumbersome. The methods mocked out either run very long or change the GUI
    and/or terminal in some way.
    """
    def __init__(self):  # noqa
        self.notification_signal = None
        self.tmp_dir = ''

    def track_info(self, text: str, type: LogType) -> None:
        print(text)

    def __del__(self):
        pass


class TestGetGPSChannelPrefix(TestCase):
    def setUp(self) -> None:
        self.mseed_obj = MockMSeed()
        self.mseed_obj.channels = set()

    def test_pegasus_data_type(self):
        data_type = 'Pegasus'
        expected = 'V'
        result = self.mseed_obj.get_gps_channel_prefix(data_type)
        self.assertEqual(result, expected)

    def test_centaur_data_type(self):
        data_type = 'Centaur'
        expected = 'G'
        result = self.mseed_obj.get_gps_channel_prefix(data_type)
        self.assertEqual(result, expected)

    def test_unknown_data_type_pegasus_gps_channels(self):
        data_type = 'Unknown'
        self.mseed_obj.channels = {'VNS', 'VLA', 'VLO', 'VEL'}
        expected = 'V'
        result = self.mseed_obj.get_gps_channel_prefix(data_type)
        self.assertEqual(expected, result)

    def test_unknown_data_type_centaur_gps_channels(self):
        data_type = 'Unknown'
        self.mseed_obj.channels = {'GNS', 'GLA', 'GLO', 'GEL'}
        expected = 'G'
        result = self.mseed_obj.get_gps_channel_prefix(data_type)
        self.assertEqual(expected, result)

    def test_unknown_data_type_channels_do_not_match_either_data_type(self):
        data_type = 'Unknown'
        result = self.mseed_obj.get_gps_channel_prefix(data_type)
        self.assertIsNone(result)