#!/usr/bin/env python3

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

from database.tm_db import scoped_session_maker

dbcon = scoped_session_maker('idb')

# get IDB version information
idb_version = dbcon.execute('select vdf_release, vdf_issue,_UpdateTime from vdf').fetchall()[0]
vmaj, vmin, date = (*idb_version[:2], idb_version[-1].strftime('%Y-%m-%d'))

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 get_calibrated(cpc_pname, pvalue):
    try:
        altxt, = dbcon.execute('SELECT pas_altxt FROM cpc LEFT JOIN pas ON cpc.cpc_pafref=pas.pas_numbr '
                               'WHERE cpc_pname="{}" AND pas_alval={}'.format(cpc_pname, pvalue)).fetchall()[0]
    except IndexError:
        altxt = None
    finally:
        dbcon.close()
    return altxt


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)


fname = sys.argv[1]
with open(fname, '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('|')
    npars = int(head[13])
    cname = head[1]
    ack = bin(int(head[26]))
    ctime = float(head[16])
    ftime = float(head[17]) / 1e6
    timestamp = ctime + ftime
    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('|')
        pname = par_data[0]
        if int(par_data[2]) in (0, 1):
            value = int(par_data[5])
            cal = get_calibrated(pname, value)
            if cal is not None:
                value = cal
        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))

header = "# Commands generated from file: {}\n# IDB v{}.{} used ({})\n".format(fname.split('/')[-1], vmaj, vmin, date)
script = header + '# Base header: {}\n\n'.format(base_header) + '\n\n'.join(script_cmds) + '\n'

if len(sys.argv) > 2:
    outfile = sys.argv[2]
else:
    outfile = fname[:-4] + '_CCS.py'

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

print('Script written to file: ', outfile)