#!/usr/bin/python


"""
 Benutzung:
python lookuptable.py bla.csv bla.h bla.c
liest bla.csv aus, interpoliert das ganze und schreibt eine tabelle in bla.c und bla.h
Achtung, bla.c und bla.h werden brutal ueberschrieben!

Das csv muss tabs, kommas oder semikolons als trenner haben und folgende felder enthalten:

#LowValue 
	wird als y fuer alle x-werte, die kleiner als der kleinste x-Wert im csv sind, benutzt
#HighValue
	wird als y fuer alle x-werte, die groesser als der groesste x-Wert im csv sind, benutzt
#DataType
	Datentyp der y-Werte (uint16_t oder uint8_t)
#IndexType
	Datentyp der x-Werte (uint16_t oder uint8_t)
#ArrayName
	Name des Arrays

Siehe example.csv!!


Benutzung auf eigene Gefahr.
"""


import fileinput
import re
import os
import sys

# bei bedarf ergaezen.
AdditionalIncludes = [ '<avr/pgmspace.h>', '<inttypes.h>' ]


def create_printable_table(table, dataType, arrayName):
    """ returns a string with the table in *.c format """
    result = '/** autogenerated lookup table */\n'
    result += 'const ' + dataType + " g_LOOKUP_" + arrayName + '[] PROGMEM =\n'
    result += '{\n'
    for x in sorted(table.keys()):
        y = table[x]
        result += '    /* ' + repr(x).rjust(12) + ' */ ' + repr(y).rjust(12) + ',\n'
    result += '};'
    return result


def create_lookup_function(dataType, indexType, arrayName, biggestKey, highValue):
    header = ''
    body = ''

    header += '/**\n'
    header += ' * autogenerated lookup function for ' + arrayName + "\n"
    header += ' * @param i     x value\n'
    header += ' * @return      f(i), or ' + str(highValue) + ' if i is out of range\n'
    header += ' */\n'
    header += dataType + ' lookup_' + arrayName + '(' + indexType + ' i);'

    body += dataType + ' lookup_' + arrayName + '(' + indexType + ' i)\n'
    body += '{\n'
    body += '    if (i <= ' + str(biggestKey) + ')\n'
    body += '        return '
    if dataType == 'uint16_t':
        body += 'pgm_read_word(&g_LOOKUP_' + arrayName + '[i]);\n'
    elif dataType == 'uint8_t':
        body += 'pgm_read_byte(&g_LOOKUP_' + arrayName + '[i]);\n'
    else:
        body += 'ERROR: no suitable datatype\n'
    body += '    else\n'
    body += '        return ' + str(highValue) + '; /* #HighValue */\n'
    body += '}\n'
    return header, body


def interpolate_lookup_table(table, lowValue):
    #sortieren, durchgehen und interpolieren.
    oldX = 0
    for nextX in sorted(table.keys()):
        if not oldX in table.keys():
            if oldX == 0:
                startY = lowValue
                nextY = lowValue
        else:
            startY = table[oldX]
            nextY = table[nextX]
        for thisX in range(oldX, nextX):    # die punkte zwischen den stuetzstellen durchgehen
            thisY = startY + (nextY - startY) * (thisX - oldX) / (nextX - oldX) # lin. interpol.
            table[thisX] = thisY
        oldX = nextX
    return table


def parse_csv(filename):
    dataType = 'uint16_t'
    indexType = 'uint8_t'
    arrayName = 'TestArray'
    lowestKey = 'ERROR'
    biggestKey = 'ERROR'
    highValue = 'ERROR'
    lowValue = 'ERROR'
    data = {}
    csvFile = open(filename, 'r')
    for line in csvFile:
        zeile = line.strip().strip('"')
        if re.match('\#', zeile):
            myLine = re.split('[\t ;,]+', zeile)
            if re.match('\#LowValue', myLine[0]):
                lowValue = int(myLine[1].strip('"'))
            elif re.match('\#HighValue', myLine[0]):
                highValue = int(myLine[1].strip('"'))
            elif re.match('\#DataType', myLine[0]):
                dataType = str(myLine[1].strip('"'))
            elif re.match('\#IndexType', myLine[0]):
                indexType = str(myLine[1].strip('"'))
            elif re.match('\#ArrayName', myLine[0]):
                arrayName = str(myLine[1].strip('"'))
        elif re.match('[0-9]+[,;\t]+[0-9]+', zeile):
            myLine = re.split('[\t ;,]+', zeile)
            data[int(myLine[0])] = int(myLine[1])
    lowestKey = min(data.keys());
    biggestKey = max(data.keys());
    return  dataType, indexType, arrayName, biggestKey, lowestKey, highValue, lowValue, data


def write_header_file(filename, content, additionalIncludes):
    headerfile = open(filename, 'w')
    includeGuard = re.sub('\.', '_', filename) + '_'
    headerfile.write('#ifndef ' + includeGuard + '\n')
    headerfile.write('#define ' + includeGuard + '\n\n')
    for h in additionalIncludes:
        headerfile.write('#include ' + h + '\n')
    headerfile.write('\n')
    headerfile.write(content)
    headerfile.write('\n\n#endif /* ' + includeGuard + ' */\n\n')
    headerfile.close()
    return


def write_body_file(bodyFileName, headerFileName, table, funct):
    bodyfile = open(bodyFileName, 'w')
    bodyfile.write('#include "' + headerFileName + '"\n\n')
    bodyfile.write(table)
    bodyfile.write('\n\n')
    bodyfile.write(funct)
    bodyfile.write('\n\n')
    bodyfile.close()
    return


def print_usage():
    print "MOEP: Benutzung python", sys.argv[0], "bla.csv bla.h bla.c"
    print "bla.csv muss existieren, bla.h und bla.c werden ueberschrieben"



if len(sys.argv) < 4:
    print_usage()
    sys.exit()


csvFile = sys.argv[1]
headerFileName = sys.argv[2] 
bodyFileName = sys.argv[3]


dataType, indexType, arrayName, biggestKey, lowestKey, highValue, lowValue, data = parse_csv(csvFile)
fullTable = interpolate_lookup_table(data, lowValue)
header, body = create_lookup_function(dataType, indexType, arrayName, biggestKey, highValue)

write_header_file(headerFileName, header, AdditionalIncludes)
write_body_file(bodyFileName, headerFileName, create_printable_table(fullTable, dataType, arrayName), body)

sys.exit()