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

#ifndef GRAPH_LABELER_HPP_
#define GRAPH_LABELER_HPP_

#include "simple_tools.hpp"
#include "standard_base.hpp"
#include "omni/edges_position_handler.hpp"

namespace omnigraph {

/**
 * (Interface)
 * Provides string labels for vertices and edges of some graph.
 * Used with GraphPrinter to visualize graphs.
 */
template<class Graph>
class GraphLabeler {
public:
	typedef typename Graph::EdgeId EdgeId;
	typedef typename Graph::VertexId VertexId;

	virtual ~GraphLabeler() {
	}

	virtual string label(VertexId v) const = 0;

	virtual string label(EdgeId e) const = 0;

};

//template<class Graph>
//class MapGraphLabeler {
//	typedef typename Graph::EdgeId EdgeId;
//	typedef typename Graph::VertexId VertexId;
//	map<EdgeId, string> edge_map_;
//	map<VertexId, string> vertex_map_;
//
//public:
//
//	string label(VertexId v) const {
//		auto it = vertex_map_.find(v);
//		if (it == vertex_map_.end())
//			return "";
//		else
//			return it->second;
//	}
//
//	string label(EdgeId e) const {
//		auto it = edge_map_.find(e);
//		if (it == edge_map_.end())
//			return "";
//		else
//			return it->second;
//	}
//
//};

template<class Graph>
class AbstractGraphLabeler: public GraphLabeler<Graph> {
	typedef typename Graph::VertexId VertexId;
	typedef typename Graph::EdgeId EdgeId;
	const Graph& g_;
protected:
	AbstractGraphLabeler(const Graph& g): g_(g) {

	}

	const Graph& graph() const {
		return g_;
	}

public:
	/*virtual*/ std::string label(VertexId /*v*/) const {
		return "";
	}

	/*virtual*/ std::string label(EdgeId /*e*/) const {
		return "";
	}

};

/**
 * Trivial implementation of GraphLabeler.
 * All labels are "".
 */
template<class Graph>
class EmptyGraphLabeler : public GraphLabeler<Graph> {
	typedef GraphLabeler<Graph> base;
	typedef typename Graph::EdgeId EdgeId;
	typedef typename Graph::VertexId VertexId;
public:
	EmptyGraphLabeler() {}

	std::string label(VertexId /*v*/) const {
		return "";
	}

	std::string label(EdgeId /*e*/) const {
		return "";
	}
};

/**
 * Implementation of GraphLabeler for Graphs that have methods
 * str(VertexId) and str(EdgeId), such as AbstractGraph.
 */
template<class Graph>
class StrGraphLabeler : public AbstractGraphLabeler<Graph> {
	typedef AbstractGraphLabeler<Graph> base;
	typedef typename Graph::EdgeId EdgeId;
	typedef typename Graph::VertexId VertexId;
public:
	StrGraphLabeler(const Graph& g) : base(g) {}

	/*virtual*/ std::string label(VertexId v) const {
		return this->graph().str(v);
	}

	/*virtual*/ std::string label(EdgeId e) const {
		return this->graph().str(e);
	}

	/*virtual*/ ~StrGraphLabeler() {

	}
};

template <class Graph>
shared_ptr<GraphLabeler<Graph>> StrGraphLabelerInstance(const Graph& g) {
	return make_shared<StrGraphLabeler<Graph>>(g);
}

template<class Graph>
class LengthIdGraphLabeler : public StrGraphLabeler<Graph> {
	typedef StrGraphLabeler<Graph> base;
	typedef typename Graph::EdgeId EdgeId;
	typedef typename Graph::VertexId VertexId;
public:
	LengthIdGraphLabeler(const Graph& g) : base(g) {}

	/*virtual*/ std::string label(EdgeId e) const {
		std::stringstream ss;
		ss << this->graph().length(e) << " (id: " << this->graph().int_id(e) << ")";
		return ss.str();
	}

};

template<class Graph>
class LengthGraphLabeler : public StrGraphLabeler<Graph> {
	typedef StrGraphLabeler<Graph> base;
	typedef typename Graph::EdgeId EdgeId;
	typedef typename Graph::VertexId VertexId;
public:
	LengthGraphLabeler(const Graph& g) : base(g) {}

	/*virtual*/ std::string label(EdgeId e) const {
		return ToString(this->graph().length(e));
	}

};

template<class Graph>
class CoverageGraphLabeler : public AbstractGraphLabeler<Graph> {
	typedef AbstractGraphLabeler<Graph> base;
	typedef typename Graph::EdgeId EdgeId;
	typedef typename Graph::VertexId VertexId;
public:
	CoverageGraphLabeler(const Graph& g) : base(g) {}

	std::string label(EdgeId e) const {
		double coverage = this->graph().coverage(e);
		return " {Cov:" + ToString(coverage) + "}";
	}
};

template<class Graph>
class CompositeLabeler : public GraphLabeler<Graph> {
private:
	typedef typename Graph::EdgeId EdgeId;
	typedef typename Graph::VertexId VertexId;
	vector<GraphLabeler<Graph>*> list_;

	template<typename ElementId>
	string ConstructLabel(ElementId id) const {
		vector<string> to_print;
		for(size_t i = 0; i < list_.size(); i++) {
			string next = list_[i]->label(id);
			if(next.size() != 0) {
				to_print.push_back(next);
			}
		}
		string result = "";
		for(size_t i = 0; i < to_print.size(); i++) {
			result += to_print[i];
			if(i + 1 < to_print.size())
				result += "\\n";
		}
		return result;
	}

public:
	CompositeLabeler() {
	}

    CompositeLabeler(GraphLabeler<Graph> &labeler1, GraphLabeler<Graph> &labeler2, GraphLabeler<Graph> &labeler3, GraphLabeler<Graph> &labeler4) {
        AddLabeler(labeler1);
        AddLabeler(labeler2);
        AddLabeler(labeler3);
        AddLabeler(labeler4);
    }

	CompositeLabeler(GraphLabeler<Graph> &labeler1, GraphLabeler<Graph> &labeler2, GraphLabeler<Graph> &labeler3) {
		AddLabeler(labeler1);
		AddLabeler(labeler2);
		AddLabeler(labeler3);
	}

	CompositeLabeler(GraphLabeler<Graph> &labeler1, GraphLabeler<Graph> &labeler2) {
		AddLabeler(labeler1);
		AddLabeler(labeler2);
	}

	virtual ~CompositeLabeler() {
	}

	void AddLabeler(GraphLabeler<Graph> &labeler) {
		list_.push_back(&labeler);
	}

	virtual string label(VertexId vertexId) const {
		return ConstructLabel<VertexId>(vertexId);
	}

	virtual string label(EdgeId edgeId) const {
		return ConstructLabel<EdgeId>(edgeId);
	}
};

template<class Graph>
class EdgePosGraphLabeler: public AbstractGraphLabeler<Graph> {
	typedef typename Graph::EdgeId EdgeId;
	typedef typename Graph::VertexId VertexId;
public:
	const EdgesPositionHandler<Graph>& edge_pos_;

	EdgePosGraphLabeler(const Graph& g, const EdgesPositionHandler<Graph>& edge_pos) :
		AbstractGraphLabeler<Graph>(g), edge_pos_(edge_pos) {
	}

	virtual std::string label(EdgeId edgeId) const {
		return "Positions: " + edge_pos_.str(edgeId);
	}

	virtual ~EdgePosGraphLabeler() {
//		TRACE("~EdgePosGraphLabeler");
	}
private:
	DECL_LOGGER("EdgePosGraphLabeler")
};

template<class Graph>
class DefaultLabeler: public GraphLabeler<Graph> {
private:
    const Graph& g_;
    const EdgesPositionHandler<Graph> &edges_positions_;
protected:
    typedef GraphLabeler<Graph> super;
    typedef typename super::EdgeId EdgeId;
    typedef typename super::VertexId VertexId;
public:

    DefaultLabeler(const Graph &g, const EdgesPositionHandler<Graph> &position_handler) :
        g_(g), edges_positions_(position_handler) {
    }

    virtual std::string label(VertexId vertexId) const {
        return ToString(vertexId.int_id());
    }

    virtual std::string label(EdgeId edgeId) const {
        std::string ret_label;
        ret_label += "Id " + g_.str(edgeId) + "\\n";
        ret_label += "Positions:\\n"+ edges_positions_.str(edgeId);
        size_t len = g_.length(edgeId);
        double cov = g_.coverage(edgeId);
        ret_label += "Len(cov): " + ToString(len) + "(" + ToString(cov) + ")";
        return ret_label;
    }

    virtual ~DefaultLabeler() {
    }
};

}

#endif /* GRAPH_LABELER_HPP_ */
