//***************************************************************************
//* Copyright (c) 2015 Saint Petersburg State University
//* Copyright (c) 2011-2014 Saint Petersburg Academic University
//* All Rights Reserved
//* See file LICENSE for details.
//***************************************************************************

#ifndef __OMNI_ACTION_HANDLERS_HPP__
#define __OMNI_ACTION_HANDLERS_HPP__

#include "logger/logger.hpp"

#include <boost/noncopyable.hpp>
#include <string>
#include <vector>

namespace omnigraph {

using std::vector;
/**
 * ActionHandler is base listening class for graph events. All structures and information storages
 * which are meant to synchronize with graph should use this structure. In order to make handler listen
 * to graph events one should add it to graph listeners.
 * Normally structure itself extends ActionHandler and overrides several handling methods. In
 * constructor it adds itself to graph handler list and removes itself form this list in destructor.
 * All events are divided into two levels: low level events and high level events.
 * Low level events are addition/deletion of vertices/edges. These events should be triggered only after
 * high level events when all data was already transferred and graph structure is consistent.
 * High level events should be used to keep external data synchronized with graph and keep internal data
 * consistent. Now high level events are merge, glue and split. This list can be extended in near future.
 */
template<typename VertexId, typename EdgeId>
class ActionHandler : private boost::noncopyable {
    const std::string handler_name_;
  private:
    bool attached_;
  public:
    /**
     * Create action handler with given name. With this name one can find out what tipe of handler is it.
     */
    ActionHandler(const std::string& name)
            : handler_name_(name), attached_(true) {
    }

    virtual ~ActionHandler() {
        TRACE("~ActionHandler " << handler_name_);
    }

    /**
     * Method returns name of this handler
     */
    const std::string& name() const {
        return handler_name_;
    }

    /**
     * Low level event which is triggered when vertex is added to graph.
     * @param v new vertex
     */
    virtual void HandleAdd(VertexId /*v*/) {}

    /**
     * Low level event which is triggered when edge is added to graph.
     * @param e new edge
     */
    virtual void HandleAdd(EdgeId /*e*/) {}
    
    /**
     * Low level event which is triggered when vertex is deleted from graph.
     * @param v vertex to delete
     */
    virtual void HandleDelete(VertexId /*v*/) {}

    /**
     * Low level event which is triggered when edge is deleted from graph.
     * @param e edge to delete
     */
    virtual void HandleDelete(EdgeId /*e*/) {}

    /**
     * High level event which is triggered when merge operation is performed on graph, which is when
     * path of edges with all inner vertices having exactly one incoming and one outgoing edge is
     * replaced with a single edge. Since this is high level operation event of creation of new edge
     * and events of deletion of old edges should not have been triggered yet when this event was triggered.
     * @param old_edges path of edges to be replaced with single edge
     * @param new_edge new edge that was added to be a replacement of path
     */
    virtual void HandleMerge(const vector<EdgeId>& /*old_edges*/, EdgeId /*new_edge*/) {}

    /**
     * High level event which is triggered when glue operation is performed on graph, which is when
     * edge is completely replaced with other edge. This operation is widely used in bulge removal
     * when alternative path is glued to main path. Since this is high level operation event of deletion
     * of old edge should not have been triggered yet when this event was triggered.
     * @param new_edge edge glue result
     * @param edge1 edge to be glued to edge2
     * @param edge2 edge edge1 should be glued with
     */
    virtual void HandleGlue(EdgeId /*new_edge*/, EdgeId /*edge1*/, EdgeId /*edge2*/) {}

    /**
     * High level event which is triggered when split operation is performed on graph, which is when
     * edge is split into several shorter edges. Split operation is reverse to merge operation.
     * Since this is high level operation event of deletion of old edge and events of creation of new edges
     * should not have been triggered yet when this event was triggered.
     * @param old_edge edge to be split
     * @param new_edges edges which are results of split
     */
    virtual void HandleSplit(EdgeId /*old_edge*/, EdgeId /*new_edge_1*/,
                             EdgeId /*new_edge_2*/) {}

    /**
     * Every thread safe descendant should override this method for correct concurrent graph processing.
     */
    virtual bool IsThreadSafe() const {
        return false;
    }

    bool IsAttached() const {
        return attached_;
    }

    void Attach() {
        VERIFY(!attached_);
        attached_ = true;
    }

    void Detach() {
        VERIFY(attached_);
        attached_ = false;
    }
};

template<class Graph>
class GraphActionHandler : public ActionHandler<typename Graph::VertexId,
                                                typename Graph::EdgeId> {
    typedef ActionHandler<typename Graph::VertexId, typename Graph::EdgeId> base;

    const Graph& g_;

  protected:
    const Graph& g() const {
        return g_;
    }

  public:
    GraphActionHandler(const Graph& g, const std::string& name)
            : base(name),
              g_(g) {
        TRACE("Adding new action handler: " << this->name());
        g_.AddActionHandler(this);
    }

    GraphActionHandler(const GraphActionHandler<Graph> &other)
            : base(other.name()),
              g_(other.g_) {
        TRACE("Adding new action handler: " << this->name());
        g_.AddActionHandler(this);
    }

    virtual ~GraphActionHandler() {
        TRACE("Removing action handler: " << this->name());
        if(this->IsAttached())
            this->Detach();
        g_.RemoveActionHandler(this);
    }
};

/**
 * In order to support various types of graphs and make handler structure more flexible HandlerApplier
 * structure was introduced. If certain implementation of graph requires special handler triggering scheme
 * one can store certain extension of HandlerApplier in graph and trigger HandlerApplier methods instead
 * of GraphHandler methods.
 * HandlerApplier contains one method for each of graph events which define the exact way this event
 * should be triggered.
 */
template<typename VertexId, typename EdgeId>
class HandlerApplier {
    typedef ActionHandler<VertexId, EdgeId> Handler;
  public:

    virtual void
    ApplyAdd(Handler& handler, VertexId v) const = 0;

    virtual void
    ApplyAdd(Handler& handler, EdgeId e) const = 0;

    virtual void
    ApplyDelete(Handler& handler, VertexId v) const = 0;

    virtual void
    ApplyDelete(Handler& handler, EdgeId e) const = 0;

    virtual void ApplyMerge(Handler& handler, vector<EdgeId> old_edges,
                            EdgeId new_edge) const = 0;

    virtual void ApplyGlue(Handler& handler, EdgeId new_edge, EdgeId edge1,
                           EdgeId edge2) const = 0;

    virtual void ApplySplit(Handler& handler, EdgeId old_edge,
                            EdgeId new_edge_1, EdgeId new_edge2) const = 0;

    virtual ~HandlerApplier() {
    }
};

/**
 * SimpleHandlerApplier is simple implementation of handler applier with no special filtering.
 */
template<class Graph>
class SimpleHandlerApplier : public HandlerApplier<typename Graph::VertexId,
                                                   typename Graph::EdgeId> {
  public:
    typedef typename Graph::VertexId VertexId;
    typedef typename Graph::EdgeId EdgeId;
    typedef ActionHandler<VertexId, EdgeId> Handler;

    virtual void ApplyAdd(Handler& handler, VertexId v) const {
        handler.HandleAdd(v);
    }

    virtual void ApplyAdd(Handler& handler, EdgeId e) const {
        handler.HandleAdd(e);
    }

    virtual void ApplyDelete(Handler& handler, VertexId v) const {
        handler.HandleDelete(v);
    }

    virtual void ApplyDelete(Handler& handler, EdgeId e) const {
        handler.HandleDelete(e);
    }

    virtual void ApplyMerge(Handler& handler, vector<EdgeId> old_edges,
                            EdgeId new_edge) const {
        handler.HandleMerge(old_edges, new_edge);
    }

    virtual void ApplyGlue(Handler& handler, EdgeId new_edge, EdgeId edge1,
                           EdgeId edge2) const {
        handler.HandleGlue(new_edge, edge1, edge2);
    }

    virtual void ApplySplit(Handler& handler, EdgeId old_edge, EdgeId new_edge1,
                            EdgeId new_edge2) const {
        handler.HandleSplit(old_edge, new_edge1, new_edge2);
    }

};

/**
 * PairedHandlerApplier is implementation of HandlerApplier for graph with synchronization of actions
 * performed with vertices/edges and its reverse-complement analogues. Thus while corresponding
 * method was called only once event should be triggered twice: for the parameters with which method
 * was called and for reverse-complement parameters. Also certain assertions were added for bad cases.
 */
template<class Graph>
class PairedHandlerApplier : public HandlerApplier<typename Graph::VertexId,
                                                   typename Graph::EdgeId> {
  private:
    Graph &graph_;
  public:
    typedef typename Graph::VertexId VertexId;
    typedef typename Graph::EdgeId EdgeId;
    typedef ActionHandler<VertexId, EdgeId> Handler;

    PairedHandlerApplier(Graph &graph)
            : graph_(graph) {
    }

    virtual void ApplyAdd(Handler& handler, VertexId v) const {
        VertexId rcv = graph_.conjugate(v);
        handler.HandleAdd(v);
        if (v != rcv) {
            handler.HandleAdd(rcv);
        }
    }

    virtual void ApplyAdd(Handler& handler, EdgeId e) const {
        EdgeId rce = graph_.conjugate(e);
        handler.HandleAdd(e);
        if (e != rce) {
            handler.HandleAdd(rce);
        }
    }

    virtual void ApplyDelete(Handler& handler, VertexId v) const {
        VertexId rcv = graph_.conjugate(v);
        handler.HandleDelete(v);
        if (v != rcv) {
            handler.HandleDelete(rcv);
        }
    }

    virtual void ApplyDelete(Handler& handler, EdgeId e) const {
        EdgeId rce = graph_.conjugate(e);
        handler.HandleDelete(e);
        if (e != rce) {
            handler.HandleDelete(rce);
        }
    }

    virtual void ApplyMerge(Handler& handler, vector<EdgeId> old_edges,
                            EdgeId new_edge) const {
        EdgeId rce = graph_.conjugate(new_edge);
        handler.HandleMerge(old_edges, new_edge);
        if (new_edge != rce) {
            vector<EdgeId> rc_old_edges;
            for (int i = (int) old_edges.size() - 1; i >= 0; i--) {
                rc_old_edges.push_back(graph_.conjugate(old_edges[i]));
            }
            handler.HandleMerge(rc_old_edges, rce);
        }
    }

    virtual void ApplyGlue(Handler& handler, EdgeId new_edge, EdgeId edge1,
                           EdgeId edge2) const {
        EdgeId rc_edge1 = graph_.conjugate(edge1);
        EdgeId rc_edge2 = graph_.conjugate(edge2);
        VERIFY(edge1 != edge2);
        VERIFY(edge2 != rc_edge2);
        handler.HandleGlue(new_edge, edge1, edge2);
        if (edge1 != rc_edge1) {
            handler.HandleGlue(graph_.conjugate(new_edge), rc_edge1, rc_edge2);
        }
    }

    virtual void ApplySplit(Handler& handler, EdgeId old_edge,
                            EdgeId new_edge_1, EdgeId new_edge2) const {
        EdgeId rce = graph_.conjugate(old_edge);
        //VERIFY(old_edge != rce);
        handler.HandleSplit(old_edge, new_edge_1, new_edge2);
        if (old_edge != rce) {
            handler.HandleSplit(rce, graph_.conjugate(new_edge2),
                                graph_.conjugate(new_edge_1));
        }
    }

  private:
    DECL_LOGGER("PairedHandlerApplier")
};

};

#endif
