using LightXML

macro logmsg(s)
end

include("../src/types.jl")

name2sym(name) = join(map(uppercasefirst, split(name, '-')))

const GEN_TOP = """
# This file is automatically generated.
# Do not edit this file by hand.
# Make changes to gen.jl or the source specification instead.
"""

const GEN_BOTTOM = "# end generated code"

const CLS_TOP = """# Classes
const CLASS_MAP = Dict{TAMQPClassId,ClassSpec}("""

const CLS_BOTTOM = """) # CLASS_MAP")

function make_classmethod_map()
    cmmap = Dict{Tuple{Symbol,Symbol},MethodSpec}()
    for v in values(CLASS_MAP)
        for m in values(v.method_map)
            cmmap[(v.name,m.name)] = m
        end
    end
    cmmap
end

const CLASSNAME_MAP = Dict{Symbol,ClassSpec}(v.name => v for v in values(CLASS_MAP))
const CLASSMETHODNAME_MAP = make_classmethod_map()
# end Classes
"""

const DOMAIN_TOP = "# Domains"
const DOMAIN_BOTTOM = "# end Domains\n"

const domainmap = Dict{String,Type}(
    "bit" => TAMQPBit,
    "octet" => TAMQPOctet,
    "short" => TAMQPShortInt,
    "long" => TAMQPLongInt,
    "longlong" => TAMQPLongLongInt,
    "shortstr" => TAMQPShortStr,
    "longstr" => TAMQPLongStr,
    "timestamp" => TAMQPTimeStamp,
    "table" => TAMQPFieldTable,
    "class-id" => TAMQPClassId,
    "method-id" => TAMQPMethodId
)

const precreated_consts = ["FrameEnd"]
const clsindent = " "^4
const methindent = " "^8
const argsindent = " "^12

function gen_spec(specfile)
    xdoc = parse_file(specfile)
    amqp = root(xdoc)

    println("# Source: ", specfile)
    println(GEN_TOP)

    println("const AMQP_VERSION = v", '"', attribute(amqp, "major"), '.', attribute(amqp, "minor"), '.', attribute(amqp, "revision"), '"')
    println("const AMQP_DEFAULT_PORT = ", attribute(amqp, "port"))
    println("")

    println("# Constants")
    for constant in get_elements_by_tagname(amqp, "constant")
        has_attribute(constant, "class") && continue
        constantname = name2sym(attribute(constant, "name"))
        (constantname in precreated_consts) && continue
        constantvalue = attribute(constant, "value")
        println("const ", constantname, " = ", constantvalue)
    end
    println("")

    println("# Error Codes")
    for constant in get_elements_by_tagname(amqp, "constant")
        !has_attribute(constant, "class") && continue
        cls = attribute(constant, "class")
        name = attribute(constant, "name")
        constantname = name2sym("$cls-$name")
        constantvalue = attribute(constant, "value")
        println("const ", constantname, " = ", constantvalue)
    end
    println("")

    # domains
    println(DOMAIN_TOP)
    for domain in get_elements_by_tagname(amqp, "domain")
        name = attribute(domain, "name")
        (name in keys(domainmap)) && continue
        name = "TAMQP" * name2sym(name)
        typ = domainmap[attribute(domain, "type")].name
        println("const $name = $typ")
    end
    println(DOMAIN_BOTTOM)

    # classes and methods
    println(CLS_TOP)
    clssep = ""
    for cls in get_elements_by_tagname(amqp, "class")
        clsname = Symbol(name2sym(attribute(cls, "name")))
        clsidx = parse(Int, attribute(cls, "index"))

        println(clsindent, clssep, "$clsidx => ClassSpec($clsidx, :$clsname, Dict{TAMQPMethodId, MethodSpec}(")
        isempty(clssep) && (clssep = ", ")

        methsep = ""
        for meth in get_elements_by_tagname(cls, "method")
            methname = Symbol(name2sym(attribute(meth, "name")))
            methidx = parse(Int, attribute(meth, "index"))
            methargs = Pair{Symbol,Type}[]
            methrespelem = find_element(meth, "response")
            methresp = (methrespelem === nothing) ? :Nothing : Symbol(name2sym(attribute(methrespelem, "name")))
            println(methindent, methsep, "$methidx => MethodSpec($methidx, :$methname, :$methresp, Pair{Symbol,DataType}[")
            isempty(methsep) && (methsep = ", ")

            argssep = ""
            for arg in get_elements_by_tagname(meth, "field")
                fieldname = Symbol(name2sym(attribute(arg, "name")))
                fielddomain = attribute(arg, "domain")
                if fielddomain === nothing
                    fielddomain = attribute(arg, "type")
                end
                fieldtype = (fielddomain in keys(domainmap)) ? domainmap[fielddomain].name : ("TAMQP" * name2sym(fielddomain))
                println(argsindent, argssep, ":$fieldname => $fieldtype")
                isempty(argssep) && (argssep = ", ")
            end

            println(methindent, "]) # method $methname")
        end

        println(clsindent, ")) # class $clsname")
    end
    println(CLS_BOTTOM)

    println(GEN_BOTTOM)
end

gen_spec("amqp0-9-1.extended.xml")
