/*******************************************************************************
 * 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
 *
 *******************************************************************************/

#include "LandmarkState.hh"
#include "AltaricaState.hh"
#include "AltaricaModel.hh"
#include "LandmarkMetaEvent.hh"
#include "Transition.hh"
#include "Variable.hh"
#include "Event.hh"
#include "MetaEvent.hh"
#include "Assignment.hh"
#include "Values.hh"
#include "iostream"

using namespace epoch::altarica;

LandmarkState::LandmarkState(AltaricaState *as) {
    as_ = as;   
    labels_ = new labelsType(as->getAModel()->getNStateVars(), NULL);    
    flowLabels_ = new labelsType(as->getAModel()->getNFlowVars(), NULL);    
}

void LandmarkState::addSelfAsLabel() {
    for (unsigned int i = 0; i<labels_->size(); ++i) {
	if ((*labels_)[i] == NULL) {
	    (*labels_)[i] = new std::map<Value*, AltaricaState*, ValueComparator>();
	}	
	Value *v = as_->getStateValue(i);
	if (v != NULL) {	    
	    std::vector<Value*> * vfv = v->getSingletons();	    
	    for (std::vector<Value*>::iterator j = vfv->begin(); j!= vfv->end(); ++j) {
		if ((*labels_)[i]->find(*j) == (*labels_)[i]->end())
		    (*(*labels_)[i])[*j] = new AltaricaState(false, as_->getAModel(),as_->getAModel()->getInitInfo());
		(*(*labels_)[i])[*j]->addStateValue(i, *j);
		(*j)->own();
	    }	    
	}
    }    
}

void LandmarkState::removeSelfAsLabel() {
    for (unsigned int i = 0; i<labels_->size(); ++i) {
	Value *v = as_->getStateValue(i);
	if (v != NULL) {	    
	    std::vector<Value*> * vfv = v->getSingletons();	    
	    for (std::vector<Value*>::iterator j = vfv->begin(); j!= vfv->end(); ++j) {
		if ((*labels_)[i]->find(*j) != (*labels_)[i]->end()) {
		    AltaricaState * lm = (*(*labels_)[i])[*j];
		    lm->removeValue(i, *j);
		    if (lm->isEmpty()) {
			delete lm;
			((*labels_)[i])->erase(*j);
			//			(*(*labels_)[i])[*j] = NULL;
		    }
		}
		//		(*j)->own();
	    }	    
	}
    }    
}


void LandmarkState::addLabel(unsigned int i, Value* v, const AltaricaState* label) {
    if ((*labels_)[i] == NULL) {
	(*labels_)[i] = new std::map<labelsKeyType, AltaricaState*, ValueComparator>();
	(*((*labels_)[i]))[v] = label->clone();
    } else {
	std::map<labelsKeyType, AltaricaState*, ValueComparator>::iterator j = (*labels_)[i]->find(v);
	if (j == (*labels_)[i]->end())
	    (*((*labels_)[i]))[v] = label->clone();
	else
	    label->mergeToState(j->second);	    
    }    
}


void LandmarkState::addNoopLabels(LandmarkState *ps) {
    for (unsigned int i =0; i<labels_->size(); ++i) {
	Value *v = as_->getStateValue(i);
	Value *vps = ps->getState()->getStateValue(i);
	if (v != NULL && vps != NULL) {	    
	    std::vector<Value*> *vfv = v->getSingletons();	    
	    for (std::vector<Value*>::iterator j = vfv->begin(); j!= vfv->end(); ++j) {
		if ((*labels_)[i] != NULL)
		    if ((*labels_)[i]->find(*j) != (*labels_)[i]->end())
			(*(*labels_)[i])[*j]->mergeIntersectState(ps->getLabel(i,*j));
	    }	    
	}
    }
}


AltaricaState* LandmarkState::getLabel(Variable* v, Value *fv) const {
    if (v->isFlow())
	return getFlowLabel(v->getIndex(), fv);
    return getLabel(v->getIndex(), fv);	

}

AltaricaState* LandmarkState::getLabel(unsigned int i, Value *fv)  const {
    if ((*labels_)[i] == NULL)
	return NULL;
    if ((*labels_)[i]->find(fv) == (*labels_)[i]->end())
	return NULL;
    return (*(*labels_)[i])[fv];
}


AltaricaState* LandmarkState::getFlowLabel(unsigned int i, Value *fv)  const {
    if ((*flowLabels_)[i] == NULL)
	return NULL;
    if ((*flowLabels_)[i]->find(fv) == (*flowLabels_)[i]->end())
	return NULL;
    return (*(*flowLabels_)[i])[fv];
}


std::string LandmarkState::toString() const {
    std::string s;
    s += as_->toString(true,true,false,false);
    s +=  "LABELS :\n";
    for (unsigned int i = 0; i<labels_->size(); ++i) {
	if (as_->getStateValue(i) != NULL) {
	    if ((*labels_)[i] != NULL)
		for (std::map<labelsKeyType, AltaricaState*>::const_iterator j = (*labels_)[i]->begin(); j!= (*labels_)[i]->end(); ++j) {
		    s += "STATE VAR:" + as_->getAModel()->getStateVar(i)->toString() + "/" + j->first->toString() + " : \n" ;
		    s += j->second->toString(true,false,false,false) ;
		    s += "\n";
		}
	}
    }
    for (unsigned int i = 0; i<flowLabels_->size(); ++i) {
	if (as_->getFlowValue(i) != NULL) {
	    if ((*flowLabels_)[i] != NULL)
		for (std::map<labelsKeyType, AltaricaState*>::const_iterator j = (*flowLabels_)[i]->begin(); j!= (*flowLabels_)[i]->end(); ++j) {
		    s += "FLOW VAR:" + as_->getAModel()->getFlowVar(i)->toString() + "/" + j->first->toString() + " : \n" ;
		    if (j->second != NULL)
			s += j->second->toString(true,false,false,false) ;
		     s+= "\n";
		}
	}
    }
    return s;
}


void LandmarkState::computeLabels(const AltaricaModel *am, const AltaricaState *ps, LandmarkMetaEvent *la) {

    // as_ is current macro state
    
    std::vector<std::vector<Value*> *> assigned(ps->getAModel()->getNStateVars(), NULL);
    AltaricaState * ts = ps->clone();

    // we replay the transition to see what values are created by it
    
    const std::vector<Event*> *evs = la->getMetaEvent()->getEvents();

    for (std::vector<Event*>::const_iterator evi = evs->begin(); evi!= evs->end(); ++evi) {
	const std::vector<Transition*> * trans = (*evi)->getTrans();
	for (std::vector<Transition*>::const_iterator transi = trans->begin(); transi!=trans->end(); ++transi) {
	    const std::vector<Assignment*> * assigns = (*transi)->getAssigns();
	    for (std::vector<Assignment*>::const_iterator assi = assigns->begin(); assi!= assigns->end(); ++assi) {
		//assigned[(*k)->getVar()->getIndex()] = true;
		Value * v = (*assi)->evaluate(am, ts);
		std::vector<Value*> *afv = v->getSingletons();
		for (std::vector<Value*>::iterator i = afv->begin(); i!= afv->end(); ++i) {
		    if (assigned[(*assi)->getVar()->getIndex()] == NULL)
			assigned[(*assi)->getVar()->getIndex()] = new std::vector<Value*>();		    
		    assigned[(*assi)->getVar()->getIndex()]->push_back(*i);
		}		
	    }
	}
    }

    // for each of values created by the transition, we add transition label

    for (unsigned int i = 0; i<ps->getAModel()->getNStateVars(); ++i) {
	if (assigned[i]!=NULL) {
	    for (std::vector<Value*>::iterator j = assigned[i]->begin(); j!= assigned[i]->end(); ++j) 
		addLabel(i,(Value*)*j, la->getLabel());		    	    
	}
    }
}

void LandmarkState::mergeIntersectLabels(const LandmarkState * os) {
    const labelsType * l = os->getLabels();
    for (unsigned int i =0; i<labels_->size(); ++i) {
	if ((*labels_)[i] == NULL) {
	    if (((*l)[i])!=NULL) 
		(*labels_)[i] = new std::map<labelsKeyType, AltaricaState*, ValueComparator>(*((*l)[i]));
	    else 
		(*labels_)[i] = new std::map<labelsKeyType, AltaricaState*, ValueComparator>();
	} else {
	    if ((*l)[i]!=NULL)
		for (std::map<Value*, AltaricaState*,ValueComparator>::iterator 
			 j = (*l)[i]->begin(); j!= (*l)[i]->end(); ++j) { 
		    if ((*labels_)[i]->find(j->first) == (*labels_)[i]->end()) 
			(*(*labels_)[i])[j->first] = j->second->clone();
		    else 
			(*(*labels_)[i])[j->first]->intersectState(j->second);		
		}
	}


    }


    l = os->getFlowLabels();
    for (unsigned int i =0; i<flowLabels_->size(); ++i) {
	if ((*flowLabels_)[i] == NULL) {
	    if (((*l)[i])!=NULL) 
		(*flowLabels_)[i] = new std::map<labelsKeyType, AltaricaState*, ValueComparator>(*((*l)[i]));
	    else 
		(*flowLabels_)[i] = new std::map<labelsKeyType, AltaricaState*, ValueComparator>();
	} else {
	    if ((*l)[i]!=NULL)
		for (std::map<Value*, AltaricaState*,ValueComparator>::iterator 
			 j = (*l)[i]->begin(); j!= (*l)[i]->end(); ++j) { 
		    if ((*flowLabels_)[i]->find(j->first) == (*flowLabels_)[i]->end()) 
			(*(*flowLabels_)[i])[j->first] = j->second->clone();
		    else 
			(*(*flowLabels_)[i])[j->first]->intersectState(j->second);		
		}
	}


    }


}



void LandmarkState::computeFlowLabels(const AltaricaModel *am) {
    unsigned int nfv = am->getNFlowVars();
    as_->deleteFlowValues();
    for (unsigned int i = 0; i<nfv; ++i) {
	Variable *v = am->getFlowVar(i);
	v->evaluateLM(am, this);
	std::cout << this->toString();
    }
	
}
