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

#include "Event.hh"
#include "Transition.hh"
#include "Node.hh"
#include "EventLaw.hh"
#include "MemberAccess.hh"


namespace epoch {
  namespace altarica {

    // Event::Event(std::string * name) {
    //     name_ = new std::string(*name);
    //     this->path_ = NULL;
    //     this->path_indexes_ = NULL;
    // }


    Event::Event(MemberAccess *ma) {
      ma_ = ma;
      law_ = NULL;
    }


    void Event::addUppers(std::set<Event*>* b) {
      for (std::set<Event*>::iterator it = b->begin();
           it != b->end(); ++it)
        addUpper(*it);
    }

    void Event::addUpper(Event *b) {

      uppers_.insert(b);
      for (std::set<Event*>::iterator li = lowers_.begin(); li!=lowers_.end(); ++li)
        (*li)->addUpper(b);
    }


    void Event::addLowers(std::set<Event*>* b) {
      for (std::set<Event*>::iterator it = b->begin();
           it != b->end(); ++it)
        addLower(*it);
    }

    void Event::addLower(Event *b) {
      lowers_.insert(b);
      for (std::set<Event*>::iterator ui = uppers_.begin(); ui!=uppers_.end(); ++ui)
        (*ui)->addLower(b);
    }


    void Event::addAttribute(const std::string a) {
      attributes_.push_back(std::string(a));
    }



    void Event::addAttributes(const std::vector<std::string> *va) {
      for (std::vector<std::string>::const_iterator si = va->begin(); si != va->end(); ++si) {
        addAttribute(*si);
      }
    }


    void Event::setLaw(EventLaw* el) {
      if (law_ != NULL)
        delete law_;
      law_ = el;
      if (law_ != NULL)
        law_->setNode(node_);
    }

    const EventLaw * Event::getLaw() const {
      return law_;
    }



    Event * Event::clone() {
      Event *n = new Event(ma_->clone());
      n->addAttributes(&attributes_);
      n->addUppers(&uppers_);
      n->addLowers(&lowers_);
      if (law_ != NULL)
        n->setLaw(law_->clone());

      return n;
    }
    const MemberAccess* Event::getMA() const {
      return ma_;
    }



    // void Event::addPath(std::string *p) {
    //     if (path_ == NULL) {
    //  path_ = new std::vector<std::string*>();
    //  path_indexes_ = new std::vector<int>();
    //     }

    //     path_->push_back(new std::string(*p));
    //     path_indexes_->push_back(-1);
    // }

    // void Event::addPathIndexed(std::string *p, int i) {
    //     if (path_ == NULL) {
    //  path_ = new std::vector<std::string*>();
    //  path_indexes_ = new std::vector<int>();
    //     }
    //     path_->push_back(new std::string(*p));
    //     path_indexes_->push_back(i);

    // }

    std::set<Event*> const * Event::getUppers() {
      return &uppers_;
    }

    void Event::setNode(Node*n) {
      node_ = n;
      if (law_ != NULL)
        law_->setNode(n);
    }

    void Event::addTrans(Transition* t) {
      trans_.push_back(t);
    }

    void Event::addSync(Synchronisation* s) {
      bool found = false;
      for (std::vector<Synchronisation*>::iterator i = syncs_.begin(); i!= syncs_.end(); ++i)
        if (*i == s) {
          found = true;
          break;
        }
      if (!found)
        syncs_.push_back(s);
    }

    int Event::nSynchros() {
      return (syncs_.size());
    }

    const Node * Event::getNode() {
      return node_;
    }

    std::vector<const Node*>* Event::getPath() const {
      std::vector<const Node*>* path = node_->getPath();
      path->push_back(node_);
      return path;
    }

    std::string Event::getFullName() const {
      std::stringstream ss;
      for(auto &n: *getPath())
        ss << n->getMA()->toString() << ".";
      ss << getMA()->toString();
      return ss.str();
    }

    bool Event::isMorePriorThan(Event*e) {
      if (e->getNode() != node_)
        return false;
      for (std::set<Event*>::iterator i = lowers_.begin(); i!= lowers_.end(); ++i) {
        if (e == *i)
          return true;
      }
      for (std::set<Event*>::iterator i = lowers_.begin(); i!= lowers_.end(); ++i) {
        return (*i)->isMorePriorThan(e);
      }
      return false;
    }

    bool Event::isLessPriorThan(Event*e) {
      if (e->getNode() != node_)
        return false;
      for (std::set<Event*>::iterator i = uppers_.begin(); i!= uppers_.end(); ++i) {
        if (e == *i)
          return true;
      }
      for (std::set<Event*>::iterator i = uppers_.begin(); i!= uppers_.end(); ++i) {
        return (*i)->isLessPriorThan(e);
      }
      return false;
    }

    bool Event::isEnabled(const AltaricaModel *am, AltaricaState *s) {
      if (law_ != NULL && law_->isDisabled())
        return false;
      if (s == NULL)
        return true;
      if (trans_.size() == 0)
        return true;
      for (std::vector<Transition*>::iterator ti = trans_.begin(); ti != trans_.end(); ++ti) {
        if ( (*ti)->precondOK(am, s))
          return true;
      }
      return false;
    }


    bool Event::isDirac() const {
      if (law_ == NULL)
        return false;
      return (law_->isDirac());
    }



    void Event::doEvent(const AltaricaModel *am, AltaricaState *s, AltaricaState *ns) {
      for (std::vector<Transition*>::iterator ti = trans_.begin();
           ti != trans_.end(); ++ti) {
        if ((*ti)->precondOK(am, s))
          (*ti)->doTransition(am, s,ns);
      }
    }

    std::string Event::toString() {
      std::string s;
      s+= " "+ node_->toString() + "." + ma_->toString() + " ";
      return s;

    }

    void Event::deleteExprs() {
      if(law_ != NULL)
        law_->deleteExpr();
    }

    Event::~Event() {
      delete ma_;
      delete law_;
    }
  }
}
