#ifndef INCLUDE_LEARN_INTER____LEARN____RENDEZVOUS_HPP_
#define INCLUDE_LEARN_INTER____LEARN____RENDEZVOUS_HPP_

#include <Interaction.hpp>
#include <Connector.hpp>

#include <bitset>

class Port;
class ConnT__learn__rendezVous;

class PT__learn__intPort;
class PT__learn__silent;

class Inter__learn__rendezVous :  public Interaction {
public:
    virtual ~Inter__learn__rendezVous();

    // Implementation for get/set declared pure virtual in interface
    virtual const vector<Port *> &ports() const;
    virtual bool hasPorts() const;
    virtual void addPort(Port &port);
    virtual void removePort(Port &port);

    Inter__learn__rendezVous(const ConnT__learn__rendezVous &connector);
    Inter__learn__rendezVous(const ConnT__learn__rendezVous &connector, const vector<Port *> &ports);
    Inter__learn__rendezVous(const ConnT__learn__rendezVous &connector, bool p1, bool p2, bool p3);

    virtual void recycle();
    void recycle(const vector<Port *> &ports);

    bool operator<=(const Interaction &interaction) const;
    bool operator==(const Interaction &interaction) const;
    bool operator!=(const Interaction &interaction) const;
    bool operator<(const Interaction &interaction) const;
    bool nonEmptyIntersection(const Interaction &interaction) const;

    bool isDefined() const;
    bool hasSubDefined() const;

protected:
    // Implementation for get/set declared pure virtual in interface
    virtual vector<Port *> &ports();
    void refreshPorts() const ;
    mutable bool port_vector_fresh;
    mutable vector<Port *> mPorts;

    bitset<3> involvedPorts;

    mutable bool defined;
    mutable bool refresh_defined;

private:
    vector<Port *>::size_type findPort(const Port *p) const;
    void commonRecycle(const vector<Port *> &ports);
    bool en(size_t index) const;

    static const bitset<3> predefined;
};

inline
vector<Port *>::size_type Inter__learn__rendezVous::findPort(const Port *p) const {
    vector<Port *>::size_type idx;

    idx = 0;
    for (vector<QuotedPortReference *>::const_iterator i = connector().ports().begin();
         i != connector().ports().end();
         i++, idx++) {
         if (p == &((*i)->port())) {
             break;
         }
    }
    assert(idx < connector().ports().size()); // means we couldn't find the corresponding port.
    return idx;
}

inline bool Inter__learn__rendezVous::en(size_t index) const {
    return involvedPorts.test(index);
}

inline bool Inter__learn__rendezVous::isDefined() const {
    if (refresh_defined) {
        refresh_defined = false;
        defined = involvedPorts == predefined;
    }
    return defined;
}

inline bool Inter__learn__rendezVous::hasSubDefined() const {
    return isDefined();
}

inline
bool Inter__learn__rendezVous::hasPorts() const {
    return involvedPorts.any();
}

inline
void Inter__learn__rendezVous::addPort(Port &port){
    const vector<Port *>::size_type post_shift = findPort(&port);

    if (!(involvedPorts.test(post_shift))) {
        involvedPorts.set(post_shift);
        port_vector_fresh = false;
        refresh_defined = true;
    }
}

inline
void Inter__learn__rendezVous::removePort(Port &port){
    const vector<Port *>::size_type post_shift = findPort(&port);

    if (involvedPorts.test(post_shift)) {
        involvedPorts.reset(post_shift);
        port_vector_fresh = false;
        refresh_defined = true;
    }
}

inline
bool Inter__learn__rendezVous::operator!=(const Interaction &interaction) const {
    return ! (*this == interaction);
}

inline
bool Inter__learn__rendezVous::nonEmptyIntersection(const Interaction &interaction) const {
    bool ret = false;

    // check if interactions are from the same connector
    if (&connector() == &interaction.connector()) {
        assert(dynamic_cast<const Inter__learn__rendezVous *>(&interaction) != NULL);
        const Inter__learn__rendezVous *other = static_cast<const Inter__learn__rendezVous *>(&interaction);
        ret = ((involvedPorts & other->involvedPorts).any());
    }
    return ret;
}

#endif // INCLUDE_LEARN_INTER____LEARN____RENDEZVOUS_HPP_
