#ifndef _DEPGRAPH_H
#define _DEPGRAPH_H

#include <tuple>
#include <vector>
#include <set>
#include <map>
#include <string>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <unordered_set>
#include "include/ngram.h"
#include "common/token/token.h"
#include "common/token/deplabel.h"
#include "common/token/supertag.h"
#include "macros_base.h"

typedef std::tuple<int, int, ttoken> GraphEdge;
#define EDGE_X(X)			(std::get<0>(X))
#define EDGE_Y(X)			(std::get<1>(X))
#define EDGE_LABEL(X)		(std::get<2>(X))

namespace graphparsing_1ec_n4 {
	class DepParser;
}
namespace graphparsing_1ec_n5 {
	class DepParser;
}

class DependencyGraph
{
private:
	std::vector<POSTaggedWord> m_lSentence;
	std::vector<GraphEdge> m_lGraph;
	DependencyTree m_lTree;
	std::vector<BiGram<int> > m_lCrossEdge;
public:

	void clear()
	{
		m_lSentence.clear();
		m_lGraph.clear();
		m_lTree.clear();
	}

	friend std::istream & operator>>(std::istream & is, DependencyGraph& depgraph) {
		ttoken token,line;
		std::vector<ttoken> vector_token;
		std::vector<int> vector_head;
		vector_token.clear();
		vector_head.clear();
		depgraph.clear();
		int fn = 0,n = 0;
		int debug = 0;
		int root = 0;
		depgraph.m_lTree.push_back(DependencyTreeNode(POSTaggedWord(ROOT_WORD, ROOT_POSTAG), - 1, ROOT_DEPLABEL));
		while (true) {
			std::getline(is, line);
			if (line.empty()) {
				break;
			}
			n++;
			DependencyTaggedTreeNode node;
			std::istringstream iss(line);
			int x,head;
			ttoken word, tag , label,fathers;
			iss >> x >> word >> token >> tag >> token >> token >> token >> token >> head >> label >> token;
			if (head == -1)
				root = -1;
			std::getline(iss, fathers);
			if (debug)
			{
				std::cout << x << ' ' << word << ' ' << tag << ' ' << head << ' ' << label << ' ' <<token<< ' ' << fathers << std::endl;
			}
			depgraph.m_lSentence.push_back(POSTaggedWord(word, tag));
			depgraph.m_lTree.push_back(DependencyTreeNode(POSTaggedWord(word, tag),head,label));
			vector_token.push_back(fathers);
			if (token != "_") { ++fn; vector_head.push_back(x); }
		}
		if (root == -1)
			for (int i = 1 ; i <= n ; i++)
				depgraph.m_lTree[i] = DependencyTreeNode(std::get<0>(depgraph.m_lTree[i])
														,std::get<1>(depgraph.m_lTree[i])+1
														,std::get<2>(depgraph.m_lTree[i]));
		for (int j = 0; j < n; j++)
		{
			std::istringstream iss(vector_token[j]);
			for (int i = 0; i < fn; i++)
			{
				iss >> token;
				if (token == "_") continue;
				else depgraph.m_lGraph.push_back(GraphEdge(vector_head[i], j + 1, token));
			}			
		}
		if (debug)
		{
			for (const auto arc : vector_head)
				std::cout << arc <<' ';
			std::cout<<std::endl;
		}
		
		return is;
	}
	void print() const
	{
		std::cout << m_lSentence.size() << std::endl;
		for (const auto arc : m_lGraph)
		{
			std::cout << EDGE_X(arc) << ' ' << EDGE_Y(arc) << ' ' << EDGE_LABEL(arc) << std::endl;
		}
	}

	std::vector<POSTaggedWord> getSentence() const
	{
		return m_lSentence;
	}
	std::vector<GraphEdge> getGraph() const
	{
		return m_lGraph;
	}

	int getedgesum() {
		return m_lGraph.size();
	}

	DependencyTree getTree() const
	{
		return m_lTree;
	}

	bool IsProjective()
	{
		m_lCrossEdge.clear();
		for (int i = 0; i < m_lGraph.size(); i++)
			for (int j = i + 1; j < m_lGraph.size(); j++)
			{
				auto arc1 = m_lGraph[i], arc2 = m_lGraph[j];
				if (cross(arc1, arc2)) m_lCrossEdge.push_back(BiGram<int>(i, j));
			}
		if (m_lCrossEdge.size() == 0)
			return true;
		else return false;
	}

	bool OneCross()
	{
		for (const auto arc1 : m_lGraph)
		{
			int num = 0;
			for (const auto arc2 : m_lGraph)
				if (cross(arc1, arc2)) num++;
			if (num > 1) return false;
		}
		return true;
	}

	bool HaveTwoDirection()
	{
		for (const auto arc1 : m_lGraph)
			for (const auto arc2 : m_lGraph)
			{
				if ( EDGE_X(arc1) == EDGE_Y(arc2) && EDGE_X(arc2) == EDGE_Y(arc1) )
					return true;
			}
		return false;
	}

	bool HaveSingleRing()
	{
		for (const auto arc1 : m_lGraph)			
			{
				if (EDGE_X(arc1) == EDGE_Y(arc1))
					return true;
			}
		return false;
	}

	bool IsInner(int x, int x1, int x2)
	{
		return (x - x1) * (x - x2) < 0;
	}

	bool cross(GraphEdge arc1, GraphEdge arc2)
	{
		return ( (EDGE_X(arc1) - EDGE_X(arc2)) * (EDGE_X(arc1) - EDGE_Y(arc2))
			* (EDGE_Y(arc1) - EDGE_X(arc2)) * (EDGE_Y(arc1) - EDGE_Y(arc2)) < 0);
		
	}

	bool HaveTailPoint()
	{
		int n = m_lSentence.size();
		for (const auto arc1 : m_lGraph)
		{
			if (EDGE_X(arc1) == n || EDGE_Y(arc1) == n)
				return true;
		}
		return false;
	}

	bool OneEdgeCut_Plannar(int op)
	{
		for (int ii = 0; ii < m_lGraph.size(); ii++)
		{
			int flag = 1;
			for (auto arc : m_lCrossEdge)
			{
				if (arc.first() != ii && arc.second() != ii)
					flag = 0;
			}
			if (flag)
			{
				if (op == 1)
				{
					auto arccut = m_lGraph[ii];
					std::vector<GraphEdge>::iterator Iter;
					for (Iter = m_lGraph.begin(); Iter != m_lGraph.end(); Iter++)
						if ((*Iter) == arccut)
						{m_lGraph.erase(Iter); break;}
				}
				return true;
			}
		}
		return false;
	}

	bool TwoEdgeCut_Plannar(int op)
	{
		for (int ii = 0; ii < m_lGraph.size(); ii++)
			for (int jj = ii + 1; jj < m_lGraph.size(); jj++)
		{
			int flag = 1;
			for (auto arc : m_lCrossEdge)
			{
				if (arc.first() != ii && arc.second() != ii && 
					arc.first() != jj && arc.second() != jj)
					flag = 0;
			}
			if (flag)
			{
				if (op == 1)
				{
					auto arccut1 = m_lGraph[ii], arccut2 = m_lGraph[jj];
					std::vector<GraphEdge>::iterator Iter;
					for (Iter = m_lGraph.begin(); Iter != m_lGraph.end(); Iter++)
						if ((*Iter) == arccut1)
						{
							m_lGraph.erase(Iter); break;
						}
					for (Iter = m_lGraph.begin(); Iter != m_lGraph.end(); Iter++)
						if ((*Iter) == arccut2)
						{
							m_lGraph.erase(Iter); break;
						}
				}
				return true;
			}
		}
		return false;
	}

	bool ThreeEdgeCut_Plannar(int op)
	{
		for (int ii = 0; ii < m_lGraph.size(); ii++)
			for (int jj = ii + 1; jj < m_lGraph.size(); jj++)
				for (int kk = jj + 1; kk < m_lGraph.size(); kk++)
				{
					int flag = 1;
					for (auto arc : m_lCrossEdge)
					{
						if (arc.first() != ii && arc.second() != ii &&
							arc.first() != jj && arc.second() != jj &&
							arc.first() != kk && arc.second() != kk)
							flag = 0;
					}
					if (flag)
					{
						if (op == 1)
						{
							auto arccut1 = m_lGraph[ii], arccut2 = m_lGraph[jj],arccut3 = m_lGraph[kk];
							std::vector<GraphEdge>::iterator Iter;
							for (Iter = m_lGraph.begin(); Iter != m_lGraph.end(); Iter++)
								if ((*Iter) == arccut1)
								{
									m_lGraph.erase(Iter); break;
								}
							for (Iter = m_lGraph.begin(); Iter != m_lGraph.end(); Iter++)
								if ((*Iter) == arccut2)
								{
									m_lGraph.erase(Iter); break;
								}
							for (Iter = m_lGraph.begin(); Iter != m_lGraph.end(); Iter++)
								if ((*Iter) == arccut3)
								{
									m_lGraph.erase(Iter); break;
								}
						}
						return true;
					}
				}
		return false;
	}

	int SinglePoint(int op)
	{
		std::unordered_set<int> single;
		int n = m_lSentence.size();
		single.clear();
		for (int i = 1; i <= n; i++)
			single.insert(n);
		for (auto arc : m_lGraph)
		{
			single.erase(EDGE_X(arc));
			single.erase(EDGE_Y(arc));
		}
		if (op == 0)
			return single.size();
		else if (op == 1)
		{
			int sum = 0;
			for (auto arc : single)
				if (TREENODE_LABEL(m_lTree[arc - 1]) == "punct") sum++;
			return sum;
		}
	}

	int SumPoint()
	{
		return m_lSentence.size();
	}
	
	bool IsOneEdgeInnerCross_Plannar(int out = -1)
	{
		if (out == -1)
		if (IsProjective()) return true;

		for (int i = 0; i < m_lGraph.size(); i++)
		if (i != out)
		{
			int x = -1;
			for (auto arcx : m_lCrossEdge)
				if ( (arcx.first() == i || arcx.second() == i) && arcx.first() != out && arcx.second() != out )
				{
					int y;
					GraphEdge arc1 = m_lGraph[i], arc2 = m_lGraph[arcx.first() + arcx.second() - i];
					if (IsInner(EDGE_X(arc2), EDGE_X(arc1), EDGE_Y(arc1))) y = EDGE_X(arc2);
					else y = EDGE_Y(arc2);
					if (x == -1) x = y;
					if (x != y) return 0;
				}
			if (x != -1)
			{
				int arc1x = EDGE_X(m_lGraph[i]), arc1y = EDGE_Y(m_lGraph[i]);
				if (arc1x > arc1y) std::swap(arc1x, arc1y);
				int arc1xr = arc1y, arc1yl = arc1x;
				for (auto arc2 : m_lGraph)
				{
					int arc2x = EDGE_X(arc2), arc2y = EDGE_Y(arc2);
					if (arc2x > arc2y) std::swap(arc2x, arc2y);
					if (arc2x == arc1x) arc1xr = std::min(arc1xr, arc2y);
					if (arc2y == arc1y) arc1yl = std::max(arc1yl, arc2x);
				}
				if (x != arc1xr && x != arc1yl) return 0;
			}
		}
		return 1;
	}

	bool OneEdgeCut_OneInnerCross_Plannar(int op = 0)
	{
		if (IsOneInnerCross_Plannar()) return true;

		for (int j = 0; j < m_lGraph.size(); j++)
			if (IsOneInnerCross_Plannar(j) == 1) 
				return 1;

		return 0;
	}
	
	bool OneEdgeCut_OneEdgeInnerCross_Plannar(int op = 0)
	{
		if (IsOneEdgeInnerCross_Plannar()) return true;

		for (int j = 0; j < m_lGraph.size(); j++)
			if (IsOneEdgeInnerCross_Plannar(j) == 1)
				return 1;

		return 0;
	}
	bool IsOneInnerCross_Plannar(int out = -1)
	{
		if (out == -1)
		if (IsProjective()) return true;

		for (int i = 0; i < m_lGraph.size(); i++)
		if (i != out)
		{
			int x = -1;
			for (auto arcx : m_lCrossEdge)
				if ( (arcx.first() == i || arcx.second() == i) && arcx.first() != out && arcx.second() != out)
				{
					int y;
					GraphEdge arc1 = m_lGraph[i],arc2 = m_lGraph[arcx.first() + arcx.second() - i];
					if (IsInner(EDGE_X(arc2), EDGE_X(arc1), EDGE_Y(arc1))) y = EDGE_X(arc2);
					else y = EDGE_Y(arc2);
					if (x == -1) x = y;
					if (x != y) return 0;
				}
		}
		return 1;
	}

	bool IsOneEndPoint()
	{
		if (IsProjective()) return true;
		std::set<int> CrossEdge_set;
		for (auto arcx : m_lCrossEdge)
			CrossEdge_set.insert(arcx.first()), CrossEdge_set.insert(arcx.second());
		for (auto i : CrossEdge_set)
		{
			std::set<int> x_set;
			int crosstime = 0;
			x_set.clear();
			for (auto arcx : m_lCrossEdge)
				if (arcx.first() == i || arcx.second() == i)
				{
					GraphEdge arc2 = m_lGraph[arcx.first() + arcx.second() - i];
					int y1 = EDGE_X(arc2), y2 = EDGE_Y(arc2);
					if (x_set.size() == 0)
					{
						x_set.insert(y1);
						x_set.insert(y2);
					}
					else if (x_set.size() == 1)
					{
						if (x_set.find(y1) == x_set.end() && x_set.find(y2) == x_set.end())
							return 0;
					}
					else if (x_set.size() == 2)
					{
						if (x_set.find(y1) == x_set.end() && x_set.find(y2) == x_set.end())
							return 0;
						if (x_set.find(y1) != x_set.end())
						{
							x_set.clear();
							x_set.insert(y1);
						}
						if (x_set.find(y2) != x_set.end())
						{
							x_set.clear();
							x_set.insert(y2);
						}
					}
				}			
		}
		
		return 1;
	}
	
	bool IsOneEdgeCut_OneEndPoint(int op = 0)
	{
		std::set<int> CrossEdge_set;
		for (auto arcx : m_lCrossEdge)
			CrossEdge_set.insert(arcx.first()), CrossEdge_set.insert(arcx.second());
		for (auto edge1 : CrossEdge_set )
			for (auto i : CrossEdge_set)
			{
				if (i == edge1)
					continue;
				std::set<int> x_set;
				int crosstime = 0;
				x_set.clear();
				for (auto arcx : m_lCrossEdge)
					if (arcx.first() == i || arcx.second() == i)
					{
						int crossedge = arcx.first() + arcx.second() - i;
						if (crossedge == edge1)
							continue;
						GraphEdge arc2 = m_lGraph[crossedge];
						int y1 = EDGE_X(arc2), y2 = EDGE_Y(arc2);
						if (x_set.size() == 0)
						{
							x_set.insert(y1);
							x_set.insert(y2);
						}
						else if (x_set.size() == 1)
						{
							if (x_set.find(y1) == x_set.end() && x_set.find(y2) == x_set.end())
								break;
						}
						else if (x_set.size() == 2)
						{
							if (x_set.find(y1) == x_set.end() && x_set.find(y2) == x_set.end())
								break;
							if (x_set.find(y1) != x_set.end())
							{
								x_set.clear();
								x_set.insert(y1);
							}
							if (x_set.find(y2) != x_set.end())
							{
								x_set.clear();
								x_set.insert(y2);
							}
						}
					}
				return true;
			}
		/*for (auto arcx : m_lCrossEdge) {
			GraphEdge arc1 = m_lGraph[arcx.first()], arc2 = m_lGraph[arcx.second()];
			std::cout << EDGE_X(arc1) << ' ' << EDGE_Y(arc1) << ' ' << EDGE_X(arc2) << ' ' << EDGE_Y(arc2) << std::endl;
		}
		system("pause");*/
		return false;
	}

	bool IsTwoEdgeCut_OneEndPoint(int op = 0)
	{
		std::set<int> CrossEdge_set;
		for (auto arcx : m_lCrossEdge)
			CrossEdge_set.insert(arcx.first()), CrossEdge_set.insert(arcx.second());
		for (auto edge1 : CrossEdge_set)
			for (auto edge2 : CrossEdge_set)
				for (auto i : CrossEdge_set)
				{
					if (i == edge1 || i == edge2)
						continue;
					std::set<int> x_set;
					int crosstime = 0;
					x_set.clear();
					for (auto arcx : m_lCrossEdge)
						if (arcx.first() == i || arcx.second() == i)
						{
							int crossedge = arcx.first() + arcx.second() - i;
							if (crossedge == edge1 || crossedge == edge2)
								continue;
							GraphEdge arc2 = m_lGraph[crossedge];
							int y1 = EDGE_X(arc2), y2 = EDGE_Y(arc2);
							if (x_set.size() == 0)
							{
								x_set.insert(y1);
								x_set.insert(y2);
							}
							else if (x_set.size() == 1)
							{
								if (x_set.find(y1) == x_set.end() && x_set.find(y2) == x_set.end())
									break;
							}
							else if (x_set.size() == 2)
							{
								if (x_set.find(y1) == x_set.end() && x_set.find(y2) == x_set.end())
									break;
								if (x_set.find(y1) != x_set.end())
								{
									x_set.clear();
									x_set.insert(y1);
								}
								if (x_set.find(y2) != x_set.end())
								{
									x_set.clear();
									x_set.insert(y2);
								}
							}
						}
					return true;
				}
		/*for (auto arcx : m_lCrossEdge) {
			GraphEdge arc1 = m_lGraph[arcx.first()], arc2 = m_lGraph[arcx.second()];
			std::cout << EDGE_X(arc1) << ' ' << EDGE_Y(arc1) << ' ' << EDGE_X(arc2) << ' ' << EDGE_Y(arc2) << std::endl;
		}
		system("pause");*/
		return false;
	}

	bool IsThreeEdgeCut_OneEndPoint(int op = 0)
	{
		std::set<int> CrossEdge_set;
		for (auto arcx : m_lCrossEdge)
			CrossEdge_set.insert(arcx.first()), CrossEdge_set.insert(arcx.second());
		for (auto edge1 : CrossEdge_set)
			for (auto edge2 : CrossEdge_set)
				for (auto edge3 : CrossEdge_set)
				for (auto i : CrossEdge_set)
				{
					if (i == edge1 || i == edge2 || i == edge3)
						continue;
					std::set<int> x_set;
					int crosstime = 0;
					x_set.clear();
					for (auto arcx : m_lCrossEdge)
						if (arcx.first() == i || arcx.second() == i)
						{
							int crossedge = arcx.first() + arcx.second() - i;
							if (crossedge == edge1 || crossedge == edge2 || crossedge == edge3)
								continue;
							GraphEdge arc2 = m_lGraph[crossedge];
							int y1 = EDGE_X(arc2), y2 = EDGE_Y(arc2);
							if (x_set.size() == 0)
							{
								x_set.insert(y1);
								x_set.insert(y2);
							}
							else if (x_set.size() == 1)
							{
								if (x_set.find(y1) == x_set.end() && x_set.find(y2) == x_set.end())
									break;
							}
							else if (x_set.size() == 2)
							{
								if (x_set.find(y1) == x_set.end() && x_set.find(y2) == x_set.end())
									break;
								if (x_set.find(y1) != x_set.end())
								{
									x_set.clear();
									x_set.insert(y1);
								}
								if (x_set.find(y2) != x_set.end())
								{
									x_set.clear();
									x_set.insert(y2);
								}
							}
						}
					return true;
				}
		/*for (auto arcx : m_lCrossEdge) {
			GraphEdge arc1 = m_lGraph[arcx.first()], arc2 = m_lGraph[arcx.second()];
			std::cout << EDGE_X(arc1) << ' ' << EDGE_Y(arc1) << ' ' << EDGE_X(arc2) << ' ' << EDGE_Y(arc2) << std::endl;
		}
		system("pause");*/
		return false;
	}

	friend std::ostream & operator<<(std::ostream & os, DependencyGraph& depgraph)
	{
		std::set<int> heads;
		heads.clear();
		for (const auto arc : depgraph.m_lGraph)
		{
			heads.insert(EDGE_X(arc));
			//std::cout << EDGE_X(arc) << ' ' << EDGE_Y(arc) << ' ' << EDGE_LABEL(arc) << std::endl;
		}
		//std::cout << depgraph.m_lSentence.size() << ' ' << depgraph.m_lTree.size() << std::endl;
		for (int i = 1; i <= depgraph.m_lSentence.size(); i++)
		{
			ttoken word = TREENODE_WORD(depgraph.m_lTree[i]);
			ttoken postag = TREENODE_POSTAG(depgraph.m_lTree[i]);
			os << i<< ' ' << word << ' ' << word << ' ' << postag << ' ' << postag << " _ _ _ ";
			os << TREENODE_HEAD(depgraph.m_lTree[i]) << ' ' << TREENODE_LABEL(depgraph.m_lTree[i])<<' ';
			if (heads.find(i)!=heads.end()) os << word;
			else os << '_';
			for (auto head : heads)
			{
				ttoken label = "_";
				for (auto arc : depgraph.m_lGraph)
					if (EDGE_X(arc) == head && EDGE_Y(arc) == i)
						label = EDGE_LABEL(arc);
				os << ' ' << label;
			}			
			os << std::endl;
		}
		os << std::endl;
		return os;
	}
	friend class graphparsing_1ec_n4::DepParser;
	friend class graphparsing_1ec_n5::DepParser;

};





#endif
