##########################
# lcmtocsv.py
#
# author: Eric Martin 2018 MBARI
#
# script to convert lcm log files from a single channel
# with a  known type into memory.  
# 
#
###########################

import sys
import lcm
import argparse
import importlib
import pandas as pd


def process_event(event, package, module):
    lib = importlib.import_module(package)
    lcm_t = eval('lib.' + module)
    msg = lcm_t.decode(event.data)

    return msg


def event_headers(event, package, module):
    # decode the data if its the channel we want
    msg = process_event(event, package, module)

    # ignore the methods in this list
    fields = [field for field in dir(msg) if not field.startswith('_')]

    cols = list()

    # lcm event info
    cols.append("evt_channel")
    cols.append("evt_number")
    cols.append("evt_timestamp")
    for field in fields:
        value = getattr(msg, field)

        if callable(value):
            continue
        # print(field+str(type(field)))

        if type(value) is str:
            cols.append(str(value))
            continue

        try:
            iterator = iter(value)
        except TypeError:
            # not a list
            cols.append(str(field))
            # print(str(field))
        else:
            # its a list
            i = 0
            for idx, val in enumerate(value):
                s = str(field).replace(',', '_')
                try:
                    s += "_%s" % val.name
                except:
                    s += "_%i" % i
                    i += 1
                # print(s)
                cols.append(str(s))

    return cols


def event_data(event, package, module):
    def append_data(dat, amt):
        try:
            if type(amt) is str or type(amt) is str:
                s = str(amt).replace(',', '_')
                dat.append(str(s))
                return

            else:
                dat.append(amt)
                return

        except TypeError:
            pass

        except AttributeError as ae:
            pass

    data = list()

    msg = process_event(event, package, module)

    # ignore the methods in this list
    fields = [field for field in dir(msg) if not field.startswith('_')]

    # lcm event info
    data.append("%s" % event.channel)
    data.append(event.eventnum)
    data.append(event.timestamp)

    for field in fields:
        value = getattr(msg, field)

        if callable(value):
            continue

        if type(value) is str:
            data.append(str(value))
            continue

        try:
            iterator = iter(value)
        except TypeError:
            # not a list
            append_data(data, value)
        else:
            # its a list
            for val in value:
                append_data(data, val)

    return data


def parse_file(in_file: list, channels: dict, package: str = 'gss') -> pd.DataFrame:
    """

    :rtype: pd.DataFrame
    """
    i = 0
    rows = list()
    # load the logfile for reading

    data = dict()
    colnames = dict()
    dframe = dict()

    for chan, module in channels.items():
        data[chan] = list()
        colnames[chan] = None
        dframe[chan] = None

    # print(data)
    for log_name in in_file:

        sys.stderr.write("Processing File: %s\r\n" % log_name)
        log = lcm.EventLog(log_name, "r")

        for event in log:
            if event.channel in channels.keys():

                # data
                data[event.channel].append(event_data(event, package, channels[event.channel]))

                # head
                if colnames[event.channel] is None: colnames[event.channel] = event_headers(event, package,
                                                                                            channels[event.channel])

                i += 1

                # Write out a progress into stderr
                percent_done = float(log.tell()) / float(log.size()) * 100.0
                # sys.stderr.write('Progress: %10.2f | Records Found: %i\r' % (percent_done,i))
                sys.stderr.flush()

    sys.stderr.write('\n')

    for chan in channels:
        dframe[chan] = pd.DataFrame(data[chan], columns=colnames[chan])
    return dframe


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='process lcm log files by individual channels')
    parser.add_argument('out_file', type=str, help='filename for output pickle file')
    parser.add_argument('in_file', type=str, help='lcm log filename to convert', nargs=argparse.REMAINDER)
    parser.add_argument('--package', type=str, default='gss', help='package name of lcm type module')
    args = parser.parse_args()

    # Parse for
    channels = {
        'SOI_NAV_SOLUTION': 'nav_solution_t',
        'SEABIRD_CTD_STAT': 'pcomms_t',
    }

    df = parse_file(args.in_file, channels, args.package)
    df.to_pickle(args.out_file)
