#!/usr/bin/env python3
#
#@file make_protocol.py
#@brief python script to generate code for PolyPacket
#@author Jason Berger
#@date 02/19/2019
#

import sys
import os
from mako.template import Template
import yaml
import argparse
import pkgutil

sizeDict = {
    "uint8" : 1,
    "int8" : 1,
    "char" : 1,
    "uint16" : 2,
    "int16" : 2,
    "uint32" : 4,
    "int32" : 4,
    "int64" : 8,
    "uint64" : 8,
    "int" : 4,
    "string" : 16,
}

args = None
parser = None


class FieldVal:
    def __init__(self, name):
        self.field = 0
        self.name = name 
        self.val = 0 
        self.desc = ""

class RegFlag:
    def __init__(self, name):
        self.name = name 
        self.reg = 0
        self.val = 0 
        self.desc = ""    

class RegField:
    def __init__(self, name):
        self.reg = 0
        self.name = name
        self.mask = 0XFFFFFFFF
        self.desc = ""
        self.vals = []
    
    def addVal(self, fieldVal):
        fieldVal.field = self
        self.vals.append(fieldVal)

class DeviceReg:
    def __init__(self,name):
        self.name = name
        self.addr = 0
        self.type ="uint8_t"
        self.perm = "RW"
        self.desc = ""
        self.default = 0
        self.fields = []
        self.flags = []
        self.size = 1
        self.device = 0
    
    def addField(self, field):
        field.reg = self
        self.fields.append(field)
    
    def addFlag(self, flag):
        flag.reg = self 
        self.flags.append(flag)
    
    def formatHex(self, val):
        return "{0:#0{1}x}".format(val,(self.size *2) + 2)

    def printAddr(self):
        return "{0:#0{1}x}".format(self.addr,(self.device.addrSize *2) + 2)


class Device:
    def __init__(self, name):
        self.name = name
        self.prefix = ""
        self.bus = "I2C"
        self.addr = 0
        self.addrSize =1
        self.aiMask = 0 #auto incrment mask 
        self.regs = {}

    def addReg(self, reg):
        reg.device = self
        self.regs[reg.name] = reg


    def parseYAML(self,yamlFile):
        data = open(yamlFile)
        objDevice = yaml.load(data , Loader=yaml.FullLoader)

        if 'name' in objDevice:
            self.name = objDevice['name']
        
        if 'prefix' in objDevice:
            self.prefix = objDevice['prefix']
        
        if 'bus' in objDevice:
            self.bus = objDevice['bus']
        
        if 'ai_mask' in objDevice:
            self.aiMask = objDevice['ai_mask'] #int(objDecive['ai_mask'],0)
        
        if 'registers' in objDevice:
            for regNode in objDevice['registers']:
                newReg = DeviceReg(list(regNode.keys())[0])
                regItem = list(regNode.values())[0]

                if 'addr' in regItem:
                    newReg.addr = regItem['addr'] #int(regItem['addr'],0) 
                if 'type' in regItem:
                    newReg.type = regItem['type']
                    newReg.size = sizeDict[newReg.type.replace("_t","")]
                if 'size' in regItem:
                    newReg.size = regItem['size']
                if 'perm' in regItem:
                    newReg.perm = regItem['perm'].upper()
                if 'desc' in regItem:
                    newReg.desc = regItem['desc']
                if 'name' in regItem:
                    newReg.desc = regItem['name']

                self.addReg(newReg)      
        
        if 'values' in objDevice:
            for propNode in objDevice['values']:
                regName = list(propNode.keys())[0]
                propItem = list(propNode.values())[0]
                if regName in self.regs:
                    curReg = self.regs[regName]  
                    if 'fields' in propItem:
                        for fieldNode in propItem['fields']:
                            newField = RegField(list(fieldNode.keys())[0])
                            fieldItem = list(fieldNode.values())[0]
                            if 'mask' in fieldItem:
                                newField.mask = fieldItem['mask']
                            if 'values' in fieldItem:
                                for valNode in fieldItem['values']:
                                    newVal = FieldVal(list(valNode.keys())[0])
                                    valItem = list(valNode.values())[0]
                                    if 'val' in valItem:
                                        newVal.val = valItem['val']
                                    if 'desc' in valItem:
                                        newVal.desc = valItem['desc']
                                    if 'name' in valItem:
                                        newVal.desc = valItem['name']
                                    newField.addVal(newVal)
                            
                            curReg.addField(newField)

                    if 'flags' in propItem:
                        for flagNode in propItem['flags']:
                            newFlag = RegFlag(list(flagNode.keys())[0])
                            flagItem = list(flagNode.values())[0]
                            if 'name' in flagItem:
                                newFlag.name = flagItem['name']
                            if 'mask' in flagItem:
                                newFlag.val = flagItem['mask']
                            if 'val' in flagItem:
                                newFlag.val = flagItem['val']
                            if 'desc' in flagItem:
                                newFlag.desc = flagItem['desc']
                            
                            curReg.addFlag(newFlag)

        print("Parsed device: " + self.name )
        print( "registers: " + str(len(self.regs)))




        



        

# Initialize the argument parser
def init_args():
    global parser
    parser = argparse.ArgumentParser("Tool to generate code and documentation for PolyPacket protocol")
    parser.add_argument('-i', '--input', type=str, help='input file to parse', default="")
    parser.add_argument('-o', '--output', type=str, help='Output path', default="")
    parser.add_argument('-d', '--document', type=str, help='documentation path', default="")
    parser.add_argument('-t', '--template', type=str, help='path to create template', default="")



def buildTemplate(object, templateFile, outputFile):
    template = Template(pkgutil.get_data('mrtutils',templateFile) )
    text_file = open( outputFile , "w")
    text_file.write("\n".join(template.render(obj = object).splitlines()))
    text_file.close()

def buildRegisterFiles(device , outputPath):
    baseName = device.name + "_regs"
    buildTemplate(device, 'templates/reg_header_template.h', outputPath +"/"+baseName + ".h" )
    buildTemplate(device, 'templates/reg_source_template.c', outputPath +"/"+baseName + ".c" )

def main():
    global path
    global parser
    global args

    init_args()
    args= parser.parse_args()
    argCount = len(sys.argv)
    

    inputFile = args.input
    path = args.output
    docPath = args.document

    device = Device('unnamed')

    if inputFile == "":
        print("No input file specified, use -e to create an example file")
        sys.exit()

    # if os.path.isfile(inputFile):
    print("parsing " + inputFile )
    device.parseYAML(inputFile)

    print(" Generating Register files for " + device.name)
    buildRegisterFiles(device, path)


if __name__ == "__main__":
    main()