/*******************************************************************************
 * 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_VARIABLE_H_
#define ALTA_VARIABLE_H_

#include <vector>
#include <string>
#include <utility>

namespace epoch {
namespace altarica {

class Node;
class Assignment;
class Assertion;
class MemberAccess;
class Value;
class Expression;
class AltaricaState;
class AltaricaModel;
class Domain;
class LandmarkState;

class Variable {

protected:

	unsigned int index_;
	mutable unsigned int loop_status_;  // 0 means nothing
	// 1 for evaluating
	// 2 for in_loop debug


	//! full qualified name
	MemberAccess *ma_;
	//! variable type
	Domain * domain_;
	//! state or flow variable
	bool state_;
	//! attributes (tool-dependant)
	std::vector<std::string> attributes_;
	const Node * node_;
	Assignment * init_;

	std::vector<Assertion*> * asserts_; // Assertions that change its value

	std::vector<std::pair<const Node*, const MemberAccess*>*> *aliases_;

	static int nbUnComputedOpen(std::vector<Variable*> * vv, AltaricaState *s);

public:

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

	//! constructeur minimal
	Variable(std::string name);
	Variable(MemberAccess* ma);

	//! constructeur un peu plus evolué
	Variable(std::string name, bool isState);
	Variable(MemberAccess *ma, bool isState);

	//! encore un autre
	Variable(std::string name, Domain* d);
	Variable(MemberAccess *ma, Domain* d);

	//! constructeur quasi-complet
	Variable(std::string name, Domain* d, bool isState);
	Variable(MemberAccess *ma, Domain* d, bool isState);

	void setMemberAccess(MemberAccess * ma);
	const MemberAccess * getMA() const;


	void addToMASecond(int i);
	void addToMASecond(std::string s);

	//! State or flow
	void setState(bool s);

	//! variable domain
	void setDomain(Domain *d);

	//! State attribute variable
	void setState();

	//! Flow attribute
	void setFlow();

	//! attributs externes a altarica
	void addAttribute(const std::string a);


	//! attributs externes a altarica
	void addAttributes(const std::vector<std::string> *va);
	const std::vector<std::string> *getAttributes() const;

	void addAssert(Assertion* a);
	void addAsserts(const std::vector<Assertion*> * a);
	const  std::vector<Assertion*> * getAsserts() const;


	void setNode(const Node* n);
	const Node * getNode() const;
	const std::vector<Node *>* getNodes() const;
	void setParentNode(const Node* n);
	const Node * getParentNode();

	std::vector<const Node *>* getPath() const;
	std::string getFullName() const;

	void setInit(Assignment* n);
	const Assignment * getInit();

	std::vector<Variable*> * flattenDomain();

	Variable * clone() const;

	//      std::string* getName();

	Domain * getDomain() const;

	inline bool isState() const {return state_;}
	inline bool isFlow() const {return !state_;}

	// Value * getInitialValue(); // defined for Prism

	static bool equals(std::vector<Variable*> *v1, std::vector<Variable*> *v2);
	static std::vector<Variable*> *removeFromVec(std::vector<Variable*> *toRemove, std::vector<Variable*> *all);

	void evaluate(const AltaricaModel* am, AltaricaState *s) const;
	void forceEvaluate(const AltaricaModel* am, AltaricaState *s) const;
	void evaluateLM(const AltaricaModel* am, LandmarkState *lms) const;


	bool hasName(const Node* n, const MemberAccess* ma) const;
	void addAlias(const Node* n, const MemberAccess* ma);
	const  std::vector<std::pair<const Node*, const MemberAccess*>*> * getAliases() const;

	std::string toString() const;
	std::string toFiacre(const bool print_head = true) const ;
	~Variable();

	void mergeVariable(const Variable *v);

	void removeAssert(Assertion*a);

	bool removeOwner(Node *n);

	std::vector<Variable*> * infoVars(const AltaricaModel*am, const Node *n, bool flow, bool computed);
};
}
}

#endif
