/*
 *  Circuit.cpp
 *  openChams
 *
 *  Created by damien dupuis on 18/12/09.
 *  Copyright 2009 UPMC / LIP6. All rights reserved.
 *
 */

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <algorithm>
using namespace std;

#include "vlsisapd/openChams/Circuit.h"
#include "vlsisapd/openChams/Netlist.h"
#include "vlsisapd/openChams/Instance.h"
#include "vlsisapd/openChams/Net.h"
#include "vlsisapd/openChams/Schematic.h"
#include "vlsisapd/openChams/SimulModel.h"
#include "vlsisapd/openChams/Sizing.h"
#include "vlsisapd/openChams/Transistor.h"
#include "vlsisapd/openChams/Operator.h"
#include "vlsisapd/openChams/OpenChamsException.h"

namespace {
	template<class T> T getValue(xmlChar* str) {
	    std::istringstream iss;
    	iss.str((const char*) str);
	    T res;
    	iss >> res;
    	return res;
	}
}

namespace OpenChams {
    
static bool readCircuitParametersDone = false;
static bool readSimulModelsDone       = false;
static bool readNetListDone           = false;
static bool readInstancesDone         = false;
static bool readNetsDone              = false;
static bool readSchematicDone         = false;
static bool readSizingDone            = false;
    
Circuit::Circuit(Name name, Name techno) : _name(name), _techno(techno), _netlist(NULL), _schematic(NULL), _sizing(NULL) {
    readCircuitParametersDone = false;
    readSimulModelsDone       = false;
    readNetListDone           = false;
    readInstancesDone         = false;
    readNetsDone              = false;
    readSchematicDone         = false;
    readSizingDone            = false;
}

// COMPARISON FUNCTION //
bool ConnectionsSort(const Net::Connection* c1, const Net::Connection* c2) {
    return c1->getInstanceName() < c2->getInstanceName();
}

bool InstanceNameSort(const Instance* i1, const Instance* i2) {
    return i1->getName() < i2->getName();
}

bool NetNameSort(const Net* n1, const Net* n2) {
    return n1->getName() < n2->getName();
}

// USEFUL //
void Circuit::check_uppercase(string& str, vector<string>& compares, string message) {
    transform(str.begin(), str.end(), str.begin(), ::toupper);
    bool equal = false;
    for (size_t i = 0 ; i < compares.size() ; i++) {
        if (str == compares[i]) {
            equal = true;
        }
    }
    if (!equal) {
        throw OpenChamsException(message);
    }
}
    
void Circuit::check_lowercase(string& str, vector<string>& compares, string message) {
    transform(str.begin(), str.end(), str.begin(), ::tolower);
    bool equal = false;
    for (size_t i = 0 ; i < compares.size() ; i++) {
        if (str == compares[i]) {
            equal = true;
        }
    }
    if (!equal) {
        throw OpenChamsException(message);
    }
}
    
void Circuit::addSimulModel(unsigned id, SimulModel::Base base, SimulModel::Version version, std::string filePath) {
    SimulModel* sim = new SimulModel(id, base, version, filePath);
    map<unsigned, SimulModel*>::iterator it = _simulModels.find(id);
    if (it != _simulModels.end())
        throw OpenChamsException("[ERROR] Cannot define two SimulModels' models with the same ID.");
    _simulModels[id] = sim;
}

Name Circuit::readParameter(xmlNode* node, double& value) {
    xmlChar* paramNameC = xmlGetProp(node, (xmlChar*)"name");
    xmlChar* valueC     = xmlGetProp(node, (xmlChar*)"value");
    if (paramNameC && valueC) {
        Name name((const char*)paramNameC);
        value = ::getValue<double>(valueC);
        return name;
    } else {
        throw OpenChamsException("[ERROR] 'parameter' node must have 'name' and 'value' properties.");
    }
}
    
Name Circuit::readParameterEq(xmlNode* node, string& eqStr) {
    xmlChar* paramNameC = xmlGetProp(node, (xmlChar*)"name");
    xmlChar* equationC  = xmlGetProp(node, (xmlChar*)"equation");
    if (paramNameC && equationC) {
        Name name((const char*)paramNameC);
        eqStr = string ((const char*)equationC);
        return name;
    } else {
        throw OpenChamsException("[ERROR] 'parameterEq' node must have 'name' and 'equation' properties.");
    }
}
    
Name Circuit::readConnector(xmlNode* node) {
    xmlChar* connectorNameC = xmlGetProp(node, (xmlChar*)"name");
    if (connectorNameC) {
        Name name((const char*)connectorNameC);
        return name;
    } else {
        throw OpenChamsException("[ERROR] 'connector' node must have 'name' property.");
        //return Name("");
    }
}

// CIRCUIT //
void Circuit::readCircuitParameters(xmlNode* node) {
    if (readCircuitParametersDone) {
        cerr << "[WARNING] Only one 'parameters' node is allowed in circuit, others will be ignored." << endl;
        return;
    }
    if (node->type == XML_ELEMENT_NODE && node->children) {
        for (xmlNode* paramNode = node->children ; paramNode ; paramNode = paramNode->next) {
            if (paramNode->type == XML_ELEMENT_NODE) {
                if (xmlStrEqual(paramNode->name, (xmlChar*)"parameter")) {
                    double value = 0.0;
                    Name paramName = readParameter(paramNode, value);
                    if (paramName == Name("")) return; // error
                    addParameter(paramName, value);
                } else if (xmlStrEqual(paramNode->name, (xmlChar*)"parameterEq")) {
                    string eqStr = "";
                    Name paramName = readParameterEq(paramNode, eqStr);
                    if (paramName == Name("")) return; // error
        			addParameter(paramName, eqStr);
                } else {
                    cerr << "[WARNING] Only 'parameter' and 'parameterEq' nodes are allowed under 'parameters' node." << endl;
                    return;
                }
            }
        }
    }     
    readCircuitParametersDone = true;
}

void Circuit::readSimulModels(xmlNode* node) {
    if (readSimulModelsDone) {
        cerr << "[WARNING] Only one 'simulModels' node is allowed in circuit, others will be ignored." << endl;
        return;
    }
    if (node->type == XML_ELEMENT_NODE && node->children) {
        for (xmlNode* modelNode = node->children ; modelNode ; modelNode = modelNode->next) {
            if (modelNode->type == XML_ELEMENT_NODE) {
                if (xmlStrEqual(modelNode->name, (xmlChar*)"model")) {
                    xmlChar* mIdC       = xmlGetProp(modelNode, (xmlChar*)"id");
                    xmlChar* mBaseC     = xmlGetProp(modelNode, (xmlChar*)"base");
                    xmlChar* mVersionC  = xmlGetProp(modelNode, (xmlChar*)"version");
                    xmlChar* mFilePathC = xmlGetProp(modelNode, (xmlChar*)"filePath");
                    if (mIdC && mBaseC && mVersionC && mFilePathC) {
                        unsigned id = ::getValue<unsigned>(mIdC);
                        SimulModel::Base base = SimulModel::BSIM3V3;
                        string mBase((const char*)mBaseC);
                        string baseComp[3] = { "BSIM3V3", "BSIM4", "PSP" };
                        vector<string> baseComps(baseComp, baseComp+3);
                        check_uppercase(mBase, baseComps, "[ERROR] SimulModels models' base property must be \"BSIM3V3\", \"BSIM4\" or \"PSP\".");
                        if (mBase == "BSIM3V3") {
                            base = SimulModel::BSIM3V3;
                        } else if (mBase == "BSIM4") {
                            base = SimulModel::BSIM4;
                        } else if (mBase == "PSP") {
                            base = SimulModel::PSP;
                        } else {
                            throw OpenChamsException("[ERROR] SimulModels models' base property must be \"BSIM3V3\", \"BSIM4\" or \"PSP\" (check_uppercase should have filtered that).");
                            return;
                        }
                        SimulModel::Version version = SimulModel::UNDEFINED;
                        string mVersion((const char*)mVersionC);
                        string verComp[4] = { "UNDEFINED", "SVT", "HVT", "LVT" };
                        vector<string> verComps(verComp, verComp+4);
                        check_uppercase(mVersion, verComps, "[ERROR] SimulModels model's version property must be \"UNDEFINED\", \"SVT\", \"HVT\" or \"LVT\".");
                        if (mVersion == "UNDEFINED") {
                            version = SimulModel::UNDEFINED;
                        } else if (mVersion == "SVT") {
                            version = SimulModel::SVT;
                        } else if (mVersion == "HVT") {
                            version = SimulModel::HVT;
                        } else if (mVersion == "LVT") {
                            version = SimulModel::LVT;
                        } else {
                            throw OpenChamsException("[ERROR] SimulModels models' version property must be \"UNDEFINED\", \"SVT\", \"HVT\" or \"LVT\" (check_uppercase should have filtered that).");
                            return;
                        }
                        string filePath((const char*)mFilePathC);
                        addSimulModel(id, base, version, filePath);
                        // Ce simuModel DOIT être rataché au circuit !!!
                    } else {
                        throw OpenChamsException("[ERROR] 'model' node must have 'id', 'base', 'version' and 'filePath' properties.");
                    }
                } else {
                    cerr << "[WARNING] Only 'model' nodes are allowed under 'simulModels' node." << endl;
                    return;
                }
            }
        }
    }     
    readSimulModelsDone = true;
}

// NETLIST //
void Circuit::readNetList(xmlNode* node) {
    if (readNetListDone) {
        cerr << "[WARNING] Only one 'netlist' node is allowed in circuit, others will be ignored." << endl;
        return;
    }
    Netlist* netlist = new Netlist(this);
    xmlNode* child = node->children;
    for (xmlNode* node = child; node; node = node->next) {
        if (node->type == XML_ELEMENT_NODE) {
            if (xmlStrEqual(node->name, (xmlChar*)"instances")) {
                readInstances(node, netlist);
            } else if (xmlStrEqual(node->name, (xmlChar*)"nets")) {
                readNets(node, netlist);
            } else {
                cerr << "[WARNING] Only 'instances' and 'nets' nodes are allowed in 'netlist', others will be ignored." << endl;
            }
        }
    }
    readNetListDone = true;
    _netlist = netlist;
}

// INSTANCES //
void Circuit::readInstances(xmlNode* node, Netlist* netlist) {
    if (readInstancesDone) {
        cerr << "[WARNING] Only one 'instances' node is allowed in 'netlist', others will be ignored." << endl;
        return;
    }
    xmlNode* child = node->children;
    for (xmlNode* node = child; node; node = node->next) {
        if (node->type == XML_ELEMENT_NODE) {
            if (xmlStrEqual(node->name, (xmlChar*)"instance")) {
                readInstance(node, netlist);
            } else {
                cerr << "[WARNING] Only 'instance' nodes are allowed in 'instances', others will be ignored." << endl;
            }
        }
    }
    readInstancesDone = true;
}

Instance* Circuit::readInstance(xmlNode* node, Netlist* netlist) {
    xmlChar* iNameC  = xmlGetProp(node, (xmlChar*)"name");
    xmlChar* iModelC = xmlGetProp(node, (xmlChar*)"model");
    xmlChar* iMOSC   = xmlGetProp(node, (xmlChar*)"mostype");
    xmlChar* iSBCC   = xmlGetProp(node, (xmlChar*)"sourceBulkConnected");
    Instance* inst = NULL;
    if (iNameC && iModelC && iMOSC && iSBCC) {
        Name instanceName((const char*)iNameC);
        Name modelName((const char*)iModelC);
        string mosStr((const char*)iMOSC);
        string mosComp[2] = {"NMOS", "PMOS"};
        vector<string> mosComps (mosComp, mosComp+2);
        check_uppercase(mosStr, mosComps, "[ERROR] In 'instance', 'mostype' must be 'NMOS' or 'PMOS'.");
        string sourceBulkStr((const char*)iSBCC);
        string sbcComp[4] = {"true", "false", "on", "off"};
        vector<string> sbcComps(sbcComp, sbcComp+4);
        check_lowercase(sourceBulkStr, sbcComps, "[ERROR] In 'instance', 'sourceBulkConnected' must 'true', 'false', 'on' or 'off'.");
        bool sourceBulkConnected = ((sourceBulkStr == "true") || (sourceBulkStr == "on")) ? true : false;
        inst = netlist->addInstance(instanceName, modelName, Name(mosStr), sourceBulkConnected);
    } else {
        throw OpenChamsException("[ERROR] 'instance' node must have 'name', 'model', 'mostype' and 'sourceBulkConnected' properties.");
        //return inst;
    }

    xmlNode* child = node->children;
    for (xmlNode* node = child; node; node = node->next) {
        if (node->type == XML_ELEMENT_NODE) {
            if (xmlStrEqual(node->name, (xmlChar*)"connectors")) {
                readInstanceConnectors(node, inst);
            } else if (xmlStrEqual(node->name, (xmlChar*)"parameters")) {
                readInstanceParameters(node, inst);
            } else if (xmlStrEqual(node->name, (xmlChar*)"transistors")) {
                readInstanceTransistors(node, inst);
            } else {
                //cerr << "[WARNING] Only 'parameters' node is allowed in 'instance', others will be ignored." << endl;
                cerr << "[WARNING] Only 'conectors', 'transistors' and 'parameters' nodes are allowed in 'instance', others will be ignored." << endl;
            }
        }
    }
    return inst;
}
 
void Circuit::readInstanceConnectors(xmlNode* node, Instance* inst) {
	xmlNode* child = node->children;
    for (xmlNode* node = child; node; node = node->next) {
        if (node->type == XML_ELEMENT_NODE) {
            if (xmlStrEqual(node->name, (xmlChar*)"connector")) {
                Name connectorName = readConnector(node);
                if (connectorName == Name("")) return; // error
                inst->addConnector(connectorName);
            }
        }
    }
}
    
void Circuit::readInstanceParameters(xmlNode* node, Instance* inst) {
    xmlNode* child = node->children;
    for (xmlNode* node = child; node; node = node->next) {
        if (node->type == XML_ELEMENT_NODE) {
            if (xmlStrEqual(node->name, (xmlChar*)"parameter")) {
                double value = 0.0;
                Name paramName = readParameter(node, value);
                if (paramName == Name("")) return; // error
                inst->addParameter(paramName, value);
            } else if (xmlStrEqual(node->name, (xmlChar*)"parameterEq")) {
                string eqStr = "";
                Name paramName = readParameterEq(node, eqStr);
                if (paramName == Name("")) return; // error
                inst->addParameter(paramName, eqStr);
            } else {
                cerr << "[WARNING] Only 'parameter' and 'parameterEq' nodes are allowed under 'instance' node." << endl;
                return;
            }
        }
    }
}

void Circuit::readInstanceTransistors(xmlNode* node, Instance* inst) {
    xmlNode* child = node->children;
    for (xmlNode* node = child; node; node = node->next) {
        if (node->type == XML_ELEMENT_NODE) {
            if (xmlStrEqual(node->name, (xmlChar*)"transistor")) {
                readTransistor(node, inst);
            } else {
                cerr << "[WARNING] Only 'transistor' nodes are allowed in 'transistors', others will be ignored." << endl;
            }

        }
    }
}
    
void Circuit::readTransistor(xmlNode* node, Instance* inst) {
    xmlChar* tNameC  = xmlGetProp(node, (xmlChar*)"name");
    Transistor* trans = NULL;
    if (tNameC) {
        Name tName((const char*)tNameC);
        trans = inst->addTransistor(tName);
    } else {
        throw OpenChamsException("[ERROR] 'transistor' node must have 'name' property.");
        //return inst;
    }
    
    xmlNode* child = node->children;
    for (xmlNode* node = child; node; node = node->next) {
        if (node->type == XML_ELEMENT_NODE) {
            if (xmlStrEqual(node->name, (xmlChar*)"connection")) {
                readTransistorConnection(node, trans);
            } else if (xmlStrEqual(node->name, (xmlChar*)"parameters")) {
                cerr << "Transistor parameters NOT SUPPORTED YET" << endl;
            } else {
                cerr << "[WARNING] Only 'conectors', 'transistors' and 'parameters' nodes are allowed in 'instance', others will be ignored." << endl;
            }
        }
    }
}

void Circuit::readTransistorConnection(xmlNode* node, Transistor* trans) {
    xmlChar* gateC   = xmlGetProp(node, (xmlChar*)"gate");
    xmlChar* sourceC = xmlGetProp(node, (xmlChar*)"source");
    xmlChar* drainC  = xmlGetProp(node, (xmlChar*)"drain");
    xmlChar* bulkC   = xmlGetProp(node, (xmlChar*)"bulk");
    if (gateC && sourceC && drainC && bulkC) {
        Name gateN  ((const char*)gateC);
        Name sourceN((const char*)sourceC);
        Name drainN ((const char*)drainC);
        Name bulkN  ((const char*)bulkC);
        trans->setGate(gateN);
        trans->setSource(sourceN);
        trans->setDrain(drainN);
        trans->setBulk(bulkN);
    } else {
        throw OpenChamsException("[ERROR] 'connection' node must have 'gate', 'source', 'drain' and 'bulk' properties.");
    }

}
 
// NETS //
void Circuit::readNets(xmlNode* node, Netlist* netlist) {
    if (readNetsDone) {
        cerr << "[WARNING] Only one 'nets' node is allowed in 'netlist', others will be ignored." << endl;
        return;
    }
    xmlNode* child = node->children;
    for (xmlNode* node = child; node; node = node->next) {
        if (node->type == XML_ELEMENT_NODE) {
            if (xmlStrEqual(node->name, (xmlChar*)"net")) {
                readNet(node, netlist);
            } else {
                cerr << "[WARNING] Only 'net' nodes are allowed in 'nets', others will be ignored." << endl;
            }
        }
    }
    readNetsDone = true;
}
    
Net* Circuit::readNet(xmlNode* node, Netlist* netlist) {
    xmlChar* nNameC   = xmlGetProp(node, (xmlChar*)"name");
    xmlChar* nTypeC   = xmlGetProp(node, (xmlChar*)"type");
    xmlChar* nExternC = xmlGetProp(node, (xmlChar*)"isExternal");
    Net* net = NULL;
    if (nNameC && nTypeC && nExternC) {
        Name netName((const char*)nNameC);
        string typeStr((const char*)nTypeC);
        string typeComp[3] = {"power", "ground", "logical"};
        vector<string> typeComps(typeComp, typeComp+3);
        check_lowercase(typeStr, typeComps, "[ERROR] In 'net', 'type' must be 'power', 'ground' or 'logical'.");
        string externStr((const char*)nExternC);
        string extComp[4] = {"true", "false", "on", "off"};
        vector<string> extComps(extComp, extComp+4);
        check_lowercase(externStr, extComps, "[ERROR] In 'net', 'isExternal' must be 'true', 'false', 'on' or 'off'.");
        bool isExternal = ((externStr == "true") || (externStr == "on")) ? true : false;
        net = netlist->addNet(netName, Name(typeStr), isExternal);
    } else {
        throw OpenChamsException("[ERROR] 'net' node must have 'name', 'type' and 'isExternal' properties.");
        //return net;
    }
    
    xmlNode* child = node->children;
    for (xmlNode* node = child; node; node = node->next) {
        if (node->type == XML_ELEMENT_NODE) {
            if (xmlStrEqual(node->name, (xmlChar*)"connector")) {
                readNetConnector(node, net);
            } else {
                cerr << "[WARNING] Only 'connector' nodes are allowed in 'net', others will be ignored." << endl;
                return NULL;
            }
        }
    }
    return net;
}
    
void Circuit::readNetConnector(xmlNode* node, Net* net) {
    xmlChar* instanceNameC  = xmlGetProp(node, (xmlChar*)"instance");
    xmlChar* connectorNameC = xmlGetProp(node, (xmlChar*)"name");
    if (instanceNameC && connectorNameC) {
        Name iName((const char*)instanceNameC);
        Name cName((const char*)connectorNameC);
        Instance* inst = net->getNetlist()->getInstance(iName);
        if (!inst) {
            string error("[ERROR] no instance named \"");
            error += iName.getString();
            error += "\" in connector of net \"";
            error += net->getName().getString();
            error += "\".";
            throw OpenChamsException(error);
            //return;
        }
        //inst->connect(cName, net->getName());
        net->connectTo(iName, cName);
    } else {
        throw OpenChamsException("[ERROR] 'connector' node must have 'instance' and 'name' properties (for net).");
    }
}

// SCHEMATIC //
void Circuit::readSchematic(xmlNode* node) {
    if (readSchematicDone) {
        cerr << "[WARNING] Only one 'schematic' node is allowed in circuit, others will be ignored." << endl;
        return;
    }
    xmlChar* zoomC = xmlGetProp(node, (xmlChar*)"zoom");
    double zoom = 1.0;
    if (zoomC) {
        zoom = ::getValue<double>(zoomC);
    } else {
        throw OpenChamsException("[ERROR] 'schematic' node must have 'zoom' property.");
    }

    Schematic* schematic = new Schematic(this, zoom);
    xmlNode* child = node->children;
    for (xmlNode* node = child; node; node = node->next) {
        if (node->type == XML_ELEMENT_NODE) {
            if (xmlStrEqual(node->name, (xmlChar*)"instance")) {
                readInstanceSchematic(node, schematic);
            } else {
                cerr << "[WARNING] Only 'instance' nodes are allowed in 'schematic', others will be ignored." << endl;
            }
        }
    }
    readSchematicDone = true;
    _schematic = schematic;
}
    
void Circuit::readInstanceSchematic(xmlNode* node, Schematic* schematic) {
    xmlChar* nameC = xmlGetProp(node, (xmlChar*)"name");
    xmlChar* xC    = xmlGetProp(node, (xmlChar*)"x");
    xmlChar* yC    = xmlGetProp(node, (xmlChar*)"y");
    xmlChar* symC  = xmlGetProp(node, (xmlChar*)"sym");
    if (nameC && xC && yC && symC) {
        Name   iName((const char*)nameC);
        double x = ::getValue<double>(xC);
        double y = ::getValue<double>(yC);
        string symStr((const char*)symC);
        string symComp[8] = {"ID", "R1", "R2", "R3", "MX", "XR", "MY", "YR"};
    	vector<string> symComps (symComp, symComp+8);
        check_uppercase(symStr, symComps, "[ERROR] In 'schematic/instance', 'sym' must be 'ID', 'R1', 'R2', 'R3', 'MX', 'XR', 'MY' or 'YR'.");
        schematic->addInstance(iName, x, y, Name(symStr));
    } else {
        throw OpenChamsException("[ERROR] 'instance' node in 'schematic' must have 'name', 'x', 'y' and 'sym' properties.");
    }
}

// SIZING //
void Circuit::readSizing(xmlNode* node) {
    if (readSizingDone) {
        cerr << "[WARNING] Only one 'sizing' node is allowed in circuit, others will be ignored." << endl;
        return;
    }
    
    Sizing* sizing = new Sizing(this);
    xmlNode* child = node->children;
    for (xmlNode* node = child; node; node = node->next) {
        if (node->type == XML_ELEMENT_NODE) {
            if (xmlStrEqual(node->name, (xmlChar*)"instance")) {
                readInstanceSizing(node, sizing);
            } else if (xmlStrEqual(node->name, (xmlChar*)"equations")) {
                readEquations(node, sizing);
            } else {
                cerr << "[WARNING] Only 'instance' and 'equations' nodes are allowed in 'sizing', others will be ignored." << endl;
            }
        }
    }
    readSizingDone = true;
    _sizing = sizing;
}
    
void Circuit::readInstanceSizing(xmlNode* node, Sizing* sizing) {
    xmlChar* nameC     = xmlGetProp(node, (xmlChar*)"name");
    xmlChar* operatorC = xmlGetProp(node, (xmlChar*)"operator");
    xmlChar* simulModC = xmlGetProp(node, (xmlChar*)"simulModel");
    xmlChar* orderC    = xmlGetProp(node, (xmlChar*)"callOrder");
    if (nameC && operatorC && simulModC && orderC) {
        Name     iName   ((const char*)nameC);
        Name     opName  ((const char*)operatorC);
        Name     simulMod((const char*)simulModC);
        unsigned callOrder = ::getValue<unsigned>(orderC);
        Operator* op = sizing->addOperator(iName, opName, simulMod, callOrder);
        xmlNode* child = node->children;
        for (xmlNode* node = child; node; node = node->next) {
            if (node->type == XML_ELEMENT_NODE) {
                if (xmlStrEqual(node->name, (xmlChar*)"constraint")) {
                    readConstraint(node, op);
                } else {
                    cerr << "[WARNING] Only 'constraint' nodes are allowed in 'instance' of 'sizing', others will be ignored." << endl;
                }
            }
        }
        
    } else {
        throw OpenChamsException("[ERROR] 'instance' node in 'sizing' must have 'name', 'operator', 'simulModel' and 'callOrder' properties.");
    }
}
    
void Circuit::readConstraint(xmlNode* node, Operator* op) {
    // attributes of constraint may be :
    //    param ref refParam [factor]
    //    param refEquation [factor]
    xmlChar* paramC    = xmlGetProp(node, (xmlChar*)"param");
    xmlChar* refC      = xmlGetProp(node, (xmlChar*)"ref");
    xmlChar* refParamC = xmlGetProp(node, (xmlChar*)"refParam");
    xmlChar* refEqC    = xmlGetProp(node, (xmlChar*)"refEquation");
    xmlChar* factorC   = xmlGetProp(node, (xmlChar*)"factor");
    if (paramC && refC && refParamC) {
        Name param    ((const char*)paramC);
        Name ref      ((const char*)refC);
        Name refParam ((const char*)refParamC);
        double factor = 1.0;
        if (factorC) {
            factor = ::getValue<double>(factorC);
        }
        op->addConstraint(param, ref, refParam, factor);
    } else if (paramC && refEqC) {
        Name param ((const char*)paramC);
        Name refEq ((const char*)refEqC);
        double factor = 1.0;
        if (factorC) {
            factor = ::getValue<double>(factorC);
        }
        op->addConstraint(param, refEq, factor);
    } else {
        throw OpenChamsException("[ERROR] 'constraint' node must have 'param, ref, refParam, [factor]' or 'param, refEq, [factor]' properties.");
    }
}
    
void Circuit::readEquations(xmlNode* node, Sizing* sizing) {
    xmlNode* child = node->children;
    for (xmlNode* node = child; node; node = node->next) {
        if (node->type == XML_ELEMENT_NODE) {
            if (xmlStrEqual(node->name, (xmlChar*)"eq")) {
                readEquation(node, sizing);
            } else {
                throw OpenChamsException("[ERROR] Only 'eq' nodes are allowed in 'equations'.");
            }
        }
    }
}
    
void Circuit::readEquation(xmlNode* node, Sizing* sizing) {
    xmlChar* nameC     = xmlGetProp(node, (xmlChar*)"name");
    xmlChar* equationC = xmlGetProp(node, (xmlChar*)"equation");
    if (nameC && equationC) {
        Name   eName ((const char*)nameC);
        string eqStr ((const char*)equationC);
        sizing->addEquation(eName, eqStr);
    } else {
        throw OpenChamsException("[ERROR] 'eq' node in 'equations' must have 'name' and 'equation' properties.");
    }
}
    
Circuit* Circuit::readFromFile(const string filePath) {
    LIBXML_TEST_VERSION;
    Circuit* cir = NULL;
    xmlDoc* doc = xmlReadFile(filePath.c_str(), NULL, 0);
    if (doc == NULL) {
        string error ("[ERROR] Failed to parse: ");
        error += filePath;
        throw OpenChamsException(error);
        //return NULL;
    }
    xmlNode* rootElement = xmlDocGetRootElement(doc);
    if (rootElement->type == XML_ELEMENT_NODE && xmlStrEqual(rootElement->name, (xmlChar*)"circuit")) {
        xmlChar* circuitNameC = xmlGetProp(rootElement, (xmlChar*)"name");
        xmlChar* technoNameC  = xmlGetProp(rootElement, (xmlChar*)"techno");
        
        if (circuitNameC && technoNameC) {
            Name circuitName ((const char*)circuitNameC);
            Name technoName  ((const char*)technoNameC);
            cir = new Circuit(circuitName, technoName);
        } else {
            throw OpenChamsException("[ERROR] 'circuit' node must have 'name' and 'techno' properties.");
            return NULL;
        }

        
        xmlNode* child = rootElement->children;
        for (xmlNode* node = child; node; node = node->next) {
            if (node->type == XML_ELEMENT_NODE) {
                if (xmlStrEqual(node->name, (xmlChar*)"parameters")) {
                    cir->readCircuitParameters(node);
                }
                else if (xmlStrEqual(node->name, (xmlChar*)"simulModels")) {
                    cir->readSimulModels(node);
                }
                else if (xmlStrEqual(node->name, (xmlChar*)"netlist")) {
                    cir->readNetList(node);
                }
                else if (xmlStrEqual(node->name, (xmlChar*)"schematic")) {
                    cir->readSchematic(node);
                }
                else if (xmlStrEqual(node->name, (xmlChar*)"sizing")) {
                    cir->readSizing(node);
                }
                else {
                    string error("[ERROR] Unknown section ");
                    error += string((const char*)node->name);
                    error += " in circuit description.";
                    throw OpenChamsException(error);
                    return NULL;
                }
            }
        }
    }
    if (!readNetListDone) {
        throw OpenChamsException("[ERROR] no <netlist> section was found in parsed file !");
    }
    return cir;
}
    
Netlist* Circuit::createNetlist() {
    if (_netlist)
        throw OpenChamsException("[ERROR] Cannot create two netlists in one circuit.");

    _netlist = new Netlist(this);
    if (!_netlist)
        throw OpenChamsException("[ERROR] Cannot create netlist.");

    return _netlist;
}

Schematic* Circuit::createSchematic(double zoom) {
    if (_schematic)
        throw OpenChamsException("[ERROR] Cannot create two scheamtics in one circuit.");

    _schematic = new Schematic(this, zoom);
    if (!_schematic)
        throw OpenChamsException("[ERROR] Cannot create schematic.");

    return _schematic;
}

Sizing* Circuit::createSizing() {
    if (_sizing)
        throw OpenChamsException("[ERROR] Cannot create two sizings in one circuit.");

    _sizing = new Sizing(this);
    if (!_sizing)
        throw OpenChamsException("[ERROR] Cannot create sizing.");

    return _sizing;
}

bool Circuit::writeToFile(string filePath) {
    ofstream file;
    file.open(filePath.c_str());
    if (!file.is_open()) {
        string error("[ERROR] Cannot open file ");
        error += filePath;
        error += " for writting.";
        throw OpenChamsException(error);
    }
    // checks before do anything
    if (!_netlist) {
        //cerr << "no netlist" << endl; cerr.flush();
        throw OpenChamsException("[ERROR] Cannot writeToFile since no netlist is defined !");
        //return false;
    }    
    if (_netlist->hasNoInstances()) {
        //cerr << "no instances" << endl; cerr.flush();
        throw OpenChamsException("[ERROR] Cannot writeToFile since no instance is defined in netlist !");
        //return false;
    }
    if (_netlist->hasNoNets()) {
        //cerr << "no nets" << endl; cerr.flush();
        throw OpenChamsException("[ERROR] Cannot writeToFile since no net is defined in netlist !");
        //return false;
    }
    
    file << "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" << endl
         << "<circuit name=\"" << _name.getString() << "\" techno=\"" << _techno.getString() << "\">" << endl;
    if (!_params.isEmpty()) {
        file << "  <parameters>" << endl;
    	for (map<Name, double>::const_iterator it = _params.getValues().begin() ; it != _params.getValues().end() ; ++it) {
        	file << "    <parameter name=\"" << (*it).first.getString() << "\" value=\"" << (*it).second << "\"/>" << endl;
    	}
        for (map<Name, string>::const_iterator it = _params.getEqValues().begin() ; it != _params.getEqValues().end() ; ++it) {
            file << "    <parameterEq name=\"" << (*it).first.getString() << "\" equation=\"" << (*it).second << "\"/>" << endl;
        }
        file << "  </parameters>" << endl;
    }
    file << "  <netlist>" << endl
         << "    <instances>" << endl;
    vector<Instance*> instances = _netlist->getInstances();
    sort(instances.begin(), instances.end(), InstanceNameSort); // sort based on instances' names
    for (vector<Instance*>::iterator it = instances.begin() ; it != instances.end() ; ++it) {
        Instance* inst = (*it);
        if (inst->hasNoConnectors()) {
            string error("[ERROR] Cannot writeToFile since instance (");
            error += inst->getName().getString();
            error += ") has no connectors !";
            throw OpenChamsException(error);
            //return false;
        }
        if (inst->hasNoTransistors()) {
            string error("[ERROR] Cannot writeToFile since instance (");
            error += inst->getName().getString();
            error += ") has no transistors !";
            throw OpenChamsException(error);
        }
        string sourceBulkStr = (inst->isSourceBulkConnected()) ? "True" : "False";
        file << "      <instance name=\"" << inst->getName().getString() << "\" model=\"" << inst->getModel().getString() << "\" mostype=\"" << inst->getMosType().getString() << "\" sourceBulkConnected=\"" << sourceBulkStr << "\">" << endl;
        file << "        <connectors>" << endl;
        for (map<Name, Net*>::const_iterator it = inst->getConnectors().begin() ; it != inst->getConnectors().end() ; ++it) {
            file << "          <connector name=\"" << (*it).first.getString() << "\"/>" << endl;
        }
        file << "        </connectors>" << endl
             << "        <transistors>" << endl;
        for (vector<Transistor*>::const_iterator it = inst->getTransistors().begin() ; it != inst->getTransistors().end() ; ++it ) {
            Transistor* tr = (*it);
            file << "          <transistor name=\"" << tr->getName().getString() << "\">" << endl
                 << "            <connection gate=\"" << tr->getGate().getString() << "\" source=\"" << tr->getSource().getString() << "\" drain=\"" << tr->getDrain().getString() << "\" bulk=\"" << tr->getBulk().getString() << "\"/>" << endl
                 << "          </transistor>" << endl;
        }
        file << "        </transistors>" << endl;
        if (!inst->getParameters().isEmpty()) {
            Parameters params = inst->getParameters();
            file << "        <parameters>" << endl;
            for (map<Name, double>::const_iterator it = params.getValues().begin() ; it != params.getValues().end() ; ++it) {
                file << "          <parameter name=\"" << (*it).first.getString() << "\" value=\"" << (*it).second << "\"/>" << endl;
            }
            file << "        </parameters>" << endl;
        }
		file << "      </instance>" << endl;
    }
    file << "    </instances>" << endl
         << "    <nets>" << endl;
    vector<Net*> nets = _netlist->getNets();
    sort(nets.begin(), nets.end(), NetNameSort); // sort based on nets' names
    for (vector<Net*>::iterator it = nets.begin() ; it != nets.end() ; ++it) {
        Net* net = (*it);
        if (net->hasNoConnections()) {
            string error("[ERROR] Cannot writeToFile since net (");
            error += net->getName().getString();
            error += ") has no connectors !";
            throw OpenChamsException(error);
            //return false;
        }
        string externStr = (net->isExternal()) ? "True" : "False";
        file << "      <net name=\"" << net->getName().getString() << "\" type=\"" << net->getType().getString() << "\" isExternal=\"" << externStr << "\">" << endl;
        vector<Net::Connection*> connections = net->getConnections();
        sort(connections.begin(), connections.end(), ConnectionsSort);
        for (vector<Net::Connection*>::iterator it = connections.begin() ; it != connections.end() ; ++it) {
            file << "        <connector instance=\"" << (*it)->getInstanceName().getString() << "\" name=\"" << (*it)->getConnectorName().getString() << "\"/>" << endl;
        }
		file << "      </net>" << endl;
    }
    file << "    </nets>" << endl;
    file << "  </netlist>" << endl;
    if (_schematic && !_schematic->hasNoInstances()) {
        file << "  <schematic zoom=\"" << _schematic->getZoom() << "\">" << endl;
        for (map<Name, Schematic::Infos*>::const_iterator it = _schematic->getInstances().begin() ; it != _schematic->getInstances().end(); ++it ) {
            Schematic::Infos* infos = (*it).second;
            file << "    <instance name=\"" << ((*it).first).getString() << "\" x=\"" << infos->getX() << "\" y=\"" << infos->getY() << "\" sym=\"" << infos->getSymmetry().getString() << "\"/>" << endl;
        }
        file << "  </schematic>" << endl;
    }
    if (_sizing && !_sizing->hasNoOperators()) {
        file << "  <sizing>" << endl;
        for (map<Name, Operator*>::const_iterator it = _sizing->getOperators().begin() ; it != _sizing->getOperators().end() ; ++it) {
            Operator* op = (*it).second;
            file << "    <instance name=\"" << ((*it).first).getString() << "\" operator=\"" << op->getName().getString() << "\" simulModel=\"" << op->getSimulModel().getString() << "\" callOrder=\"" << op->getCallOrder() << "\">" << endl;
            if (!op->hasNoConstraints()) {
                for (map<Name, Operator::Constraint*>::const_iterator cit = op->getConstraints().begin() ; cit != op->getConstraints().end() ; ++cit) {
                    Operator::Constraint* cn = (*cit).second;
                    Name ref = cn->getRef();
                    if (ref == Name("")) {
                        file << "      <constraint param=\"" << ((*cit).first).getString() << "\" refEquation=\"" << cn->getRefParam().getString() << "\" factor=\"" << cn->getFactor() << "\"/>" << endl;
                    } else {
                        file << "      <constraint param=\"" << ((*cit).first).getString() << "\" ref=\"" << cn->getRef().getString() << "\" refParam=\"" << cn->getRefParam().getString() << "\" factor=\"" << cn->getFactor() << "\"/>" << endl;
                    }
                }
            }
            file << "    </instance>" << endl;
        }
        if (!_sizing->hasNoEquations()) {
            file << "    <equations>" << endl;
            for (map<Name, string>::const_iterator it = _sizing->getEquations().begin() ; it != _sizing->getEquations().end() ; ++it) {
                file << "      <eq name=\"" << ((*it).first).getString() << "\" equation=\"" << (*it).second << "\"/>" << endl;
            }
            file << "    </equations>" << endl;
        }
        file << "  </sizing>" << endl;
    }
    file << "</circuit>" << endl;
    file.close();
	return true;        
}

}

