/*******************************************************************************
 * 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 "MetaEvent.hh"
#include "Event.hh"
#include "EventLaw.hh"
#include <sstream>
#include <cfloat>
#include <iostream>

using namespace epoch::altarica;    


double MetaEvent::complementOthersProba(std::vector<const Event*> * allEvents,  AltaricaModel *am, AltaricaState *as) {
    double proba = 1.0;
    for (std::vector<const Event*>::iterator i = allEvents->begin(); i!= allEvents->end(); ++i) {
	if (!containsE(*i))
	    proba *= 1.0-(*i)->getLaw()->evalParam(am,as);
    }
    return proba;
}


void MetaEvent::push_back(Event *e) {
    events_->push_back(e);
}

bool MetaEvent::containsE(const Event *e) const{
    for (std::vector<Event*>::iterator i = events_->begin(); i!= events_->end(); ++i)
	if (e == *i)
	    return true;
    return false;

}



MetaEvent* MetaEvent::unionMetaEvents(std::vector<MetaEvent*> * metavec) {
    MetaEvent * unions = new MetaEvent();
    for (std::vector<MetaEvent*>::iterator si = metavec->begin(); si!=metavec->end(); ++si) {	
	unions->add(*si);
    }
    return unions;
}

bool MetaEvent::contains(const MetaEvent* mev) const 
{
  for (auto i:*mev->getEvents()) 
    if (!containsE(i))
      return false;
  return true;  
}


bool MetaEvent::equals(const MetaEvent* mev) const
{
  if (!contains(mev))
    return false;
  if (!mev->contains(this))
    return false;  
  return true;  
}


bool MetaEvent::addMEV(std::vector<MetaEvent*> * list, MetaEvent* mev) 
{
  for (auto i:*list) 
    if (i->equals(mev))
      return false;
  list->push_back(mev);  
  return true;  
}


std::vector<MetaEvent*>* MetaEvent::combineMetaEvents(std::vector<MetaEvent*> * metavec) {
  std::vector<MetaEvent*> * combined = new std::vector<MetaEvent*>();
    
  while (!metavec->empty()) {
    MetaEvent * mev = metavec->back();
    metavec->pop_back();
    
    if (combined->empty())
      combined->push_back(mev);
    else {      
      std::vector<MetaEvent*> newMEVs;
      for (auto i:*combined) {
        if (!i->contains(mev)) {          
          MetaEvent * nmev = i->clone();
          nmev->add(mev);          
          newMEVs.push_back(nmev);    
        }        
      }
      for (auto i:newMEVs)
        if (!addMEV(combined, i))
          delete i;
    }    
    
  }
  
  return combined;
  
}


unsigned int MetaEvent::size() {
	return events_->size();
}



bool MetaEvent::eventIn(Event *e) {
    for (std::vector<Event*>::iterator i = events_->begin(); i!= events_->end(); ++i)
	if (e == *i)
	    return true;
    return false;
}

bool MetaEvent::add(MetaEvent* m) {
  bool changed = false;  
  const std::vector<Event*> * otherE = m->getEvents();
  for (std::vector<Event*>::const_iterator i = otherE->begin(); i!= otherE->end(); ++i) {
    if (!eventIn(*i)) {       
      events_->push_back(*i);
      changed = true;      
    }    
  }
  return true;  
}


std::vector<MetaEvent*>* MetaEvent::buildVec(std::vector<std::vector<Event*>*>* v) {
    std::vector<MetaEvent*>* ret = new std::vector<MetaEvent*>();
    for (std::vector<std::vector<Event*>*>::iterator i = v->begin(); i!= v->end(); ++i) {
	ret->push_back(new MetaEvent(*i));
    }
    return ret;
}

MetaEvent::MetaEvent() {
    events_ = new std::vector<Event*>();
}

MetaEvent::MetaEvent(std::vector<Event*> *v) {
    events_ = v;
}

MetaEvent * MetaEvent::clone() const {
    MetaEvent * r = new MetaEvent();
    for (std::vector<Event*>::iterator i = events_->begin(); i!= events_->end(); ++i)
	r->push_back(*i);
    r->setProba(proba_);
    r->setReward(reward_);
    return r;
}

MetaEvent::~MetaEvent() {
    delete events_;
}

int MetaEvent::comparePrioTo(MetaEvent* other) {
    bool meMorePrior = false;
    bool meLessPrior = false;
    const std::vector<Event*> * otherE= other->getEvents();

    // first check if I have one event comparable
    for (std::vector<Event*>::const_iterator i = otherE->begin(); 
	 meMorePrior == false &&   meLessPrior == false && i!= otherE->end(); ++i) {
	for (std::vector<Event*>::iterator j = events_->begin(); 
	     meMorePrior == false && meLessPrior == false && j!= events_->end(); ++j) {
	    if ((*j)->isMorePriorThan(*i))
		meMorePrior = true;
	    if ((*j)->isLessPriorThan(*i))
		meLessPrior = true;	    
	}
    }
    
    // if incomparable, same prio
    if (!meLessPrior && !meMorePrior)
	return 0;

    // if comparable, 
    // now check that I do not have and opposite prio event
    // if so -> same prio
    if (meLessPrior || meMorePrior)
	for (std::vector<Event*>::const_iterator i = otherE->begin(); i!= otherE->end(); ++i) {
	    for (std::vector<Event*>::iterator j = events_->begin(); j!= events_->end(); ++j) {
		if (meMorePrior && (*i)->isMorePriorThan(*j))
		    return 0;
		if (meLessPrior && (*i)->isLessPriorThan(*j))
		    return 0;
	    }
	}
    
    if (meMorePrior)
	return 1;
    if (meLessPrior)
	return -1;

    // never reached
    return 0;
}


bool MetaEvent::isEnabled(const AltaricaModel *am, AltaricaState *s) const {    
    for (std::vector<Event*>::iterator ei = events_->begin(); ei != events_->end(); ++ei) {
	if (!(*ei)->isEnabled(am, s))
	    return false;
    }
    return true;
}

bool MetaEvent::isDirac() const {
    for (std::vector<Event*>::const_iterator i = events_->begin(); i!= events_->end(); ++i) {
	if ((*i)->getLaw() != NULL && (*i)->getLaw()->isDirac())
	    return true;
    }
    return false;
}



std::string MetaEvent::toString() {
    //    std::string s;
    std::stringstream s;
    s <<  "MetaEvent: < p = ";
    s << proba_; 
    s << " r = ";
    s << reward_;
    s << "   ";
    if (events_->size() != 0)
	for (std::vector<Event*>::iterator i = events_->begin(); i!=events_->end(); ++i) 
	    s << (*i)->toString();
    else
	s << " NO EVENT";
    s<< ">\n";
    return s.str();
}

double MetaEvent::timesDiscreteProba(AltaricaModel *am, AltaricaState *as) {
  proba_ = 1.0;
  for (std::vector<Event*>::iterator i = events_->begin(); i!= events_->end(); ++i) {
    if ((*i)->getLaw() != NULL)
	    proba_ *= (*i)->getLaw()->evalParam(am,as);
  }
  return proba_;  
}
double MetaEvent::timesDiscreteProba(double p) {
    proba_ *= p;
    return proba_;    
}


void MetaEvent::setMaxContProba(AltaricaModel *am, AltaricaState *as) {
    proba_ = DBL_MIN;
    bool printed = true;
    for (std::vector<Event*>::iterator i = events_->begin(); i!= events_->end(); ++i) {
	if ((*i)->getLaw() != NULL) {
	    if (proba_ != DBL_MIN) {
		if (!printed) {
		    std::cerr<< "BIG FAT WARNING: >1 synchronised events have a continuous law: theoretical proba is 0........." << std::endl;
		    std::cerr<< toString() << std::endl;
		    printed = true;
		}
	    }
	    if ((*i)->getLaw()->evalParam(am,as) > proba_)
		proba_ = (*i)->getLaw()->evalParam(am,as);
	    
	}	    
    }
}
