#! /usr/bin/env python
import sys,os
import pprint

###############
# Notes:
# -Strings can include whitespace :-/
# -Can a string include a # ?
###############

listType= type([])
dictType= type({})

def _smartInt(str):
    if str[0:2]=="0x": return int(str,16)
    else: return int(str)

def _smartLong(str):
    if str[0:2]=="0x": return long(str,16)
    else: return long(str)

def _parseLine(line, contextStack):
    if len(line)==0:
        return True
    words= line.split(None,2)
    if len(words)==0:
        return True
    vecMode= False
    if words[0][0]=="@":
        vecMode= True
        words[0]= words[0][1:]
    currentName,currentContext,currentTypedefs= contextStack[-1]
    if len(words)>2:
        keyword= words[1].upper()
        if keyword=="STR":
            if vecMode:
                res= []
                vwords= words[2].split(',')
                for v in vwords:
                    res.append(v.strip())
                resVal= (keyword,res)
                if currentContext.has_key(words[0]) \
                        and type(currentContext[words[0]])==listType:
                    currentContext[words[0]].append(resVal)
                else:
                    currentContext[words[0]]= resVal                
            else:
                strVal= (keyword,words[2].strip())
                if currentContext.has_key(words[0]) \
                        and type(currentContext[words[0]])==listType:
                    currentContext[words[0]].append(strVal)
                else:
                    currentContext[words[0]]= strVal
            return True
#          elif keyword=="TAI":
#             if vecMode:
#                 res= []
#                 vwords= words[2].split(',')
#                 for v in vwords:
#                     res.append(v.strip())
#                 resVal= (keyword,res)
#                 if currentContext.has_key(words[0]) \
#                         and type(currentContext[words[0]])==listType:
#                     currentContext[words[0]].append(resVal)
#                 else:
#                     currentContext[words[0]]= resVal                
#             else:
#                 strVal= (keyword,words[2].strip())
#                 if currentContext.has_key(words[0]) \
#                         and type(currentContext[words[0]])==listType:
#                     currentContext[words[0]].append(strVal)
#                 else:
#                     currentContext[words[0]]= strVal
#             return True
        elif keyword=="BOOL":
            if vecMode:
                res= []
                vwords= words[2].split()
                if len(vwords)==1:
                    vwords= words[2].split(",")
                for v in vwords:
                    res.append(bool(v.strip()))
                resVal= (keyword,res)
                if currentContext.has_key(words[0]) \
                        and type(currentContext[words[0]])==listType:
                    currentContext[words[0]].append(resVal)
                else:
                    currentContext[words[0]]= resVal                
            else:
                boolVal= (keyword,bool(words[2].strip()))
                if currentContext.has_key(words[0]) \
                        and type(currentContext[words[0]])==listType:
                    currentContext[words[0]].append(boolVal)
                else:
                    currentContext[words[0]]= boolVal
            return True
        elif keyword=="S16":
            if vecMode:
                res= []
                vwords= words[2].split()
                if len(vwords)==1:
                    vwords= words[2].split(",")
                for v in vwords:
                    res.append(_smartInt(v.strip()))
                resVal= (keyword,res)
                if currentContext.has_key(words[0]) \
                        and type(currentContext[words[0]])==listType:
                    currentContext[words[0]].append(resVal)
                else:
                    currentContext[words[0]]= resVal                
            else:
                intVal= (keyword,_smartInt(words[2].strip()))
                if currentContext.has_key(words[0]) \
                        and type(currentContext[words[0]])==listType:
                    currentContext[words[0]].append(intVal)
                else:
                    currentContext[words[0]]= intVal
            return True
        elif keyword=="S32":
            if vecMode:
                res= []
                vwords= words[2].split()
                if len(vwords)==1:
                    vwords= words[2].split(",")
                for v in vwords:
                    res.append(_smartInt(v.strip()))
                resVal= (keyword,res)
                if currentContext.has_key(words[0]) \
                        and type(currentContext[words[0]])==listType:
                    currentContext[words[0]].append(resVal)
                else:
                    currentContext[words[0]]= resVal                
            else:
                intVal= (keyword,_smartInt(words[2].strip()))
                if currentContext.has_key(words[0]) \
                        and type(currentContext[words[0]])==listType:
                    currentContext[words[0]].append(intVal)
                else:
                    currentContext[words[0]]= intVal
            return True
        elif keyword=="S64":
            if vecMode:
                res= []
                vwords= words[2].split()
                if len(vwords)==1:
                    vwords= words[2].split(",")
                for v in vwords:
                    res.append(_smartLong(v.strip()))
                resVal= (keyword,res)
                if currentContext.has_key(words[0]) \
                        and type(currentContext[words[0]])==listType:
                    currentContext[words[0]].append(resVal)
                else:
                    currentContext[words[0]]= resVal                
            else:
                intVal= (keyword,_smartLong(words[2].strip()))
                if currentContext.has_key(words[0]) \
                        and type(currentContext[words[0]])==listType:
                    currentContext[words[0]].append(intVal)
                else:
                    currentContext[words[0]]= intVal
            return True
        elif keyword=="U16":
            if vecMode:
                res= []
                vwords= words[2].split()
                if len(vwords)==1:
                    vwords= words[2].split(",")
                for v in vwords:
                    res.append(_smartInt(v.strip()))
                resVal= (keyword,res)
                if currentContext.has_key(words[0]) \
                        and type(currentContext[words[0]])==listType:
                    currentContext[words[0]].append(resVal)
                else:
                    currentContext[words[0]]= resVal                
            else:
                intVal= (keyword,_smartInt(words[2].strip()))
                if currentContext.has_key(words[0]) \
                        and type(currentContext[words[0]])==listType:
                    currentContext[words[0]].append(intVal)
                else:
                    currentContext[words[0]]= intVal
            return True
        elif keyword=="F32":
            if vecMode:
                res= []
                vwords= words[2].split()
                if len(vwords)==1:
                    vwords= words[2].split(",")
                for v in vwords:
                    res.append(float(v.strip()))
                resVal= (keyword,res)
                if currentContext.has_key(words[0]) \
                        and type(currentContext[words[0]])==listType:
                    currentContext[words[0]].append(resVal)
                else:
                    currentContext[words[0]]= resVal                
            else:
                floatVal= (keyword,float(words[2].strip()))
                if currentContext.has_key(words[0]) \
                        and type(currentContext[words[0]])==listType:
                    currentContext[words[0]].append(floatVal)
                else:
                    currentContext[words[0]]= floatVal
            return True
        elif keyword=="F64":
            if vecMode:
                res= []
                vwords= words[2].split()
                if len(vwords)==1:
                    vwords= words[2].split(",")
                for v in vwords:
                    res.append(float(v.strip()))
                resVal= (keyword,res)
                if currentContext.has_key(words[0]) \
                        and type(currentContext[words[0]])==listType:
                    currentContext[words[0]].append(resVal)
                else:
                    currentContext[words[0]]= resVal                
            else:
                floatVal= (keyword,float(words[2].strip()))
                if currentContext.has_key(words[0]) \
                        and type(currentContext[words[0]])==listType:
                    currentContext[words[0]].append(floatVal)
                else:
                    currentContext[words[0]]= floatVal
            return True
        elif keyword=="U64":
            if vecMode:
                res= []
                vwords= words[2].split()
                if len(vwords)==1:
                    vwords= words[2].split(",")
                for v in vwords:
                    res.append(_smartLong(v.strip()))
                resVal= (keyword,res)
                if currentContext.has_key(words[0]) \
                        and type(currentContext[words[0]])==listType:
                    currentContext[words[0]].append(resVal)
                else:
                    currentContext[words[0]]= resVal                
            else:
                longVal= (keyword,_smartLong(words[2].strip()))
                if currentContext.has_key(words[0]) \
                        and type(currentContext[words[0]])==listType:
                    currentContext[words[0]].append(longVal)
                else:
                    currentContext[words[0]]= longVal
            return True
        elif keyword=="U8":
            if vecMode:
                res= []
                vwords= words[2].split()
                if len(vwords)==1:
                    vwords= words[2].split(",")
                for v in vwords:
                    res.append(_smartInt(v.strip()))
                resVal= (keyword,res)
                if currentContext.has_key(words[0]) \
                        and type(currentContext[words[0]])==listType:
                    currentContext[words[0]].append(resVal)
                else:
                    currentContext[words[0]]= resVal                
            else:
                intVal= (keyword,_smartInt(words[2].strip()))
                if currentContext.has_key(words[0]) \
                        and type(currentContext[words[0]])==listType:
                    currentContext[words[0]].append(intVal)
                else:
                    currentContext[words[0]]= intVal
            return True
        elif words[0]=="TYPE":
            typeName= keyword
            typeFieldRec= words[2].split()
            currentTypedefs[typeName]= typeFieldRec
            #print "defining type %s"%typeName
            return True
        elif currentTypedefs.has_key(words[1]):
            vwords= words[2].split()
            if len(vwords)==1:
                vwords= words[2].split(",")
            if len(vwords)==len(currentTypedefs[words[1]]):
                recName= words[0]
                recType= words[1]
                recMetadata= {}
                offset= 0
                for field in currentTypedefs[recType]:
                    recMetadata[field]= vwords[offset]
                    offset += 1
                if currentContext.has_key(recName) \
                        and type(currentContext[recName])==listType:
                    currentContext[recName].append(recMetadata)
                else:
                    currentContext[recName]= recMetadata
                #print "loaded %s rec for %s"%(recType,recName)
                return True
            else:
                return False

    if len(words)>1:
        if words[1].upper()=="METADATA":
            newContext= {}
            newTypedefs= {}
            currentName= words[0]
            contextStack.append((currentName,newContext,newTypedefs))
            currentContext[currentName]= newContext
            currentContext= newContext
            currentTypedefs= newTypedefs
            #print "%s%s begins"%(len(contextStack)*"  ",words[0])
            return True
        elif words[1].upper()=="MULTI":
            currentContext[words[0]]= []
            return True

    if words[0].upper()=="END":
        exitingName,exitingContext,exitingTypedefs= contextStack[-1]
        typeCounts= {}
        for k in exitingContext.keys():
            if type(exitingContext[k])==dictType:
                pass
            elif type(exitingContext[k])==listType:
                for maybeTuple in exitingContext[k]:
                    if type(maybeTuple)==dictType:
                        pass
                    else:
                        tp,val= maybeTuple
                        if not typeCounts.has_key(tp):
                            typeCounts[tp]= 0
                        typeCounts[tp] += 1
            else:
                tp,val= exitingContext[k]
                if not typeCounts.has_key(tp):
                    typeCounts[tp]= 0
                typeCounts[tp] += 1
        #print "%s%s: %d elements (%s) via %d typedefs"%\
            (len(contextStack)*"  ",
             exitingName,
             len(exitingContext.keys()),
             typeCounts,
             len(exitingTypedefs.keys()))
        contextStack.pop()
        currentName,currentContext,currentTypedefs= contextStack[-1]
        #print currentContext
        return True

    #print "words: %s"%words
    #print "currentTypedefs: %s"%currentTypedefs
    return False

def parse(f, inContext={}):
    "Parse the file object f as a config; return (topname,configdict)"
    currentName= ""
    currentTypedefs= {}
    currentContext= inContext.copy()
    contextStack= [(currentName,currentContext,currentTypedefs)]
    whichLine= 0
    for l in f: # for each line
        whichLine += 1
        newEnd= l.find("#")
        if newEnd>=0: l= l[:newEnd]
        try:
            if not _parseLine(l,contextStack):
                raise RuntimeError("Line %d unparsable: <%s>"%(whichLine,l))
        except ValueError:
            raise RuntimeError("Line %d unparsable (bad value field): <%s>"%\
                                   (whichLine,l))
    currentName,currentContext,currentTypedefs= contextStack[0]
    return currentContext

def main():
    pp= pprint.PrettyPrinter(indent=4)
    for fname in sys.argv[1:]:
        print "########### %s"%fname
        f= open(fname,"r")
        config= parse(f)
        f.close()
        pp.pprint(config)

############
# Main hook
############

if __name__=="__main__":
    main()
