#!/usr/bin/env python

import argparse, csv, configparser

header ="""
/*
 This file is automatically generated by sesnor_code_gen.py. Please do not check in any manual changes!
 */\n"""

def genDefs(reader, filename):
    declares = list()
    comments = list()
    lines = list()
    for r in reader:
        if r['Size'].strip() == '1' :
            declares.append("%s %s;" % (r['Type'], r['Name']))
        else :
            declares.append("%s %s[%s];" % (r['Type'], r['Name'], r['Size']))
        if (r['Comment']) :
            cmt = "/* %s */" % r['Comment']
        else :
            cmt = ""
        comments.append(cmt)
    max_num_char = max([len(e) for e in declares])
    for dec, cmt in zip(declares, comments) :
        l = ' ' * 8 + dec + ' ' * (max_num_char - len(dec) + 1) + cmt
        lines.append(l)
    with open(filename, 'wt') as ofile :
        ofile.write(header)
        ofile.write('\n'.join(lines))

def genInit(reader, filename) :
    lines = list()
    variables = list()
    values = list()
    for r in reader:
        variables.append('.%s' % r['Name']);
        if r['Size'] != '1':
            tmp = r['Value'].replace('(', '{')
            tmp = tmp.replace(')', '}')
            if r['Type'] == 'char' :
                val = '"%s"' % tmp
            else :
                val = '{%s}' % tmp
        else :
            if r['Type'] == 'char' :
                val = "'%s'" % r['Value']
            else :
                val = r['Value'].lower()
        values.append(val)
    max_num_char = max([len(e) for e in variables])
    for var, val in zip(variables, values) :
        l = ' ' * 8 + var + ' ' * (max_num_char - len(var) + 1) + "= %s," % val
        lines.append(l)
    with open(filename, 'wt') as ofile :
        ofile.write(header)
        ofile.write('\n'.join(lines))


def getParamInfo(r) :
    code = 'strtol(param2, NULL, 0)'
    formater = '%d'
    if 'fft_scalar' in r['Name'] or \
       'scramble_init_state' in r['Name'] or \
       'scramble_tap' in r['Name'] or \
       'hopping_init_state' in r['Name'] or \
       'hopping_tap' in r['Name'] or \
       'mask' in r['Name'] or \
       'addr' in r['Name'] :
        formater = '0x%X'
    elif 'startfreq' in r['Name'] :
        code = 'strtod(param2, NULL)'
        formater = '%.4f'
    elif r['Type'] == 'double' :
        code = 'strtod(param2, NULL)'
        formater = '%.4f'
    elif r['Type'] == 'float' :
        code = 'strtof(param2, NULL)'
        formater = '%.4f'
    elif 'uint' in r['Type'] :
        formater = '%u'
    elif r['Type'] == 'char' :
        if r['Size'] == '1' :
            code = "param2[0]"
            formater = '%c'
        else :
            code = ""
            formater = '%s'
    elif r['Type'] == 'bool' :
        formater = '%d'
    elif r['Type'] == 'antenna_pos_t' :
        code = 'strtof(param2, NULL)'
        formater = '%.2f'
    return code, formater



command_template = r'''
const char *param1, *param2;
BaseType_t len1, len2;
param1 = FreeRTOS_CLIGetParameter(pcCommandString, 1, &len1);
param2 = FreeRTOS_CLIGetParameter(pcCommandString, 2, &len2);
sensor_config_t *cfg;
if (param1 != NULL) {
        cfg = sensor_config_get_cur_cfg(); 
%s
} else {
        static int cc = 0;
        cfg = sensor_config_get_config(cc);
%s
}
return pdFALSE;
'''

write_command_template = r'''if (strncmp(param1, "{0}", len1) == 0) {{
{1}
        return pdFALSE;
}}'''

write_command_size1_template = r'''if (param2 != NULL)
        cfg->{0} = ({1}) {2};
snprintf(pcWriteBuffer, xWriteBufferLen, "{0} = {3}\n\r", cfg->{0});'''

write_command_string_template = r'''if (param2 != NULL)
        snprintf(cfg->{0}, MIN(len2, {1}-1)+1, "%s", param2);
snprintf(pcWriteBuffer, xWriteBufferLen, "{0} = {2}\n\r", cfg->{0});'''

write_command_sizeN_template = r'''uint32_t cnt = 0;
while(param2 != NULL && cnt < {0}) {{
        cfg->{1}[cnt++] = ({2}) {3};
        param2 = FreeRTOS_CLIGetParameter(pcCommandString, 2+cnt, &len2);
}}
int32_t offset = snprintf(pcWriteBuffer, xWriteBufferLen, "{1} = [");
for(cnt = 0; cnt < {0}; cnt++) {{
        if (cnt != {0} - 1)
                offset += snprintf(pcWriteBuffer + offset, xWriteBufferLen - offset, "{4}, ", cfg->{1}[cnt]);
        else
                offset += snprintf(pcWriteBuffer + offset, xWriteBufferLen - offset, "{4}]\n\r", cfg->{1}[cnt]);
}}
'''

write_command_AntennaPos_template = r'''uint32_t cnt = 0;
while(param2 != NULL && cnt < {0}) {{
        if (cnt % 2 == 0)
                cfg->{1}[cnt/2].x = ({2}) {3};
        else
                cfg->{1}[cnt/2].y = ({2}) {3};
        cnt++;
        param2 = FreeRTOS_CLIGetParameter(pcCommandString, 2+cnt, &len2);
}}
int32_t offset = snprintf(pcWriteBuffer, xWriteBufferLen, "{1} = [");
for(cnt = 0; cnt < {0} && offset < cmdMAX_OUTPUT_SIZE; cnt++) {{
        if (cnt != {0} - 1)
                offset += snprintf(pcWriteBuffer + offset, xWriteBufferLen - offset, "({4}, {4}), ", cfg->{1}[cnt].x, cfg->{1}[cnt].y);
        else
                offset += snprintf(pcWriteBuffer + offset, xWriteBufferLen - offset, "({4}, {4})]\n\r", cfg->{1}[cnt].x, cfg->{1}[cnt].y);
}}
'''

readall_command_template = r'''static uint32_t count = 0;
uint32_t cnt = 0;
int32_t offset = 0;
switch(count) {
%s
}'''

readall_case_template = r'''case {0} :
        snprintf(pcWriteBuffer, xWriteBufferLen, "{1} = {2}\r\n", cfg->{1});
        count++;
        return pdTRUE;'''

readall_case_sizeN_template = r'''case {0} :
        offset = snprintf(pcWriteBuffer, xWriteBufferLen, "{1} = [");
        for(cnt = 0; cnt < {2} && offset < cmdMAX_OUTPUT_SIZE; cnt++) {{
                if (cnt != {2} - 1)
                        offset += snprintf(pcWriteBuffer + offset, xWriteBufferLen - offset, "{3}, ", cfg->{1}[cnt]);
                else
                        offset += snprintf(pcWriteBuffer + offset, xWriteBufferLen - offset, "{3}]\n\r", cfg->{1}[cnt]);
        }}
        count++;
        return pdTRUE;'''

readall_case_AntennaPos_template = r'''case {0} :
        offset = snprintf(pcWriteBuffer, xWriteBufferLen, "{1} = [");
        for(cnt = 0; cnt < {2} && offset < cmdMAX_OUTPUT_SIZE; cnt++) {{
                if (cnt != {2} - 1)
                        offset += snprintf(pcWriteBuffer + offset, xWriteBufferLen - offset, "({3}, {3}), ", cfg->{1}[cnt].x, cfg->{1}[cnt].y);
                else
                        offset += snprintf(pcWriteBuffer + offset, xWriteBufferLen - offset, "({3}, {3})]\n\r", cfg->{1}[cnt].x, cfg->{1}[cnt].y);
        }}
        count++;
        return pdTRUE;'''

readall_case_default = \
r'''count = 0;
snprintf(pcWriteBuffer, xWriteBufferLen, "-----------CFG-EOF %d----------\r\n", cc);
if (cc == NUM_FRAME_TYPE-1) {
        cc = 0;
        return pdFALSE;        
} else {
        cc++;
        return pdTRUE;
}'''


def genCmd(reader, filename) :
    readall = list()
    write = list()
    for i, r in enumerate(reader) :
        code, formater = getParamInfo(r)
        if r['Size'] == '1' :
            sub_write = write_command_size1_template.format(r['Name'], r['Type'], code, formater).split('\n')
            sub_readall = readall_case_template.format(i, r['Name'], formater)
        elif r['Type'] == 'char' :
            sub_write = write_command_string_template.format(r['Name'], r['Size'], formater).split('\n')
            sub_readall = readall_case_template.format(i, r['Name'], formater)
        elif r['Type'] == 'antenna_pos_t' :
            sub_write = write_command_AntennaPos_template.format(r['Size'], r['Name'], 'float', code, formater).split('\n')
            sub_readall = readall_case_AntennaPos_template.format(i, r['Name'], r['Size'], formater)
        else :
            sub_write = write_command_sizeN_template.format(r['Size'], r['Name'], r['Type'], code, formater).split('\n')
            sub_readall = readall_case_sizeN_template.format(i, r['Name'], r['Size'], formater)
        sub_write = [' ' * 8 + s for s in sub_write]
        sub_write = write_command_template.format(r['Name'], '\n'.join(sub_write)).split('\n')
        sub_write = [' ' * 8 + s for s in sub_write]
        write.append('\n'.join(sub_write))
        readall.append(sub_readall)
    readall = '\n'.join(readall).split('\n')
    readall.append("default :")
    readall = readall + [' ' * 8 + e for e in readall_case_default.split('\n')]
    readall = (readall_command_template % '\n'.join(readall)).split('\n')
    readall = [' ' * 8 + s for s in readall]
    command_code = command_template % ('\n'.join(write), '\n'.join(readall))
    command_code = '\n'.join([' ' * 8 + l for l in command_code.split('\n')])
    with open(filename, 'wt') as ofile :
        ofile.write(command_code)




def combineConfig(file_list) :
    config = dict()
    for f in file_list :
        parser = configparser.ConfigParser()
        parser.optionxform = str
        parser.read(f)
        for s in  parser.sections() :
            for k in parser[s].keys() :
                config[k] = parser[s][k].strip()
    return config


def genFinalConfig(csv_final, csv_base, file_list) :
    with open(csv_base, 'rt') as fin :
        csv_input = csv.DictReader(fin)
        with open(csv_final, 'wt') as fout :
            csv_output = csv.DictWriter(fout, fieldnames = csv_input.fieldnames)
            csv_output.writeheader()
            config = combineConfig(file_list)
            for r in csv_input :
                if r['Name'] in config.keys() :
                    r['Value'] = config[r['Name']]
                csv_output.writerow(r)

if __name__ == '__main__' :
    parser = argparse.ArgumentParser(description='Script to generate sensor_config related codes')
    parser.add_argument('infile', metavar='infile', type=str, help='input config file in csv format')
    parser.add_argument('--final-cfg', metavar='final_csv', default='final.csv', type=str, help='final combined config')
    parser.add_argument('-o', '--output', type=str, help='output file name')
    parser.add_argument('-t', '--type', default='init', type=str, help='output type. init: initialization code; def: sensor_config defs')
    parser.add_argument('--feature-list', default=None, help='feature list to overwrite setting in infile. These files are in ini format')
    args = parser.parse_args()
    if args.feature_list :
        feature_list = [f + '.fea' for f in args.feature_list.split(',')]
    else :
        feature_list = []
    genFinalConfig(args.final_cfg, args.infile, feature_list)
    with open(args.final_cfg) as csvfile:
        reader = csv.DictReader(csvfile)
        if args.type == 'def' :
            genDefs(reader, args.output)
        elif args.type == 'init' :
            genInit(reader, args.output)
        elif args.type == 'cmd' :
            genCmd(reader, args.output)
