/*******************************************************************************
 * Copyright (c) 2015-2016 ONERA.
 * Ce logiciel est la propriété de l’ONERA (the French Aerospace Lab).
 * Tous droits réservés.
 *
 * Ce programme et les éléments qui l'accompagnent sont mis à disposition
 * aux conditions définies par le contrat de licence logiciel CeCILL-C soumise
 * au droit français et respectant les principes de diffusion des logiciels libres.
 * Vous pouvez utiliser, modifier et/ou redistribuer ce programme
 * sous les conditions de la licence CeCILL-C (http://www.cecill.info).

 * This software is property of ONERA (the French Aerospace Lab).
 * All rights reserved.
 *
 * This program and the accompanying materials are made available under
 * the terms of the CeCILL-C license under French law and
 * abiding by the rules of distribution of free software.
 * You can  use, modify and/ or redistribute the software under the terms of
 * the CeCILL-C license (http://www.cecill.info).
 *
 * Contributeurs/contributors:
 *     Guillaume Infantes (ONERA - Centre de Toulouse) - initial API and implementation
 *
 *******************************************************************************/

#include "AltaricaInitInfoXML.hh"
#include "AltaricaModel.hh"
#include "MemberAccess.hh"
#include "EventLaw.hh"
#include "Event.hh"
#include "Node.hh"
#include "Variable.hh"
#include <algorithm>
#include <string>
#include "domains/Domains.hh"
#include "values/ValueBool.hh"
#include "values/ValueInt.hh"
#include "values/ValueSymbol.hh"
#include "values/ValueReal.hh"
#include "expressions/ExpressionREAL.hh"



#include <iostream>


using namespace epoch::altarica;

AltaricaInitInfoXML::AltaricaInitInfoXML(std::string initFile, AltaricaModel *am) : AltaricaInitInfo(am) {
    amodel = am;
    parser.set_substitute_entities();
    parser.parse_file(initFile);
    if (parser) {
	const xmlpp::Node* pNode = parser.get_document()->get_root_node();
	processRoot(pNode);
    }
    overrideLaws();
}




void AltaricaInitInfoXML::processRoot(const xmlpp::Node * root) {
    xmlpp::Node::NodeList list = root->get_children();
      for(xmlpp::Node::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter) {
	  const xmlpp::TextNode* nodeText = dynamic_cast<const xmlpp::TextNode*>(*iter);
	  if(nodeText && nodeText->is_white_space()) 
	      continue;
	  processNode(*iter);
      }
}

void AltaricaInitInfoXML::processEvent(const xmlpp::Node * xmlnode, std::vector<Node *>  *altNodeVector) {
    const xmlpp::Element* nodeLaw = dynamic_cast<const xmlpp::Element*>(xmlnode);  
    const xmlpp::Element::AttributeList& attributes = nodeLaw->get_attributes();
    std::string eventName;
    std::string lawName;
    std::string param;
    bool expLaw = true;
    bool noParam = false;
    double paramdouble;
    for(xmlpp::Element::AttributeList::const_iterator iter = attributes.begin(); iter != attributes.end(); ++iter) {
	const xmlpp::Attribute* attribute = *iter;
	std::string attname = attribute->get_name();
	std::string attval = attribute->get_value();
	if (attname.compare("name") == 0) 
	    eventName = attval;
	else if (attname.compare("law") == 0) 
	    lawName = attval;
	else if (attname.compare("param") == 0)
	    param = attval;
    }
    
    if (lawName.compare("dirac") == 0)
	expLaw = false;
    if (param.empty())
	noParam = true;
    else {
	std::istringstream stm;
	stm.str(param);
	stm >>paramdouble;
    } 
    
    MemberAccess * tempMA = new MemberAccess(eventName);

    for (std::vector<Node*>::iterator altNodeIt = altNodeVector->begin(); altNodeIt!= altNodeVector->end(); ++altNodeIt) {

	Event * oldEvent = (*altNodeIt)->findEventByName(tempMA);
	Expression *oldLaw = NULL;
	if (expLaw && noParam) {
	    oldLaw = oldEvent->getLaw()->getParam();
	}
	
	EventLaw * newLaw;
	if (expLaw) {
	    if (oldLaw != NULL)
		newLaw = new EventLaw(ALTARICA_EXP,oldLaw);
	    else
		newLaw = new EventLaw(ALTARICA_EXP,new ExpressionREAL(paramdouble));
	} else {
	    newLaw = new EventLaw(ALTARICA_DIRAC_0,0);
	}
	
	initLaws[oldEvent] = newLaw;
    }
}

void AltaricaInitInfoXML::processValue(const xmlpp::Node * xmlnode, std::vector<Node *>* altNodeVector) {
    const xmlpp::Element* nodeValue = dynamic_cast<const xmlpp::Element*>(xmlnode);
    const xmlpp::Element::AttributeList& attributes = nodeValue->get_attributes();
    std::string varName;
    std::string valName;
    for(xmlpp::Element::AttributeList::const_iterator iter = attributes.begin(); iter != attributes.end(); ++iter) {
	const xmlpp::Attribute* attribute = *iter;
	std::string attname = attribute->get_name();
	std::string attval = attribute->get_value();
	if (attname.compare("var") == 0) 
	    varName = attval;
	else if (attname.compare("val") == 0) 
	    valName = attval;
    }

    for (std::vector<Node*>::iterator altNodeIt = altNodeVector->begin(); altNodeIt!= altNodeVector->end(); ++altNodeIt) {
	Value * v;
	Variable * var = (*altNodeIt)->findStateVariableByName(varName);
	if (var == NULL)
	    BOOST_THROW_EXCEPTION(std::runtime_error(std::string("cannot find variable to init")));
	unsigned int varIndex = var->getIndex();
	// try to guess type from domain
	Domain * d = var->getDomain();
	if (d!= NULL) {
	    PredefinedDomain *pd = dynamic_cast<PredefinedDomain*>(d);
	    EnumDomain *ed = dynamic_cast<EnumDomain*>(d);
	    RangeDomain *rd = dynamic_cast<RangeDomain*>(d);
	    ArrayDomain *ad = dynamic_cast<ArrayDomain*>(d);
	    StructuredDomain *sd = dynamic_cast<StructuredDomain*>(d);
	    RealDomain *reald = dynamic_cast<RealDomain*>(d);

	    if (pd) {
		if (pd->isBooleanDomain()) {		    
		    // scan true of false from valName
		    std::transform(valName.begin(), valName.end(),valName.begin(), ::toupper);
		    if (valName.compare("TRUE") == 0)
			v = new ValueBool(true, false);
		    else
			v = new ValueBool(false, true);			
		} else {
		    //scan int from valName
		    std::istringstream stm;
		    int i;
		    stm.str(valName);
		    stm >>i;		    
		    v = new ValueInt(i);
		}		
	    } else if (ed) {
		v = new ValueSymbol(valName);
	    } else if (rd) {
		    //scan int from valName
		    std::istringstream stm;
		    int i;
		    stm.str(valName);
		    stm >>i;		    
		    v = new ValueInt(i);
	    } else if (reald) {
		    std::istringstream stm;
		    double i;
		    stm.str(valName);
		    stm >>i;		    
		    v = new ValueReal(i);		
	    } else if (ad) {
		BOOST_THROW_EXCEPTION(std::runtime_error(std::string("initing array domains not yet implemented")));
	    } else if (sd) {
		BOOST_THROW_EXCEPTION(std::runtime_error(std::string("initing structured domains not yet implemented")));
	    }
	} else {
	    //identifiers starting with digit are forbidden...
	    std::istringstream stm;
	    int i = INT_MIN;
	    stm.str(valName);
	    stm >>i;
	    if (i != INT_MIN) {
		v = new ValueInt(i);
	    } else {
		std::string temp(valName);
		  std::transform(temp.begin(), temp.end(),temp.begin(), ::toupper);
		  if (temp.compare("TRUE") == 0)
		      v = new ValueBool(true, false);
		  else if (temp.compare("FALSE") == 0)
		      v = new ValueBool(false, true);
		  else
		      v = new ValueSymbol(valName);
	    }	    
	}		
	initValues[varIndex] = v;	
    }
    
}



void AltaricaInitInfoXML::processNode(const xmlpp::Node * xmlnode) {


    const Glib::ustring nodename = xmlnode->get_name();

    bool nodeisclass = false;
    std::vector<Node *> *altNodeVector;

    

    xmlpp::Node* nodeName = *((xmlnode->get_children(Glib::ustring("name"))).begin());

    
    xmlpp::Node* nodeNameSon = *(nodeName->get_children().begin());
    std::string altaricaNodeName = (dynamic_cast<const xmlpp::TextNode*>(nodeNameSon))->get_content();

    xmlpp::Node::NodeList nodeTypeList = xmlnode->get_children(Glib::ustring("type"));
    if (nodeTypeList.size() != 0) {
	xmlpp::Node* nodeType = *(nodeTypeList.begin());
	xmlpp::Node::NodeList nodeTypeListSons = nodeType->get_children();
	xmlpp::Node* nodeTypeSon = *(nodeTypeListSons.begin());
	const xmlpp::TextNode * texttype = dynamic_cast<const xmlpp::TextNode*>(nodeTypeSon);
	std::string altaricaNodeType = texttype->get_content();
	if (altaricaNodeType.compare("class") == 0)
	    nodeisclass = true;
    }
    
    
    if (nodeisclass) {
	altNodeVector = amodel->findNodesByType(altaricaNodeName);
	if (altNodeVector == NULL)
	    BOOST_THROW_EXCEPTION(std::runtime_error(std::string("cannot find node by type")));	    
    } else {
	Node * n = amodel->findNodeByFullName(altaricaNodeName);
	if (n==NULL)
	    BOOST_THROW_EXCEPTION(std::runtime_error(std::string("cannot find node")));
	altNodeVector = new std::vector<Node*>();
	altNodeVector->push_back(n);
    }

    xmlpp::Node::NodeList lawnode = xmlnode->get_children(Glib::ustring("laws"));
    xmlpp::Node::NodeList eventlist = (*(lawnode.begin()))->get_children(Glib::ustring("event"));
    for(xmlpp::Node::NodeList::iterator iter = eventlist.begin(); iter != eventlist.end(); ++iter) {
	processEvent(*iter, altNodeVector);
    }

    
    xmlpp::Node::NodeList valuesnode = xmlnode->get_children(Glib::ustring("values"));
    xmlpp::Node::NodeList valueList = (*(valuesnode.begin()))->get_children(Glib::ustring("value"));
    for(xmlpp::Node::NodeList::iterator iter = valueList.begin(); iter != valueList.end(); ++iter) {
	processValue(*iter, altNodeVector);
    }
    delete altNodeVector;
    
}



