from pathlib import Path

from sohstationviewer.model.mseed_data.mseed_reader import MSeedReader
from tests.base_test_case import BaseTestCase

TEST_DATA_DIR = Path(__file__).resolve().parent.parent.parent.joinpath(
    'test_data')
ascii_file = TEST_DATA_DIR.joinpath(
    "Q330-sample/day_vols_AX08/AX08.XA..LOG.2021.186")
blockettes_files = TEST_DATA_DIR.joinpath(
    "Q330_unimplemented_ascii_block/XX-3203_4-20221222190255")
multiplex_file = TEST_DATA_DIR.joinpath(
    "Q330_multiplex/XX-3203_4-20221222183011")
soh_file = TEST_DATA_DIR.joinpath(
    "Q330-sample/day_vols_AX08/AX08.XA..VKI.2021.186")
waveform_file = TEST_DATA_DIR.joinpath(
    "Q330-sample/day_vols_AX08/AX08.XA..LHE.2021.186")
mass_pos_file = TEST_DATA_DIR.joinpath(
    "Q330-sample/day_vols_AX08/AX08.XA..VM1.2021.186")
gap_file = TEST_DATA_DIR.joinpath(
    "Centaur-sample/SOH/"
    "XX.3734.SOH.centaur-3_3734..20180817_000000.miniseed.miniseed")


class TestMSeedReader(BaseTestCase):
    def setUp(self) -> None:
        self.soh_data = {}
        self.mass_pos_data = {}
        self.waveform_data = {}
        self.log_data = {}

    def test_read_ascii(self):
        args = {
            'file_path': ascii_file,
            'is_multiplex': False,
            'req_soh_chans': ['LOG'],
            'soh_data': self.soh_data,
            'mass_pos_data': self.mass_pos_data,
            'waveform_data': self.waveform_data,
            'log_data': self.log_data
        }
        reader = MSeedReader(**args)
        reader.read()
        self.assertEqual(list(self.log_data.keys()), ['AX08'])
        self.assertEqual(list(self.log_data['AX08'].keys()), ['LOG'])
        self.assertEqual(len(self.log_data['AX08']['LOG']), 16)
        self.assertEqual(
            self.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(
            self.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):
        args = {
            'file_path': blockettes_files,
            'is_multiplex': True,
            'req_soh_chans': ['ACE'],
            'soh_data': self.soh_data,
            'mass_pos_data': self.mass_pos_data,
            'waveform_data': self.waveform_data,
            'log_data': self.log_data
        }
        reader = MSeedReader(**args)
        reader.read()
        self.assertEqual(list(self.log_data.keys()), ['3203'])
        self.assertEqual(list(self.log_data['3203'].keys()), ['ACE'])
        self.assertEqual(len(self.log_data['3203']['ACE']), 1)
        self.assertEqual(
            self.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 = {
            'file_path': multiplex_file,
            'is_multiplex': False,
            'req_wf_chans': ['EL1'],
            'soh_data': self.soh_data,
            'mass_pos_data': self.mass_pos_data,
            'waveform_data': self.waveform_data,
            'log_data': self.log_data
        }
        reader = MSeedReader(**args)
        reader.read()
        self.assertEqual(list(self.waveform_data.keys()), ['3203'])
        self.assertEqual(list(self.waveform_data['3203'].keys()), ['EL1'])
        self.assertEqual(self.waveform_data['3203']['EL1']['samplerate'], 200)
        self.assertEqual(self.waveform_data['3203']['EL1']['startTmEpoch'],
                         1671730004.145029)
        self.assertEqual(self.waveform_data['3203']['EL1']['endTmEpoch'],
                         1671730013.8)
        self.assertEqual(self.waveform_data['3203']['EL1']['size'], 1932)
        self.assertEqual(self.waveform_data['3203']['EL1']['gaps'], [])
        self.assertEqual(len(self.waveform_data['3203']['EL1']['tracesInfo']),
                         1)

    def test_is_multiplex_read_channel(self):
        # is_multiplex = True => read every record
        args = {
            'file_path': multiplex_file,
            'is_multiplex': True,
            'req_wf_chans': ['EL1'],
            'soh_data': self.soh_data,
            'mass_pos_data': self.mass_pos_data,
            'waveform_data': self.waveform_data,
            'log_data': self.log_data
        }
        reader = MSeedReader(**args)
        reader.read()
        self.assertEqual(list(self.waveform_data.keys()), ['3203'])
        self.assertEqual(list(self.waveform_data['3203'].keys()), ['EL1'])
        self.assertEqual(self.waveform_data['3203']['EL1']['samplerate'], 200)
        self.assertEqual(self.waveform_data['3203']['EL1']['startTmEpoch'],
                         1671730004.145029)
        self.assertEqual(self.waveform_data['3203']['EL1']['endTmEpoch'],
                         1671730720.4299)
        self.assertEqual(self.waveform_data['3203']['EL1']['size'], 143258)
        self.assertEqual(self.waveform_data['3203']['EL1']['gaps'], [])
        self.assertEqual(len(self.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 = {
            'file_path': multiplex_file,
            'is_multiplex': False,
            'req_wf_chans': ['EL2'],
            'soh_data': self.soh_data,
            'mass_pos_data': self.mass_pos_data,
            'waveform_data': self.waveform_data,
            'log_data': self.log_data
        }
        reader = MSeedReader(**args)
        reader.read()
        self.assertEqual(list(self.waveform_data.keys()), [])

    def test_is_multiplex_selected_channel_in_middle(self):
        # is_multiplex = True => the selected channel will be read
        args = {
            'file_path': multiplex_file,
            'is_multiplex': True,
            'req_wf_chans': ['EL2'],
            'soh_data': self.soh_data,
            'mass_pos_data': self.mass_pos_data,
            'waveform_data': self.waveform_data,
            'log_data': self.log_data
        }
        reader = MSeedReader(**args)
        reader.read()
        self.assertEqual(list(self.waveform_data.keys()), ['3203'])
        self.assertEqual(list(self.waveform_data['3203'].keys()), ['EL2'])
        self.assertEqual(self.waveform_data['3203']['EL2']['samplerate'], 200)
        self.assertEqual(self.waveform_data['3203']['EL2']['startTmEpoch'],
                         1671730004.3100293)
        self.assertEqual(self.waveform_data['3203']['EL2']['endTmEpoch'],
                         1671730720.5499)
        self.assertEqual(self.waveform_data['3203']['EL2']['size'], 143249)
        self.assertEqual(self.waveform_data['3203']['EL2']['gaps'], [])
        self.assertEqual(len(self.waveform_data['3203']['EL2']['tracesInfo']),
                         1)

    def test_existing_time_range(self):
        # 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 = {
            'file_path': soh_file,
            'is_multiplex': False,
            'req_soh_chans': ['VKI'],
            'soh_data': self.soh_data,
            'mass_pos_data': self.mass_pos_data,
            'waveform_data': self.waveform_data,
            'log_data': self.log_data,
            'read_start': 1625456018.0,
            'read_end': 1625505627.9998999
        }
        reader = MSeedReader(**args)
        reader.read()
        self.assertEqual(list(self.soh_data['AX08'].keys()), ['VKI'])
        self.assertEqual(self.soh_data['AX08']['VKI']['startTmEpoch'],
                         1625446018.0)
        self.assertEqual(self.soh_data['AX08']['VKI']['endTmEpoch'],
                         1625510328.0)

    def test_non_existing_time_range(self):
        # if given time range out of the data time, no station will be created
        args = {
            'file_path': soh_file,
            'is_multiplex': False,
            'req_soh_chans': ['VKI'],
            'soh_data': self.soh_data,
            'mass_pos_data': self.mass_pos_data,
            'waveform_data': self.waveform_data,
            'log_data': self.log_data,
            'read_start': 1625356018.0,
            'read_end': 1625405627.9998999
        }
        reader = MSeedReader(**args)
        reader.read()
        self.assertEqual(self.soh_data, {})
        self.assertEqual(self.mass_pos_data, {})
        self.assertEqual(self.waveform_data, {})

    def test_read_waveform(self):
        args = {
            'file_path': waveform_file,
            'is_multiplex': False,
            'req_wf_chans': ['LHE'],
            'soh_data': self.soh_data,
            'mass_pos_data': self.mass_pos_data,
            'waveform_data': self.waveform_data,
            'log_data': self.log_data
        }
        reader = MSeedReader(**args)
        reader.read()
        self.assertEqual(list(self.waveform_data.keys()), ['AX08'])
        self.assertEqual(list(self.waveform_data['AX08'].keys()), ['LHE'])
        self.assertEqual(self.waveform_data['AX08']['LHE']['samplerate'], 1)
        self.assertEqual(self.waveform_data['AX08']['LHE']['startTmEpoch'],
                         1625445156.000001)
        self.assertEqual(self.waveform_data['AX08']['LHE']['endTmEpoch'],
                         1625532949.0)
        self.assertEqual(self.waveform_data['AX08']['LHE']['size'], 87794)
        self.assertEqual(self.waveform_data['AX08']['LHE']['gaps'], [])
        self.assertEqual(len(self.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 = {
            'file_path': mass_pos_file,
            'is_multiplex': False,
            'include_mp123zne': True,
            'soh_data': self.soh_data,
            'mass_pos_data': self.mass_pos_data,
            'waveform_data': self.waveform_data,
            'log_data': self.log_data
        }
        reader = MSeedReader(**args)
        reader.read()
        self.assertEqual(list(self.mass_pos_data.keys()), ['AX08'])
        self.assertEqual(list(self.mass_pos_data['AX08'].keys()), ['VM1'])
        self.assertEqual(self.mass_pos_data['AX08']['VM1']['samplerate'], 0.1)
        self.assertEqual(self.mass_pos_data['AX08']['VM1']['startTmEpoch'],
                         1625444970.0)
        self.assertEqual(self.mass_pos_data['AX08']['VM1']['endTmEpoch'],
                         1625574570.0)
        self.assertEqual(self.mass_pos_data['AX08']['VM1']['size'], 12961)
        self.assertEqual(self.mass_pos_data['AX08']['VM1']['gaps'], [])
        self.assertEqual(len(self.mass_pos_data['AX08']['VM1']['tracesInfo']),
                         1)

    def test_gap(self):
        # gaps will be detected when gap_minimum is set
        args = {
            'file_path': gap_file,
            'is_multiplex': True,
            'soh_data': self.soh_data,
            'mass_pos_data': self.mass_pos_data,
            'waveform_data': self.waveform_data,
            'log_data': self.log_data,
            'gap_minimum': 60
        }
        reader = MSeedReader(**args)
        reader.read()
        self.assertEqual(list(self.soh_data.keys()), ['3734'])
        self.assertEqual(sorted(list(self.soh_data['3734'].keys())),
                         ['EX1', 'EX2', 'EX3', 'GAN', 'GEL', 'GLA', 'GLO',
                          'GNS', 'GPL', 'GST', 'LCE', 'LCQ', 'VCO', 'VDT',
                          'VEC', 'VEI', 'VPB'])
        self.assertAlmostEqual(self.soh_data['3734']['EX1']['samplerate'],
                               0.0166, 3)
        self.assertEqual(self.soh_data['3734']['EX1']['startTmEpoch'],
                         1534512840.0)
        self.assertEqual(self.soh_data['3734']['EX1']['endTmEpoch'],
                         1534550340.0)
        self.assertEqual(self.soh_data['3734']['EX1']['size'], 597)
        expected_gaps = [
            [1534515900.0, 1534515960.0], [1534519020.0, 1534519080.0],
            [1534522140.0, 1534523940.0], [1534526820.0, 1534526880.0],
            [1534529940.0, 1534530000.0], [1534533060.0, 1534533120.0],
            [1534536180.0, 1534536240.0], [1534539300.0, 1534539360.0],
            [1534542420.0, 1534542480.0], [1534545540.0, 1534545600.0],
            [1534548660.0, 1534548720.0]]
        self.assertEqual(self.soh_data['3734']['EX1']['gaps'], expected_gaps)

    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 = {
            'file_path': gap_file,
            'is_multiplex': True,
            'soh_data': self.soh_data,
            'mass_pos_data': self.mass_pos_data,
            'waveform_data': self.waveform_data,
            'log_data': self.log_data,
            'gap_minimum': None
        }
        reader = MSeedReader(**args)
        reader.read()
        self.assertEqual(list(self.soh_data.keys()), ['3734'])
        self.assertEqual(sorted(list(self.soh_data['3734'].keys())),
                         ['EX1', 'EX2', 'EX3', 'GAN', 'GEL', 'GLA', 'GLO',
                          'GNS', 'GPL', 'GST', 'LCE', 'LCQ', 'VCO', 'VDT',
                          'VEC', 'VEI', 'VPB'])
        self.assertAlmostEqual(self.soh_data['3734']['EX1']['samplerate'],
                               0.0166, 3)
        self.assertEqual(self.soh_data['3734']['EX1']['startTmEpoch'],
                         1534512840.0)
        self.assertEqual(self.soh_data['3734']['EX1']['endTmEpoch'],
                         1534550340.0)
        self.assertEqual(self.soh_data['3734']['EX1']['size'], 597)
        self.assertEqual(self.soh_data['3734']['EX1']['gaps'], [])  # no gaps