from unittest import TestCase
from pathlib import Path

from sohstationviewer.model.mseed_data.mseed import MSeed
from sohstationviewer.model.general_data.general_data import \
    ProcessingDataError


TEST_DATA_DIR = Path(__file__).resolve().parent.parent.parent.joinpath(
    'test_data')
pegasus_data = TEST_DATA_DIR.joinpath("Pegasus-sample")
q330_data = TEST_DATA_DIR.joinpath("Q330-sample")
blockettes_data = TEST_DATA_DIR.joinpath("Q330_unimplemented_ascii_block")
multiplex_data = TEST_DATA_DIR.joinpath("Q330_multiplex")
centaur_data = TEST_DATA_DIR.joinpath("Centaur-sample")


class TestMSeed(TestCase):
    def test_path_not_exist(self):
        # raise exception when path not exist
        args = {
            'data_type': 'Q330',
            'is_multiplex': False,
            'folder': '_',
            'on_unittest': True
        }
        with self.assertRaises(ProcessingDataError) as context:
            MSeed(**args)
            self.assertEqual(
                str(context.exception),
                "Path '_' not exist"
            )

    def test_read_text_only(self):
        # There is no station recognized, add text to key 'TEXT' in log_data
        args = {
            'data_type': 'Pegasus',
            'is_multiplex': False,
            'folder': pegasus_data,
            'req_soh_chans': ['_'],
            'on_unittest': True
        }

        obj = MSeed(**args)
        self.assertEqual(list(obj.log_data.keys()), ['TEXT'])
        self.assertEqual(len(obj.log_data['TEXT']), 2)
        self.assertEqual(
            obj.log_data['TEXT'][0][:100],
            '\n\n** STATE OF HEALTH: XX.KC01...D.2020.130'
            '\n2020-05-09 00:00:09.839 UTC: I(TimingThread): timing unce')

        self.assertEqual(
            obj.log_data['TEXT'][1][:100],
            '\n\n** STATE OF HEALTH: XX.KC01...D.2020.129'
            '\n2020-05-08 22:55:45.390 UTC: I(Initializations): Firmware')

    def test_read_text_with_soh(self):
        # text get station from soh data with TXT as channel to add to log_data
        args = {
            'data_type': 'Pegasus',
            'is_multiplex': False,
            'folder': pegasus_data,
            'req_soh_chans': ['VE1'],
            'on_unittest': True
        }

        obj = MSeed(**args)
        self.assertEqual(list(obj.log_data.keys()), ['TEXT', 'KC01'])
        self.assertEqual(len(obj.log_data['TEXT']), 0)
        self.assertEqual(list(obj.log_data['KC01'].keys()), ['TXT'])
        self.assertEqual(len(obj.log_data['KC01']['TXT']), 2)
        self.assertEqual(
            obj.log_data['KC01']['TXT'][0][:100],
            '\n\n** STATE OF HEALTH: XX.KC01...D.2020.130'
            '\n2020-05-09 00:00:09.839 UTC: I(TimingThread): timing unce')

        self.assertEqual(
            obj.log_data['KC01']['TXT'][1][:100],
            '\n\n** STATE OF HEALTH: XX.KC01...D.2020.129'
            '\n2020-05-08 22:55:45.390 UTC: I(Initializations): Firmware')

    def test_read_text_with_waveform(self):
        # text get station from waveform data with TXT as channel to add to
        # log_data
        args = {
            'data_type': 'Pegasus',
            'is_multiplex': False,
            'folder': pegasus_data,
            'req_wf_chans': ['HH1'],
            'req_soh_chans': ['_'],
            'on_unittest': True
        }

        obj = MSeed(**args)
        self.assertEqual(list(obj.log_data.keys()), ['TEXT', 'KC01'])
        self.assertEqual(len(obj.log_data['TEXT']), 0)
        self.assertEqual(list(obj.log_data['KC01'].keys()), ['TXT'])
        self.assertEqual(len(obj.log_data['KC01']['TXT']), 2)
        self.assertEqual(
            obj.log_data['KC01']['TXT'][0][:100],
            '\n\n** STATE OF HEALTH: XX.KC01...D.2020.130'
            '\n2020-05-09 00:00:09.839 UTC: I(TimingThread): timing unce')

        self.assertEqual(
            obj.log_data['KC01']['TXT'][1][:100],
            '\n\n** STATE OF HEALTH: XX.KC01...D.2020.129'
            '\n2020-05-08 22:55:45.390 UTC: I(Initializations): Firmware')

    def test_read_ascii(self):
        # info is text wrapped in mseed format
        args = {
            'data_type': 'Q330',
            'is_multiplex': False,
            'folder': q330_data,
            'req_soh_chans': ['LOG'],
        }
        obj = MSeed(**args)
        self.assertEqual(list(obj.log_data.keys()), ['TEXT', 'AX08'])
        self.assertEqual(list(obj.log_data['AX08'].keys()), ['LOG'])
        self.assertEqual(obj.log_data['TEXT'], [])
        self.assertEqual(len(obj.log_data['AX08']['LOG']), 16)
        self.assertEqual(
            obj.log_data['AX08']['LOG'][0][:100],
            '\n\nSTATE OF HEALTH: From:1625456260.12  To:1625456260.12\n\r'
            '\nQuanterra Packet Baler Model 14 Restart. V'
        )
        self.assertEqual(
            obj.log_data['AX08']['LOG'][1][:100],
            '\n\nSTATE OF HEALTH: From:1625456366.62  To:1625456366.62'
            '\nReducing Status Polling Interval\r\n[2021-07-0'
        )

    def test_read_blockettes_info(self):
        # info in blockette 500
        args = {
            'data_type': 'Q330',
            'is_multiplex': True,
            'folder': blockettes_data,
            'req_soh_chans': ['ACE'],
        }
        obj = MSeed(**args)
        self.assertEqual(list(obj.log_data.keys()), ['TEXT', '3203'])
        self.assertEqual(list(obj.log_data['3203'].keys()), ['ACE'])
        self.assertEqual(obj.log_data['TEXT'], [])
        self.assertEqual(len(obj.log_data['3203']['ACE']), 1)
        self.assertEqual(
            obj.log_data['3203']['ACE'][0][:100],
            '\n\nSTATE OF HEALTH: From:1671729287.00014  To:1671729287.0'
            '\n===========\nVCO correction: 53.7109375\nTim'
        )

    def test_not_is_multiplex_read_channel(self):
        # is_multiplex = False => stop when reach to channel not match req
        # so the channel 'EL1' is read but not finished
        args = {
            'data_type': 'Q330',
            'is_multiplex': False,
            'folder': multiplex_data,
            'req_soh_chans': [],
            'req_wf_chans': ['EL1']
        }
        obj = MSeed(**args)
        self.assertEqual(list(obj.waveform_data.keys()), ['3203'])
        self.assertEqual(list(obj.waveform_data['3203'].keys()), ['EL1'])
        self.assertEqual(obj.waveform_data['3203']['EL1']['samplerate'], 200)
        self.assertEqual(obj.waveform_data['3203']['EL1']['startTmEpoch'],
                         1671730004.145029)
        self.assertEqual(obj.waveform_data['3203']['EL1']['endTmEpoch'],
                         1671730013.805)
        self.assertEqual(obj.waveform_data['3203']['EL1']['size'], 1932)
        self.assertEqual(obj.waveform_data['3203']['EL1']['gaps'], [])
        self.assertEqual(len(obj.waveform_data['3203']['EL1']['tracesInfo']),
                         1)

    def test_is_multiplex_read_channel(self):
        # is_multiplex = True => read every record
        args = {
            'data_type': 'Q330',
            'is_multiplex': True,
            'folder': multiplex_data,
            'req_soh_chans': [],
            'req_wf_chans': ['EL1']
        }
        obj = MSeed(**args)
        self.assertEqual(list(obj.waveform_data.keys()), ['3203'])
        self.assertEqual(list(obj.waveform_data['3203'].keys()), ['EL1'])
        self.assertEqual(obj.waveform_data['3203']['EL1']['samplerate'], 200)
        self.assertEqual(obj.waveform_data['3203']['EL1']['startTmEpoch'],
                         1671730004.145029)
        self.assertEqual(obj.waveform_data['3203']['EL1']['endTmEpoch'],
                         1671730720.4348998)
        self.assertEqual(obj.waveform_data['3203']['EL1']['size'], 143258)
        self.assertEqual(obj.waveform_data['3203']['EL1']['gaps'], [])
        self.assertEqual(len(obj.waveform_data['3203']['EL1']['tracesInfo']),
                         1)

    def test_not_is_multiplex_selected_channel_in_middle(self):
        # won't reached selected channel because previous record doesn't meet
        # requirement when is_multiplex = False
        args = {
            'data_type': 'Q330',
            'is_multiplex': False,
            'folder': multiplex_data,
            'req_soh_chans': [],
            'req_wf_chans': ['EL2']
        }
        obj = MSeed(**args)
        self.assertEqual(list(obj.waveform_data.keys()), [])

    def test_is_multiplex_selected_channel_in_middle(self):
        # is_multiplex = True => the selected channel will be read
        args = {
            'data_type': 'Q330',
            'is_multiplex': True,
            'folder': multiplex_data,
            'req_soh_chans': [],
            'req_wf_chans': ['EL2']
        }
        obj = MSeed(**args)
        self.assertEqual(list(obj.waveform_data.keys()), ['3203'])
        self.assertEqual(list(obj.waveform_data['3203'].keys()), ['EL2'])
        self.assertEqual(obj.waveform_data['3203']['EL2']['samplerate'], 200)
        self.assertEqual(obj.waveform_data['3203']['EL2']['startTmEpoch'],
                         1671730004.3100293)
        self.assertEqual(obj.waveform_data['3203']['EL2']['endTmEpoch'],
                         1671730720.5549)
        self.assertEqual(obj.waveform_data['3203']['EL2']['size'], 143249)
        self.assertEqual(obj.waveform_data['3203']['EL2']['gaps'], [])
        self.assertEqual(len(obj.waveform_data['3203']['EL2']['tracesInfo']),
                         1)

    def test_existing_time_range(self):
        import os
        print(os.getcwd())
        # check if data_time is from the given range, end time may get
        # a little greater than read_end according to record's end time
        args = {
            'data_type': 'Q330',
            'is_multiplex': False,
            'folder': q330_data,
            'req_soh_chans': [],
            'read_start': 1625456018.0,
            'read_end': 1625505627.9998999
        }
        obj = MSeed(**args)
        self.assertEqual(obj.keys, ['AX08'])
        self.assertEqual(list(obj.soh_data['AX08'].keys()), ['VKI'])
        self.assertEqual(list(obj.mass_pos_data['AX08'].keys()), [])
        self.assertEqual(list(obj.waveform_data['AX08'].keys()), [])
        self.assertEqual(obj.data_time['AX08'], [1625446018.0, 1625510338.0])

    def test_non_existing_time_range(self):
        # if given time range out of the data time, no station will be created
        args = {
            'data_type': 'Q330',
            'is_multiplex': False,
            'folder': q330_data,
            'req_soh_chans': [],
            'read_start': 1625356018.0,
            'read_end': 1625405627.9998999
        }
        obj = MSeed(**args)
        self.assertEqual(obj.keys, [])
        self.assertEqual(obj.soh_data, {})
        self.assertEqual(obj.mass_pos_data, {})
        self.assertEqual(obj.waveform_data, {})
        self.assertEqual(obj.data_time, {})

    def test_read_waveform(self):
        # data from tps similar to waveform but not separated at gaps
        args = {
            'data_type': 'Q330',
            'is_multiplex': False,
            'folder': q330_data,
            'req_soh_chans': [],
            'req_wf_chans': ['LHE']
        }
        obj = MSeed(**args)
        self.assertEqual(list(obj.waveform_data.keys()), ['AX08'])
        self.assertEqual(list(obj.waveform_data['AX08'].keys()), ['LHE'])
        self.assertEqual(obj.waveform_data['AX08']['LHE']['samplerate'], 1)
        self.assertEqual(obj.waveform_data['AX08']['LHE']['startTmEpoch'],
                         1625445156.000001)
        self.assertEqual(obj.waveform_data['AX08']['LHE']['endTmEpoch'],
                         1625532950.0)
        self.assertEqual(obj.waveform_data['AX08']['LHE']['size'], 87794)
        self.assertEqual(obj.waveform_data['AX08']['LHE']['gaps'], [])
        self.assertEqual(len(obj.waveform_data['AX08']['LHE']['tracesInfo']),
                         1)

    def test_read_mass_pos_channel(self):
        # mass position channels will be read if one or both include_mpxxxxxx
        # are True
        args = {
            'data_type': 'Q330',
            'is_multiplex': True,
            'folder': q330_data,
            'req_soh_chans': [],
            'req_wf_chans': [],
            'include_mp123zne': True
        }
        obj = MSeed(**args)
        self.assertEqual(list(obj.mass_pos_data.keys()), ['AX08'])
        self.assertEqual(list(obj.mass_pos_data['AX08'].keys()), ['VM1'])
        self.assertEqual(obj.mass_pos_data['AX08']['VM1']['samplerate'], 0.1)
        self.assertEqual(obj.mass_pos_data['AX08']['VM1']['startTmEpoch'],
                         1625444970.0)
        self.assertEqual(obj.mass_pos_data['AX08']['VM1']['endTmEpoch'],
                         1625574580.0)
        self.assertEqual(obj.mass_pos_data['AX08']['VM1']['size'], 12961)
        self.assertEqual(obj.mass_pos_data['AX08']['VM1']['gaps'], [])
        self.assertEqual(len(obj.mass_pos_data['AX08']['VM1']['tracesInfo']),
                         1)

    def test_gap(self):
        # gaps will be detected when gap_minimum is set
        args = {
            'data_type': 'Centaur',
            'is_multiplex': True,
            'folder': centaur_data,
            'req_soh_chans': [],
            'gap_minimum': 60
        }
        obj = MSeed(**args)
        self.assertEqual(list(obj.soh_data.keys()), ['3734'])
        self.assertEqual(sorted(list(obj.soh_data['3734'].keys())),
                         ['EX1', 'EX2', 'EX3', 'GAN', 'GEL', 'GLA', 'GLO',
                          'GNS', 'GPL', 'GST', 'LCE', 'LCQ', 'VCO', 'VDT',
                          'VEC', 'VEI', 'VPB'])
        self.assertAlmostEqual(obj.soh_data['3734']['EX1']['samplerate'],
                               0.0166, 3)
        self.assertEqual(obj.soh_data['3734']['EX1']['startTmEpoch'],
                         1534512840.0)
        self.assertEqual(obj.soh_data['3734']['EX1']['endTmEpoch'],
                         1534550400.0)
        self.assertEqual(obj.soh_data['3734']['EX1']['size'], 597)
        self.assertEqual(obj.gaps['3734'], [[1534521420.0, 1534524000.0]])

    def test_not_detect_gap(self):
        # if gap_minimum isn't set but gap exist, data still be separated, but
        # gap won't be added to gap list
        args = {
            'data_type': 'Centaur',
            'is_multiplex': True,
            'folder': centaur_data,
            'req_soh_chans': [],
            'gap_minimum': None
        }
        obj = MSeed(**args)
        self.assertEqual(list(obj.soh_data.keys()), ['3734'])
        self.assertEqual(sorted(list(obj.soh_data['3734'].keys())),
                         ['EX1', 'EX2', 'EX3', 'GAN', 'GEL', 'GLA', 'GLO',
                          'GNS', 'GPL', 'GST', 'LCE', 'LCQ', 'VCO', 'VDT',
                          'VEC', 'VEI', 'VPB'])
        self.assertAlmostEqual(obj.soh_data['3734']['EX1']['samplerate'],
                               0.0166, 3)
        self.assertEqual(obj.soh_data['3734']['EX1']['startTmEpoch'],
                         1534512840.0)
        self.assertEqual(obj.soh_data['3734']['EX1']['endTmEpoch'],
                         1534550400.0)
        self.assertEqual(obj.soh_data['3734']['EX1']['size'], 597)
        self.assertEqual(obj.gaps['3734'], [])  # no gaps