#!/home/stef/bin/python

# No. I don't care if this code sucks.
# Python is the second crappiest language on the earth,
# (perl is first). i hate this language, its philosophy,
# its brainfucked logic. I hope it will disappear soon into
# the darkest oblivion ever, but unfortunately there's no 
# better language to achieve this result faster.
# are you a python fan/developer? i don't care. This is my
# personal opinion. You can have handful of technical reasons
# to beat me up and drop me in a pit full of knives, bringing
# all the internet to laugh at me, but i don't care. With 
# python I feel as comfortable as an elephant dressing a pair 
# of blue jeans.
# 
# Dear Stefano from 2004. Here is Stefano from 2014.
# You are an idiot.
# Sincerely.

import sys
import xml.dom.minidom
import string
import apigenparser

class CodeGenHelper:
    """an helper class to deal with strings creation
    """
    def paramString(self,sign):
        """return the parameter string on the type
        int,int,float,char*
        optimal to declare types but not dummy names
        """
        paramString = ""
        for i in xrange(len(sign.paramList)):
            paramString=paramString+sign.paramList[i]
 
            if i != len(sign.paramList)-1:
                paramString=paramString+", "
        return paramString

    def paramStringValued(self,sign):
        """return the parameter string on the type
        int foo,int bar,float baz,char* buz
        optimal to declare types and names 
        """
        paramStringValued = ""
        for i in xrange(len(sign.paramList)):
            paramStringValued=paramStringValued+sign.paramList[i]+" param"+str(i)
    
            if i != len(sign.paramList)-1:
                paramStringValued=paramStringValued+", "
        return paramStringValued

    def paramStringValuedNoTypes(self,sign):
        """return the parameter string on the type
        foo,bar,baz,buz
        optimal to declare only names 
        """

        paramStringValuedNoTypes = ""

        for i in xrange(len(sign.paramList)):
            paramStringValuedNoTypes=paramStringValuedNoTypes+" param"+str(i)
    
            if i != len(sign.paramList)-1:
                paramStringValuedNoTypes=paramStringValuedNoTypes+", "

        return paramStringValuedNoTypes
    def F77InterfaceParameters(self,sign):
        s=""
        
        for i in xrange(len(sign.paramList)):
            s=s+" param"+str(i)
    
            if i != len(sign.paramList)-1:
                paramStringValuedNoTypes=paramStringValuedNoTypes+", "

    def F90Function(self,func):
        s=""

        sign = func.sign

        s=s+"subroutine f90xml_"+func.name+"(ret, "+self.paramStringValuedNoTypes(sign)+")\n"
        s = s + "\timplicit none\n"
        s = s + "\t"+sign.tagToF90(sign.returnType)+", intent(out) :: ret\n"

        for i in xrange(len(sign.paramList)):
            intent = "in"
            if sign.paramList[i] == "error *":
                intent="out"

            s=s+"\t"+sign.tagToF90(sign.paramList[i])+", intent("+intent+") :: param"+str(i)+"\n"    

        s=s+"\tcharacter(len=128) :: func\n"
        s=s+"\tfunc='"+func.name+"'\n\n"
        s=s+"\tcall x"+sign.type+"(ret,func,"+self.paramStringValuedNoTypes(sign)+")\n"
        s=s+"end subroutine\n\n"
        return s









#  subroutine f90xml_di_createDocFromURI(doc,dom,filename,opt,err)
#    implicit none
#    integer, intent(out) :: doc
#    integer, intent(in) :: dom
#    character(len=*), intent(in) :: filename
#    integer, intent(in) :: opt
#    integer, intent(out) :: err


class CodeGenerator:
    def __init__(self):
        self.__parser=apigenparser.ApiGenParser()
    
    def make(self,filename):
        self.__parser.parse(filename)

        f=file("f90xml.f90","w")
    
        f.write("! Automatically generated by APIGen \n")
        f.write("! Changes will not survive. Modify  \n")
        f.write("! apigen.xml file instead           \n")
        f.write("\n")
        f.write("module f90xml\n")
        f.write("\n")
        vars=file("f90vars.inc","r")
        f.writelines(vars.readlines())
        vars.close()
        f.write("\n")
        f.write("contains\n")
        
        f.close()

        f=file("signatureList","w")
    
        f.write("/* Automatically generated by APIGen */\n")
        f.write("/* Changes will not survive. Modify    */\n")
        f.write("/* apigen.xml file instead                     */\n")
        f.write("\n\n")
        f.close()

        for sign in self.__parser.signList:
            self.__createCodeForSign(sign)

        f=file("f90xml.f90","a")
    
        f.write("\n")
        f.write("end module f90xml\n")
        f.write("\n")
        
        f.close()
    def __createCodeForSign(self,sign):
        self.__createMultiplexerHeader(sign)
        self.__createMultiplexerCFile(sign)
        self.__createImplementationForSign(sign)
        self.__createAPIDocsForSign(sign)
        self.__createF90InterfaceForSign(sign)

    def __createMultiplexerHeader(self,sign):
        type=sign.type
        returnType=sign.returnType
        paramList=sign.paramList

        f=file("mp_"+type+".h","w")

        f.write("/* automatically generated by APIGen */\n")
        f.write("\n")
        f.write("#include \"stringobj.h\"\n")
        f.write("#include \"defines.h\"\n")
        f.write("\n")
        f.write("void f77xml_"+type+"_print();\n")
        a="void f77xml_"+type+"_multiplexer("+returnType+", StringObj *, "
        
        for i in xrange(len(paramList)):
            a=a+paramList[i]
            if i != len(paramList)-1:
                a=a+", "
            else:
                a=a+");\n"

        f.write(a)
        f.close()


    def __createMultiplexerCFile(self,sign):
        type=sign.type
        returnType=sign.returnType
    
        cg=CodeGenHelper()
        
        paramString=cg.paramString(sign)
        paramStringValued=cg.paramStringValued(sign)
        paramStringValuedNoTypes=cg.paramStringValuedNoTypes(sign)

        f=file("mp_"+type+".c","w")
    
        f.write("/* Automatically generated by APIGen */\n")
        f.write("/* Changes will not survive. Modify    */\n")
        f.write("/* apigen.xml file instead                     */\n")
        f.write("\n\n")
        f.write("#include <gdome.h>\n")
        f.write("#include <gdome-xpath.h>\n")
        f.write("#include <unistd.h>\n")
        f.write("#include <assert.h>\n")
        f.write("#include \"mp_"+type+".h\"\n")
        f.write("#include \"cache.h\"\n")
        f.write("#include \"defines.h\"\n")
        f.write("#include \"domimpl.h\"\n")
        f.write("#include \"error.h\"\n")
        f.write("\n")
        f.write("\n")
        f.write("/*\n")
        f.write(" * multiplexer definition for "+type+" type functions\n")
        f.write(" *\n")
        f.write(" */\n")
        f.write("\n")
        f.write("\n")
        f.write("struct signatures_"+type+" {\n")
        f.write("\tchar funcname[FUNCTION_NAME_MAX_LEN];\n")
        f.write("\tvoid (*functor)("+returnType+", "+paramString+");\n")
        f.write("};\n")
        f.write("\n")
        f.write("\n")
        
        f.write("/* prototypes for handling functions */\n")
        f.write("\n")
    
        for func in sign.functionList:
            f.write("private void f77xml__"+func.name+"("+returnType+", "+paramString+");\n")
    
        f.write("\n")
        f.write("\n")
        f.write("private struct signatures_"+type+" "+type+"Array[] = {\n")
        for func in sign.functionList:
            f.write("\t{\""+func.name+"\", &f77xml__"+func.name+" },\n")
        f.write("\t{\"\", NULL }\n")
        
        f.write("};\n")
        f.write("\n")
        f.write("\n")
    
        f.write("void f77xml_"+type+"_print() {\n")
        f.write("\tstruct signatures_"+type+" *signaturePtr;\n")
        f.write("\tsignaturePtr="+type+"Array;\n")
        f.write("\tprintf(\""+type+" ("+returnType+", "+paramString+")\\n\");\n")
    
        f.write("\twhile (signaturePtr->functor) {\n")
        f.write("\t\tprintf(\"    %s\\n\",signaturePtr->funcname);\n")
        f.write("\t\tsignaturePtr++;\n")
        f.write("\t}\n")
        f.write("}\n")
        f.write("\n")
        f.write("\n")
    
        f.write("void f77xml_"+type+"_multiplexer("+returnType+" ret, StringObj *funcname, "+paramStringValued+") {\n")
        f.write("\tstruct signatures_"+type+" *signaturePtr;\n")
        f.write("\tvoid (*functorPtr)("+returnType+", "+paramString+");\n")
        f.write("\tsignaturePtr="+type+"Array;\n")
        f.write("#ifdef DEBUG\n")
        f.write("\tprintf(\""+type+" multiplexer: %s\\n\",funcname->str);\n")
        f.write("#endif\n")
        f.write("\n")
        f.write("\n")
        f.write("\twhile (signaturePtr->functor) {\n")
        f.write("\t\tif (!strcmp(funcname->str,signaturePtr->funcname)) {\n")
        f.write("\t\t\tfunctorPtr=signaturePtr->functor;\n")
        f.write("\t\t\t(*functorPtr)(ret, "+paramStringValuedNoTypes+");\n")
        f.write("\n")
        f.write("\t\t\treturn;\n")
        f.write("\t\t}\n")
        f.write("\t\tsignaturePtr++;\n")
        f.write("\t}\n")
        f.write("\n")
        f.write("\n")
        f.write("\tprintf(\"Function %s for "+type+" multiplexer undefined.\\n\",funcname->str);\n")
        f.write("}\n")
        f.write("\n")
        f.write("#include \"implementation_"+type+".c\"")
        f.write("\n")
        f.write("\n")
    
        f.close()

    def __createImplementationForSign(self,sign):
        type=sign.type
        returnType=sign.returnType
        paramList=sign.paramList
    
        cg = CodeGenHelper()
        paramString = cg.paramString(sign)
        paramStringValued = cg.paramStringValued(sign)
        paramStringValuedNoTypes = cg.paramStringValuedNoTypes(sign)
        
        f=file("implementation_"+type+".c","w")
    
        f.write("/* Automatically generated by APIGen */\n")
        f.write("/* Changes will not survive. Modify    */\n")
        f.write("/* apigen.xml file instead                     */\n")
        f.write("\n\n")
        
        for func in sign.functionList:    
            f.write(func.code)

    def __createF90InterfaceForSign(self,sign):

        f=file("f90xml.f90","a")

        type=sign.type
        returnType=sign.returnType
        paramList=sign.paramList

        cg=CodeGenHelper()
    
        for func in sign.functionList:    
            f.write(cg.F90Function(func))
#f.write("void f77xml_"+func.name+"_("+cg.F77InterfaceParameters(sign)+") {\n")
#            f.write("\tx"+type+"()\n")
#            f.write("}\n")

    def __createAPIDocsForSign(self,sign):        
        f=file("signatureList","a")
        
        for func in sign.functionList:
            f.write(func.name+"\t\t("+sign.type+" "+sign.signCode()+")\n")


            

if __name__ == '__main__':
    if (len(sys.argv) < 2):
        print "%s <xmlfile>" % sys.argv[0]
        sys.exit()

    codegen=CodeGenerator()
    codegen.make(sys.argv[1])

