/*******************************************************************************
 * Copyright (c) 2015-2017 ONERA and IRT AESE (IRT Saint Exupéry).
 * Ce logiciel est la propriété de l’ONERA (the French Aerospace Lab) et de
 * l'IRT AESE (IRT Saint Exupéry).
 * 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) and of
 * the IRT AESE (IRT Saint Exupéry).
 * 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
 *     Alexandre Albore (IRT Saint-Exupéry) - Altarica/Fiacre translation
 *
 *******************************************************************************/

#ifndef ALTA_NODE_H_
#define ALTA_NODE_H_

#include <map>
#include <set>
#include <string>
#include <vector>

namespace epoch {
  namespace altarica {

    class Parameter;
    class Variable;
    class Event;
    class EventLaw;
    class Transition;
    class Expression;
    class Assignment;
    class Synchronisation;
    class Value;
    class Assertion;
    class MemberAccess;
    class AltaricaModel;

    class Node {

    public :
      struct ltstr {
        bool operator() (const std::string s1, const std::string s2) const {
          return s1.compare(s2) < 0;
        }
      };


    protected:

      //!
      // std::string * name_;
      Node * parent_;
      MemberAccess*  ma_;
      MemberAccess* typeName_;
      std::vector<std::string> attributes_;
      std::vector<Parameter*> params_;

      //      std::vector<Variable*> vars_;
      std::map<std::string,Variable*, ltstr> stateVars_;
      std::map<std::string,Variable*, ltstr> flowVars_;
      mutable std::map<std::string,Variable*, ltstr> stateVarsCache_;
      mutable std::map<std::string,Variable*, ltstr> flowVarsCache_;
      mutable std::set<std::string> symbolCache_;

      //      std::vector<Event*> events_;
      std::map<std::string, Event*, ltstr> events_;

      std::vector<Transition*> trans_;
      std::vector<Assertion*> asserts_;
      //std::vector<Expression<OpenValue>*> asserts_;

      std::map<std::string, Node*, ltstr> subNodes_;
      //      std::vector<Node*>   subNodes_;
      std::vector<Synchronisation*> syncs_;
      std::vector<Assignment*> inits_;
      std::vector<Assignment*> paramSets_;
      std::vector<std::string> externals_;

      std::vector<std::vector<Event*>* > *events_by_prio_;

      //      bool notInEv(std::set<Event*>const *up, std::vector<Event*>  * ev);
      bool notInEv(std::set<Event*>const *up, std::map<std::string,Event*,ltstr>  * ev);
      void buildEventsByPrio();
      void matchEventsInTransitions();
      void matchEventsInSynchros();
      //      void matchVariablesInInits();
      void matchVariablesInAsserts(const AltaricaModel *am);

      unsigned int index_;


    public:

      //!
      Node();
      ~Node();

      // void setName(std::string * name);
      void setMA(std::string  name, std::vector<int> * idxs);
      void setMA(std::string  name);
      void setMA(MemberAccess* ma);

      void setTypeName(MemberAccess *ma) {typeName_ = ma;}
      const MemberAccess * getTypeName() const {return typeName_;}

      void setParent(Node *n);
      const Node * getParent() const;
      std::vector<const Node*>* getPath() const;
      std::string getFullName() const;

      void addAttribute(const std::string a);
      void addAttributes(const std::vector<std::string>* va);

      Node* clone() const;

      void addParam(Parameter* p);
      void addParams(std::vector<Parameter*>* p);
      void addVariable(Variable* v);
      void addVariables(std::vector<Variable*>* v);
      void addEvent(Event* e);
      void addEvents(std::vector<Event*>* e);
      void addTransition(Transition* t);
      void addTransitions(std::vector<Transition*>* t);
      void addAssertion(Assertion *e);
      void addAssertions(std::vector<Assertion*>  *e);
      void addSubNode(Node* n);
      void addSubNodes(std::vector<Node*>* n);
      // void addSubNode(Node* n, std::vector<int> * v);
      // void addSubNodes(std::vector<Node*>* n, std::vector<std::vector<int>* > * v);
      void addSync(Synchronisation* s);
      void addSyncs(std::vector<Synchronisation*> *s);
      void addInit(Assignment* i);
      void addInits(std::vector<Assignment*>* i);
      void addParamsSet(Assignment* p);
      void addParamsSets(std::vector<Assignment*>* p);
      void addExternal(std::string e);
      void addExternals(std::vector<std::string>* e);
      void addEventLaw(EventLaw* e);
      void addEventLaws(std::vector<EventLaw*>* e);


      //      const std::vector<Variable*> *getVariables();
      const std::map<std::string, Variable*, ltstr> * getStateVariables() const;
      const std::map<std::string, Variable*, ltstr> * getFlowVariables() const;
      const std::vector<Transition*> *getTransitions() const;
      //      const std::vector<Node*> *getSubNodes();
      const std::map<std::string, Node*, ltstr> *getSubNodes() const;
      //      const std::vector<Event*> *getEvents();
      const std::map<std::string, Event*, ltstr> *getEvents() const;
      const std::vector<Synchronisation*> *getSyncs() const;
      const std::vector<Assertion*> *getAsserts() const;
      const MemberAccess * getMA() const;


      bool hasName(std::string name);

      Event* findEventByName(const MemberAccess* eName) const;
      Variable * findStateVariableByName(const MemberAccess * eName) const;
      Variable * findStateVariableByName(const std::string& s) const;
      Variable * findFlowVariableByName(const MemberAccess * eName) const;
      Variable * findFlowVariableByName(const std::string& s) const;
      Variable * findVariableByName(const MemberAccess * eName) const;
      Variable * findVariableByName(const std::string& s) const;

      std::string toString(bool withTypes=false) const;
      std::string toFiacre() const ;
      std::string printTrans() const;


      void finalize(const AltaricaModel *am);

      void deleteExprs();

      void aliasVar(Variable* toRemove, Variable *mergedVar) ;
      void compactSimpleAsserts(const AltaricaModel *am);

      void populateVarCache() const ;
      void addStateVarToCache(Variable*v, MemberAccess *subMA, std::string vName) const;
      void addFlowVarToCache(Variable*v, MemberAccess *subMA, std::string vName) const;
      bool isSymbol(const MemberAccess *ma) const;
      bool isSymbol(std::string s) const;

      inline void setIndex(unsigned int i) { index_ = i;}
      inline unsigned int getIndex() const { return index_;}


    };
  }
}

#endif
