/** \file
 * \brief Declaration of class FaceSinkGraph
 *
 * \author Carsten Gutwenger
 *
 * \par License:
 * This file is part of the Open Graph Drawing Framework (OGDF).
 *
 * \par
 * Copyright (C)<br>
 * See README.md in the OGDF root directory for details.
 *
 * \par
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * Version 2 or 3 as published by the Free Software Foundation;
 * see the file LICENSE.txt included in the packaging of this file
 * for details.
 *
 * \par
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * \par
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, see
 * http://www.gnu.org/copyleft/gpl.html
 */

#pragma once

#include <ogdf/basic/CombinatorialEmbedding.h>
#include <ogdf/basic/Graph.h>
#include <ogdf/basic/basic.h>

namespace ogdf {
template<class E>
class List;
template<class E>
class SList;

class OGDF_EXPORT FaceSinkGraph : public Graph {
public:
	//! constructor (we assume that the original graph is connected!)
	FaceSinkGraph(const ConstCombinatorialEmbedding& E, node s);

	//! default constructor (dummy)
	FaceSinkGraph() : m_pE(nullptr) { }

	void init(const ConstCombinatorialEmbedding& E, node s);

	//! return a reference to the original graph G
	const Graph& originalGraph() const { return *m_pE; }

	//! returns a reference to the embedding E of the original graph G
	const ConstCombinatorialEmbedding& originalEmbedding() const { return *m_pE; }

	//! returns the sink-switch in G corresponding to node v in the face-sink
	//! graph, 0 if v corresponds to a face
	node originalNode(node v) const { return m_originalNode[v]; }

	//! returns the face in E corresponding to node v in the face-sink
	//! graph, 0 if v corresponds to a sink-switch
	face originalFace(node v) const { return m_originalFace[v]; }

	// returns true iff node v in the face-sink graph corresponds to a
	// face in E containing the source
	bool containsSource(node v) const { return m_containsSource[v]; }

	//! returns the list of faces f in E such that there exists an upward-planar
	//! drawing realizing E with f as external face
	//! a node v_T in tree T is returned as representative. v_T is 0 if no possible external face exists.
	node possibleExternalFaces(SList<face>& externalFaces) {
		node v_T = checkForest();
		if (v_T != nullptr) {
			gatherExternalFaces(m_T, nullptr, externalFaces);
		}
		return v_T;
	}

	node faceNodeOf(edge e) {
		return dfsFaceNodeOf(m_T, nullptr, m_pE->rightFace(e->adjSource()),
				m_pE->rightFace(e->adjTarget()));
	}

	node faceNodeOf(face f) { return dfsFaceNodeOf(m_T, nullptr, f, nullptr); }

	//! augments G to an st-planar graph (original implementation)
	/** introduces also new nodes into G corresponding to face-nodes in face sink graph)
	 */
	void stAugmentation(node h, // node corresponding to external face
			Graph& G, // original graph (not const)
			SList<node>& augmentedNodes, // list of augmented nodes
			SList<edge>& augmentedEdges); // list of augmented edges

	//! augments G to an st-planar graph
	/** (introduces only one new node as super sink into G)
	 */
	void stAugmentation(node h, // node corresponding to external face
			Graph& G, // original graph (not const)
			node& superSink, // super sink
			SList<edge>& augmentedEdges); // list of augmented edges

	//! compute the sink switches of all faces.
	// the ext. face muss be set
	void sinkSwitches(FaceArray<List<adjEntry>>& faceSwitches);

private:
	//! constructs face-sink graph
	void doInit();

	//! performs dfs-traversal and checks for backwards edges
	bool dfsCheckForest(node v, // current node
			node parent, // its parent in tree
			NodeArray<bool>& visited, // not already visited ?
			// number of internal vertices of G in current tree
			int& nInternalVertices);

	//! builds list of possible external faces
	/** all faces in tree T containing
	 * the single source s) by a dfs traversal of T
	 */
	void gatherExternalFaces(node v, // current node
			node parent, // its parent
			SList<face>& externalFaces); // returns list of possible external faces

	node dfsFaceNodeOf(node v, node parent, face f1, face f2);

	node dfsStAugmentation(node v, // current node
			node parent, // its parent
			Graph& G, // original graph (not const)
			SList<node>& augmentedNodes, // list of augmented nodes
			SList<edge>& augmentedEdges); // list of augmented edges

	node dfsStAugmentation(node v, // current node
			node parent, // its parent
			Graph& G, // original graph (not const)
			SList<edge>& augmentedEdges); // list of augmented edges


	//! associated embedding of graph G
	const ConstCombinatorialEmbedding* m_pE;
	node m_source; //!< the single source
	node m_T; //!< representative of unique tree T

	NodeArray<node> m_originalNode; //!< original node in G
	NodeArray<face> m_originalFace; //!< original face in E
	NodeArray<bool> m_containsSource; //!< contains face node the source ?

#if 0
	//! traverse the face sink tree and compute the sink witches of each internal faces
	void dfsFST(node v, //current node
		node parent, //parent of v
		FaceArray< List<adjEntry> > &faceSwitches,
		NodeArray<bool> &visited);
#endif

	//! checks if the face-sink graph is a forest with
	//!  1) there is exactly one tree T containing no internal vertex of G
	//!  2) all other trees contain exactly one internal vertex of G
	//! a node in tree T is returned as representative
	node checkForest();
};

}
