#! /usr/bin/env python3

import io
import sys
sys.path.insert(0, '..')

from database.tm_db import connect_to_db

dbcon = connect_to_db()


def merge_pools(*args, outfile=None, time_sort=False):
    """
    Merge binary TM pools with optional sorting by timestamp
    @param args:
    @param outfile:
    @param time_sort:
    """
    pcktdata = b''.join((open(tmpool, 'rb').read() for tmpool in args))
    if outfile is None:
        outfile = 'merged_pools.tmpool'

    if not time_sort:
        with open(outfile, 'wb') as fd:
            fd.write(pcktdata)
        print('>> TM packets written to {} <<'.format(outfile))
        return
    else:
        print('TIME SORTING NOT YET IMPLEMENTED FOR SMILE')
        return

    pcktstream = io.BytesIO(pcktdata)

    pckts_list = []
    pkt = True
    while pkt is not None:
        pkt = read_pus(pcktstream)
        if pkt is not None:
            pckts_list.append(pkt)

    pckts_list.sort(key=lambda x: get_cuc(x))
    with open(outfile, 'wb') as fd:
        fd.write(b''.join(pckts_list))

    print('>> TM packets written to {}, SORTED BY TIMESTAMP <<'.format(outfile))


def read_pus(data):
    pos = data.tell()
    pus_size = data.read(6)

    if pus_size == b'':
        return

    while len(pus_size) < 6:
        add = data.read(1)
        if add == b'':
            return
        pus_size += add

    data.seek(pos)

    # packet size is header size (6) + pus size field + 1
    pckt_size = int.from_bytes(pus_size[4:6], 'big') + 7

    return data.read(pckt_size)


"""
def get_cuc(tm):
    try:
        ct, ft = struct.unpack('>IH', tm[10:16])
        ft >>= 1
        return ct + ft / 2 ** 15
    except IndexError:
        return -1.
"""


def ha_to_tmpool(filename, save=True, encoding='utf-8'):
    with open(filename, 'r', encoding=encoding) as fd:
        pckts = (line for line in fd.read().split('\n') if not line.startswith('<'))
        tmpool = bytes.fromhex(''.join(pckts))

    # for x in data.split('<LENGTH>')[1:]:
    #     i = x.index('\n')
    #     try:
    #         j = x.index('<REC_DATE>')
    #     except ValueError:
    #         j = x.index('<END_DATA_BLOCK>')
    #     pckts.append(bytes.fromhex(x[i + 1:j].replace('\n', '')))

    if save:
        if isinstance(save, str):
            with open(save, 'wb') as fd:
                fd.write(tmpool)
        else:
            with open(filename + '.tmpool', 'wb') as fd:
                fd.write(tmpool)
                save = fd.name
        return save
    else:
        return tmpool


def _get_cdescr(cname):
    try:
        descr, = dbcon.execute('select ccf_descr from ccf where ccf_cname="{}"'.format(cname)).fetchall()[0]
    except IndexError:
        descr = cname
    finally:
        dbcon.close()
    return descr


def _get_piddescr(pcf_name):
    try:
        descr, = dbcon.execute('select pcf_descr from pcf where pcf_name="{}"'.format(pcf_name)).fetchall()[0]
    except IndexError:
        descr = pcf_name
    finally:
        dbcon.close()
    return descr


def _cuc_to_bytes(cuc, sync='U'):
    if isinstance(cuc, str):
        sync = cuc[-1]
        cuc = float(cuc[:-1])
    coarse = int(cuc)
    fine = int((cuc % 1) * 2 ** 15)
    if sync.upper() == 'S':
        sync = '1'
    else:
        sync = '0'
    c = coarse.to_bytes(4, 'big')
    f = int(bin(fine) + sync, 0).to_bytes(2, 'big')
    return c + f


def _add_quotes(s):
    if isinstance(s, str):
        s = '"' + s + '"'
    return s


def _tc_string(descr, pars=None, ack=None, prefix='ccs.Tcsend_DB', sleep=None):
    if descr.startswith(('ASC', 'CSC', 'RSC')):
        if sleep is None:
            sleep = ''
        else:
            sleep = 'time.sleep({:.3f})'.format(sleep)
        return '# {}\n{}'.format(descr, sleep)
    if ack is None:
        ack = ''
    else:
        ack = ', ack="{}"'.format(ack)
    if sleep is None:
        sleep = ''
    else:
        sleep = ', sleep={:.3f}'.format(sleep)
    pars = ', '.join(map(str, map(_add_quotes, pars)))
    if pars != '':
        pars = ', ' + pars
    return '{}("{}"{}{}{})'.format(prefix, descr, pars, ack, sleep)


def convert_ssf(filename, outfile=None, cmdprefix='ccs.Tcsend_DB'):
    with open(filename, 'r') as fd:
        data = fd.read().split('\n')
        data.remove('')

    nrows = len(data)
    idx = 0
    seq = 1
    cmds = {}
    base_header = ''

    while idx < nrows:
        row = data[idx]
        if not row.startswith('C|'):
            if row.startswith('1|'):
                base_header += row
            idx += 1
            continue
        head = row.split('|')
        cname = head[1]
        npars = int(head[13])
        ack = bin(int(head[26]))
        timestamp = float('.'.join(head[16:18]))
        descr = _get_cdescr(cname)
        cmds[seq] = {'CNAME': cname, 'DESCR': descr, 'ack': ack, 'timestamp': timestamp}
        parameters = []
        for i in range(npars):
            idx += 1
            par_data = data[idx].split('|')
            if int(par_data[2]) in (0, 1):
                value = int(par_data[5])
            elif int(par_data[2]) in (2, 3):
                value = float(par_data[5])
            elif int(par_data[2]) == 4:
                value = str(par_data[5])
            elif int(par_data[2]) == 5:
                value = bytes.fromhex(par_data[5])
            elif int(par_data[2]) == 7:
                time = par_data[5].replace(' ', '.')
                value = _cuc_to_bytes(time)
            elif int(par_data[2]) == 14:
                value = _get_piddescr(par_data[5])
            else:
                value = par_data[5]
            parameters.append(value)
        if npars != len(parameters):
            print(">>> {} parameters found, should be {} [{}]! <<<".format(len(parameters), npars, cname))
        if descr.startswith('DPU_IFSW_UPDT_PAR_'):
            dtype = 'TYPE_' + descr.split('_')[-1]
            for i in range(parameters[0]):
                parameters.insert(2 + (i * 4), dtype)
        cmds[seq]['parameters'] = parameters
        idx += 1
        seq += 1

    script_cmds = []
    for seq in range(1, len(cmds) + 1):
        data = cmds[seq]
        if seq < len(cmds):
            sleep = cmds[seq + 1]['timestamp'] - cmds[seq]['timestamp']
            if sleep < 0:
                print('>>> Negative timestamp difference between commands {} & {} ({:.3f}s)! Setting sleep to 0. <<<'.
                      format(seq, seq + 1, sleep))
                sleep = 0
        else:
            sleep = None
        comment = '# CMD {:d} [{:.6f}]\n'.format(seq, cmds[seq]['timestamp'])
        script_cmds.append(comment + _tc_string(data['DESCR'], data['parameters'], ack=data['ack'], sleep=sleep,
                                                prefix=cmdprefix))

    header = "# Commands generated from file: {}\n".format(filename.split('/')[-1])
    script = header + '# Base header: {}\n\n'.format(base_header) + '\n\n'.join(script_cmds) + '\n'

    if outfile is None:
        outfile = filename[:-4] + '_CCS.py'

    with open(outfile, 'w') as fd:
        fd.write(script)

    return outfile