#include <libxml/parser.h>
#include <libxml/tree.h>

#include "hurricane/Technology.h"

#include "ATechnology.h"

#include "ATechnologyXmlParser.h"


using namespace Hurricane;


namespace {

void syntaxError(const string& reason) {
    throw Error(reason);
}

DbU::Unit getUnitValue(double physicalValue) {
    return DbU::grid(DbU::physicalToGrid(physicalValue, DbU::Micro));
}

void readPhysicalRules(xmlNode* node, ATechnology* aTechnology) {
    if (node->type == XML_ELEMENT_NODE && node->children) {
        for (xmlNode* ruleNode = node->children;
            ruleNode;
            ruleNode = ruleNode->next) {
            if (ruleNode->type == XML_ELEMENT_NODE) {
                if (xmlStrEqual(ruleNode->name, (xmlChar*)"arule")) {
                    xmlChar* ruleNameC = xmlGetProp(ruleNode, (xmlChar*)"name");
                    xmlChar* valueC = xmlGetProp(ruleNode, (xmlChar*)"value");
                    xmlChar* refC = xmlGetProp(ruleNode, (xmlChar*)"ref");
                    xmlChar* layer1C = xmlGetProp(ruleNode, (xmlChar*)"layer1");
                    xmlChar* layer2C = xmlGetProp(ruleNode, (xmlChar*)"layer2");
                    if (ruleNameC && valueC && refC && layer1C && layer2C) {
                        string ruleName((const char*)ruleNameC);
                        double value = atof((const char*)valueC);
                        DbU::Unit unitValue = getUnitValue(value);
                        cerr << value << ", " << unitValue << endl;
                        string reference((const char*)refC);
                        Name layer1Name((const char*)layer1C);
                        Name layer2Name((const char*)layer2C);
                        aTechnology->addPhysicalRule(ruleName, layer1Name, layer2Name, false, unitValue, reference);
                    }
                } else if (xmlStrEqual(ruleNode->name, (xmlChar*)"rule")) {
                    xmlChar* ruleNameC = xmlGetProp(ruleNode, (xmlChar*)"name");
                    xmlChar* valueC = xmlGetProp(ruleNode, (xmlChar*)"value");
                    xmlChar* refC = xmlGetProp(ruleNode, (xmlChar*)"ref");
                    xmlChar* layerC = xmlGetProp(ruleNode, (xmlChar*)"layer");
                    xmlChar* layer1C = xmlGetProp(ruleNode, (xmlChar*)"layer1");
                    xmlChar* layer2C = xmlGetProp(ruleNode, (xmlChar*)"layer2");
                    if (ruleNameC && valueC && refC) {
                        string ruleName((const char*)ruleNameC);
                        double value = atof((const char*)valueC);
                        DbU::Unit unitValue = getUnitValue(value);
                        string reference((const char*)refC);
                        if (layerC) {
                            Name layerName((const char*)layerC);
                            aTechnology->addPhysicalRule(ruleName, layerName, unitValue, reference);
                        } else if (layer1C && layer2C) {
                            Name layer1Name((const char*)layer1C);
                            Name layer2Name((const char*)layer2C);
                            aTechnology->addPhysicalRule(ruleName, layer1Name, layer2Name, true, unitValue, reference);
                        } else {
                            aTechnology->addPhysicalRule(ruleName, unitValue, reference);
                        }
                    } else {
                        syntaxError("");
                    }
                }
            }
        }
    }
}

ATechnology* parseFileAsTechnology(const char* filePath, Technology* technology) {
    ATechnology* aTechnology = ATechnology::create(technology);


    xmlDocPtr doc; /* the resulting document tree */

    doc = xmlReadFile(filePath, NULL, 0);
    if (doc == NULL) {
        fprintf(stderr, "Failed to parse %s\n", filePath);
        exit(5);
    }

    /*Get the design element node */
    xmlNode* rootElement = xmlDocGetRootElement(doc);
    if (rootElement->type == XML_ELEMENT_NODE &&
            xmlStrEqual(rootElement->name, (xmlChar*)"technology")) {
        xmlNode* child = rootElement->children;
        for (xmlNode* node = child; node; node = node->next) {
            if (node->type == XML_ELEMENT_NODE) {
                if (xmlStrEqual(node->name, (xmlChar*)"physical_rules")) {
                    readPhysicalRules(node, aTechnology);
                } else {
                    syntaxError("unknown tag");
                }
            }
        }
    }
}

}

ATechnology* ATechnologyXmlParser::parse(const char* filePath, Technology* technology) {

    LIBXML_TEST_VERSION

    ATechnology* aTechnology = parseFileAsTechnology(filePath, technology);

    xmlCleanupParser();
    /*
     * this is to debug memory for regression tests
     */

    xmlMemoryDump();

    return NULL;
}
