#!/bin/python
# -*- coding: utf-8 -*-
#######################################################
#
# xmlparser generator - by David++ (2014/08/15)
# 
# usage:
# xmlpg.py --help
#
#######################################################

import sys
import os
import getopt
import xml.etree.ElementTree as ET


#######################################################
#
# Helper Functions 
#
#######################################################

def printline(file, depth, text):
    """print line with indent"""
    for i in range(0, depth):
        print >> file, '\t',
    print >> file, text


def NodeAsContainer(node):
    container = ''
    key = ''
    for attr in node.attrib:
        if attr in ('container_', 'cont_'):
            container = node.attrib[attr]
        elif attr in ('key_'):
            key = node.attrib[attr]
    return container, key


def Node2Struct(node):
    return node.tag[0:1].upper() + node.tag[1:]


def Node2Member(node):
    return node.tag[0:1].lower() + node.tag[1:]


def NodeHasContent(node):
    return node.text and node.text.strip()


def CppType(type):
    if (type == 'string'):
        return 'std::' + type
    return type


def IsKeyword(word):
    return word in ('container_', 'cont_', 'key_')


def AttrVarName(name):
    return name + '_';


def IsSeqCont(cont):
    return cont in ('vector', 'list', 'dequeue')


def IsMapCont(cont):
    return cont in ('map', 'multimap')


#######################################################
#
# Generate the c++ header file with config struct.
#
#######################################################
class HeaderGenerator(object):
    """docstring for HeaderGenerator """

    def __init__(self, arg):
        super(HeaderGenerator, self).__init__()
        self.xmlpg = arg

    def gen(self, tree, file=sys.stdout):
        self.genHeader(tree.getroot(), 0, file)

    def genHeader(self, node, depth, file=sys.stdout):
        # print node.tag, node.attrib, node.text
        container, key = NodeAsContainer(node)

        printline(file, depth, 'struct %s {' % Node2Struct(node))

        # memfuctions ...
        printline(file, depth + 1, 'bool parse(XMLParser& xml, XMLParser::NodePtr node);')
        printline(file, depth + 1, 'bool write(XMLWriter& xml, XMLWriter::NodePtr node);')
        printline(file, depth + 1, 'void reset();')
        printline(file, depth + 1, '')

        # content
        if NodeHasContent(node):
            printline(file, depth + 1, '%s& operator () () { return content__; }' % CppType(node.text))
            printline(file, depth + 1, 'const %s& operator () () const { return content__; }' % CppType(node.text))
            printline(file, depth + 1, '%s content__;' % CppType(node.text))
            printline(file, depth + 1, '')

        # accessor
        for attr in node.attrib:
            if not IsKeyword(attr):
                printline(file, depth + 1, 'const %s& %s() { return %s; } ' %
                          (CppType(node.attrib[attr]), attr, AttrVarName(attr)))

        if len(node.attrib) > 0:
            printline(file, depth + 1, '')

        # members
        for attr in node.attrib:
            if not IsKeyword(attr):
                printline(file, depth + 1, '%s %s;' % (CppType(node.attrib[attr]), AttrVarName(attr)))

        for child in node:
            self.genHeader(child, depth + 1, file)

        printline(file, depth, '};')

        if not depth == 0:
            if len(container) > 0:
                if IsMapCont(container):
                    if not node.attrib.has_key(key):
                        print >> sys.stderr, 'node:%s has no attrib:%s' % (node.tag, key)
                        return 0
                    printline(file, depth, 'typedef std::%s<%s, %s> %sMap;' % (
                    container, CppType(node.attrib[key]), Node2Struct(node), Node2Struct(node)))
                    printline(file, depth,
                              'typedef %sMap::iterator %sMapIter;' % (Node2Struct(node), Node2Struct(node)))
                    printline(file, depth,
                              'typedef %sMap::const_iterato
r %sMapConstIter;' % (Node2Struct(node), Node2Struct(node)))
                    printline(file, depth, '%sMap %s;' % (Node2Struct(node), Node2Member(node)))

                elif IsSeqCont(container):
                    print
line(file, depth,
                              'typedef std::%s<%s> %sCont;' % (container, Node2Struct(node), Node2Struct(node)))
                    printline(file, depth,
                              'typedef %sCont::iterator %sContIter;' % (Node2Struct(node), Node2Struct(node)))
                    printline(file, depth, 'typedef %sCont::const_iterator %sContConstIter;' % (
                    Node2Struct(node), Node2Struct(node)))
                    printline(file, depth, '%sCont %s;' % (Node2Struct(node), Node2Member(node)))
                else:
                    print >> sys.stderr, 'node:%s container is invalid:%s' % (node.tag, container)
                    return 0
            else:
                printline(file, depth, '%s %s;' % (Node2Struct(node), Node2Member(node)))

            printline(file, depth, '')


#######################################################
#
# Generate the c++ cpp file with implementation.
#
#######################################################
class CppGenerator(object):
    """docstring for CppGenerator """

    def __init__(self, arg):
        super(CppGenerator, self).__init__()
        self.xmlpg = arg

    def inline(self):
        if self.xmlpg.inline:
            return "inline "
        else:
            return ""

    def gen(self, tree, file=sys.stdout):
        self.gen_parse(Node2Struct(tree.getroot()), tree.getroot(), file)
        self.gen_writer(Node2Struct(tree.getroot()), tree.getroot(), file)
        self.gen_reset(Node2Struct(tree.getroot()), tree.getroot(), file)

    def gen_parse(self, prefix, node, file=sys.stdout):
        printline(file, 0, '%sbool %s::parse(XMLParser& xml, XMLParser::NodePtr node) {' % (self.inline(), prefix))
        printline(file, 1, 'if (!node) return false;')

        if NodeHasContent(node):
            printline(file, 1, 'content__ = xml.getNodeContent<%s>(node);' % (CppType(node.text)))

        for attr in node.attrib:
            if not IsKeyword(attr):
                printline(file, 1, '%s = xml.getNodeProp<%s>(node, "%s");' % (
                AttrVarName(attr), CppType(node.attrib[attr]), attr))

        for child in node:
            container, key = NodeAsContainer(child)
            if len(container) > 0:
                printline(file, 1, '')
                if IsMapCont(container):
                    printline(file, 1,
                              'XMLParser::NodePtr %s_node = xml.getChildNode(node, "%s");' % (child.tag, child.tag))
                    printline(file, 1, 'while (%s_node) {' % child.tag)
                    printline(file, 2, '%s item;' % Node2Struct(child))
                    printline(file, 2, 'if (item.parse(xml, %s_node))' % child.tag)
                    printline(file, 3,
                              '%s.insert(std::make_pair(item.%s, item));' % (Node2Member(child), AttrVarName(key)))
                    printline(file, 2, '%s_node = xml.getNextNode(%s_node, "%s");' % (child.tag, child.tag, child.tag))
                    printline(file, 1, '}')
                elif IsSeqCont(container):
                    printline(file, 1,
                              'XMLParser::NodePtr %s_node = xml.getChildNode(node, "%s");' % (child.tag, child.tag))
                    printline(file, 1, 'while (%s_node) {' % child.tag)
                    printline(file, 2, '%s item;' % Node2Struct(child))
                    printline(file, 2, 'if (item.parse(xml, %s_node))' % child.tag)
                    printline(file, 3, '%s.push_back(item);' % Node2Member(child))
                    printline(file, 2, '%s_node = xml.getNextNode(%s_node, "%s");' % (child.tag, child.tag, child.tag))
                    printline(file, 1, '}')
                else:
                    return 0
            else:
                printline(file, 1, '%s.parse(xml, xml.getChi