/** \file
 * \brief Offers variety of possible SimDraw creations.
 *
 * \author Michael Schulz and Daniel Lueckerath
 *
 * \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
 */

#include <ogdf/basic/Array.h>
#include <ogdf/basic/Array2D.h>
#include <ogdf/basic/Graph.h>
#include <ogdf/basic/GraphAttributes.h>
#include <ogdf/basic/basic.h>
#include <ogdf/simultaneous/SimDrawCreatorSimple.h>

namespace ogdf {

// creates simultaneous graph given by two trees with n^2+n+1 nodes
// see Geyer,Kaufmann,Vrto (GD'05) for description of graph
void SimDrawCreatorSimple::createTrees_GKV05(int n) {
	OGDF_ASSERT(n >= 1);

	node v0 = m_G->newNode();
	Array<node> v(n);
	Array2D<node> w(0, n, 0, n);
	edge e;

	for (int i = 0; i < n; i++) {
		v[i] = m_G->newNode();
		for (int j = 0; j < n; j++) {
			if (i != j) {
				w(i, j) = m_G->newNode();
			}
		}
	}

	for (int i = 0; i < n; i++) {
		e = m_G->newEdge(v0, v[i]);
		m_GA->addSubGraph(e, 0);
		m_GA->addSubGraph(e, 1);
		for (int j = 0; j < n; j++) {
			if (i != j) {
				e = m_G->newEdge(v[i], w(i, j));
				m_GA->addSubGraph(e, 0);
				e = m_G->newEdge(v[j], w(i, j));
				m_GA->addSubGraph(e, 1);
			}
		}
	}
}

// creates simultaneous graph given by a path and a planar graph
// see Erten, Kobourov (GD'04) for description of instance
void SimDrawCreatorSimple::createPathPlanar_EK04() {
	node v[10];
	edge e;

	for (int i = 1; i < 10; i++) {
		v[i] = m_G->newNode();
	}

	e = m_G->newEdge(v[1], v[2]);
	m_GA->addSubGraph(e, 0);

	e = m_G->newEdge(v[1], v[3]);
	m_GA->addSubGraph(e, 0);
	m_GA->addSubGraph(e, 1);

	e = m_G->newEdge(v[1], v[4]);
	m_GA->addSubGraph(e, 0);

	e = m_G->newEdge(v[1], v[5]);
	m_GA->addSubGraph(e, 0);

	e = m_G->newEdge(v[1], v[6]);
	m_GA->addSubGraph(e, 0);

	e = m_G->newEdge(v[2], v[3]);
	m_GA->addSubGraph(e, 0);

	e = m_G->newEdge(v[2], v[4]);
	m_GA->addSubGraph(e, 1);

	e = m_G->newEdge(v[2], v[5]);
	m_GA->addSubGraph(e, 0);

	e = m_G->newEdge(v[2], v[6]);
	m_GA->addSubGraph(e, 0);

	e = m_G->newEdge(v[2], v[7]);
	m_GA->addSubGraph(e, 0);
	m_GA->addSubGraph(e, 1);

	e = m_G->newEdge(v[2], v[8]);
	m_GA->addSubGraph(e, 0);

	e = m_G->newEdge(v[2], v[9]);
	m_GA->addSubGraph(e, 0);

	e = m_G->newEdge(v[3], v[4]);
	m_GA->addSubGraph(e, 0);

	e = m_G->newEdge(v[3], v[5]);
	m_GA->addSubGraph(e, 0);
	m_GA->addSubGraph(e, 1);

	e = m_G->newEdge(v[4], v[5]);
	m_GA->addSubGraph(e, 0);
	m_GA->addSubGraph(e, 1);

	e = m_G->newEdge(v[5], v[6]);
	m_GA->addSubGraph(e, 0);

	e = m_G->newEdge(v[5], v[9]);
	m_GA->addSubGraph(e, 0);

	e = m_G->newEdge(v[6], v[7]);
	m_GA->addSubGraph(e, 0);

	e = m_G->newEdge(v[6], v[9]);
	m_GA->addSubGraph(e, 0);

	e = m_G->newEdge(v[6], v[8]);
	m_GA->addSubGraph(e, 1);

	e = m_G->newEdge(v[7], v[8]);
	m_GA->addSubGraph(e, 0);

	e = m_G->newEdge(v[7], v[9]);
	m_GA->addSubGraph(e, 0);
	m_GA->addSubGraph(e, 1);

	e = m_G->newEdge(v[8], v[9]);
	m_GA->addSubGraph(e, 0);
	m_GA->addSubGraph(e, 1);
}

// creates simultaneous graph given by a colored K5
// see Erten, Kobourov (GD'04) for description of instance
void SimDrawCreatorSimple::createK5_EK04() {
	int number = 5;
	Array<node> v(number);
	edge e;

	for (int i = 0; i < number; i++) {
		v[i] = m_G->newNode();
	}

	for (int i = 0; i < number - 1; i++) {
		for (int j = i + 1; j < number; j++) {
			e = m_G->newEdge(v[i], v[j]);
			if ((j == i + 1) || ((j == number - 1) && (i == 0))) {
				m_GA->addSubGraph(e, 0);
			} else {
				m_GA->addSubGraph(e, 1);
			}
		}
	}
}

// creates simultaneous graph given by a colored K5
// see Gassner, Juenger, Percan, Schaefer, Schulz (WG'06) for description of instance
void SimDrawCreatorSimple::createK5_GJPSS06() {
	int number = 5;
	Array<node> v(number);
	edge e;

	for (int i = 0; i < number; i++) {
		v[i] = m_G->newNode();
	}

	for (int i = 0; i < 3; i++) {
		for (int j = i + 1; j <= 2; j++) {
			e = m_G->newEdge(v[i], v[j]);
			m_GA->addSubGraph(e, 0);
			m_GA->addSubGraph(e, 1);
		}
	}

	for (int i = 3; i < number; i++) {
		for (int j = 0; j < i; j++) {
			e = m_G->newEdge(v[i], v[j]);
			if (j == 3) {
				m_GA->addSubGraph(e, 0);
			} else {
				m_GA->addSubGraph(e, 1);
			}
		}
	}
}

// creates simultaneous graph given by two outerplanar graphs
// see Brass et al. (WADS '03) for description of instance
void SimDrawCreatorSimple::createOuterplanar_BCDEEIKLM03() {
	int number = 6;
	Array<node> v(number);
	edge e;

	for (int i = 0; i < number; i++) {
		v[i] = m_G->newNode();
	}

	for (int i = 0; i < number - 1; i++) {
		e = m_G->newEdge(v[i], v[i + 1]);
		if (!(i == 2)) {
			m_GA->addSubGraph(e, 0);
			m_GA->addSubGraph(e, 1);
		} else {
			m_GA->addSubGraph(e, 0);

			e = m_G->newEdge(v[i], v[number - 1]);
			m_GA->addSubGraph(e, 1);
		}
	}
	e = m_G->newEdge(v[number - 1], v[0]);
	m_GA->addSubGraph(e, 0);

	e = m_G->newEdge(v[0], v[3]);
	m_GA->addSubGraph(e, 1);

	e = m_G->newEdge(v[1], v[4]);
	m_GA->addSubGraph(e, 0);
	m_GA->addSubGraph(e, 1);
}

// creates simultaneous graph with crossing number 0 but
// with multicrossings of adjacent edges in mincross drawing
// see Kratochvil (GD '98) for description of instance
void SimDrawCreatorSimple::createKrat98(int N, int nodeNumber) {
	OGDF_ASSERT(N >= 1);
	OGDF_ASSERT(nodeNumber >= 1);

	Array<node> p(nodeNumber);
	Array<node> q(nodeNumber);
	Array<node> r(nodeNumber);
	Array<node> outerNodes(4);
	Array<node> outerOuterNodes(4);
	node a = m_G->newNode();
	node b = m_G->newNode();
	node nodeC = m_G->newNode();
	edge e;

	for (int i = 0; i < nodeNumber; i++) {
		p[i] = m_G->newNode();
		q[i] = m_G->newNode();
		r[i] = m_G->newNode();
	}

	for (int i = 0; i < 4; i++) {
		outerNodes[i] = m_G->newNode();
		outerOuterNodes[i] = m_G->newNode();
	}

	if (N > 1) {
		Array<node> c(N);
		for (int i = 0; i < N; i++) {
			c[i] = m_G->newNode();

			e = m_G->newEdge(c[i], nodeC);
			m_GA->addSubGraph(e, 1);

			e = m_G->newEdge(a, c[i]);
			m_GA->addSubGraph(e, 1);

			//eventuell unnoetig?
			e = m_G->newEdge(outerNodes[1], c[i]);
			m_GA->addSubGraph(e, 1);
		}
	} else {
		e = m_G->newEdge(a, nodeC);
		m_GA->addSubGraph(e, 1);
	}

	e = m_G->newEdge(a, b);
	m_GA->addSubGraph(e, 0);

	e = m_G->newEdge(b, nodeC);
	m_GA->addSubGraph(e, 0);
	m_GA->addSubGraph(e, 1);
	m_GA->addSubGraph(e, 2);

	for (int i = 0; i < nodeNumber - 1; i++) {
		e = m_G->newEdge(p[i], p[i + 1]);
		m_GA->addSubGraph(e, 0);
		m_GA->addSubGraph(e, 1);
		m_GA->addSubGraph(e, 2);

		e = m_G->newEdge(r[i], r[i + 1]);
		m_GA->addSubGraph(e, 0);
		m_GA->addSubGraph(e, 1);
		m_GA->addSubGraph(e, 2);
	}

	for (int i = 0; i < nodeNumber; i++) {
		e = m_G->newEdge(p[i], q[i]);
		m_GA->addSubGraph(e, 2);
		if (i % 2) {
			m_GA->addSubGraph(e, 0);
		} else {
			m_GA->addSubGraph(e, 1);
		}

		e = m_G->newEdge(r[i], q[i]);
		m_GA->addSubGraph(e, 2);
		if (i % 2) {
			m_GA->addSubGraph(e, 1);
		} else {
			m_GA->addSubGraph(e, 0);
		}
	}

	e = m_G->newEdge(a, p[0]);
	m_GA->addSubGraph(e, 0);
	m_GA->addSubGraph(e, 1);
	m_GA->addSubGraph(e, 2);

	e = m_G->newEdge(a, r[0]);
	m_GA->addSubGraph(e, 0);
	m_GA->addSubGraph(e, 1);
	m_GA->addSubGraph(e, 2);

	e = m_G->newEdge(r[nodeNumber - 1], b);
	m_GA->addSubGraph(e, 0);
	m_GA->addSubGraph(e, 1);
	m_GA->addSubGraph(e, 2);

	e = m_G->newEdge(p[nodeNumber - 1], nodeC);
	m_GA->addSubGraph(e, 0);
	m_GA->addSubGraph(e, 1);
	m_GA->addSubGraph(e, 2);

	for (int i = 0; i < 4; i++) {
		e = m_G->newEdge(outerOuterNodes[i], outerNodes[i]);
		m_GA->addSubGraph(e, 0);
		m_GA->addSubGraph(e, 1);
		m_GA->addSubGraph(e, 2);

		if (i < 3) {
			e = m_G->newEdge(outerOuterNodes[i], outerOuterNodes[i + 1]);
			m_GA->addSubGraph(e, 0);
			m_GA->addSubGraph(e, 1);
			m_GA->addSubGraph(e, 2);
		}
	}

	e = m_G->newEdge(outerOuterNodes[3], outerOuterNodes[0]);
	m_GA->addSubGraph(e, 0);
	m_GA->addSubGraph(e, 1);
	m_GA->addSubGraph(e, 2);

	e = m_G->newEdge(outerNodes[1], outerNodes[2]);
	m_GA->addSubGraph(e, 0);
	m_GA->addSubGraph(e, 1);
	m_GA->addSubGraph(e, 2);

	e = m_G->newEdge(outerNodes[3], outerNodes[0]);
	m_GA->addSubGraph(e, 0);
	m_GA->addSubGraph(e, 1);
	m_GA->addSubGraph(e, 2);

	e = m_G->newEdge(outerNodes[0], a);
	m_GA->addSubGraph(e, 0);
	m_GA->addSubGraph(e, 1);
	m_GA->addSubGraph(e, 2);

	e = m_G->newEdge(outerNodes[3], a);
	m_GA->addSubGraph(e, 0);
	m_GA->addSubGraph(e, 1);
	m_GA->addSubGraph(e, 2);

	e = m_G->newEdge(outerNodes[1], nodeC);
	m_GA->addSubGraph(e, 0);
	m_GA->addSubGraph(e, 1);
	m_GA->addSubGraph(e, 2);

	e = m_G->newEdge(outerNodes[2], b);
	m_GA->addSubGraph(e, 0);
	m_GA->addSubGraph(e, 1);
	m_GA->addSubGraph(e, 2);
}

// creates Graph with numberofBasic*2 outer, numberOfParallels*numberOfBasic
// inner Nodes and one Root.
void SimDrawCreatorSimple::createWheel(int numberOfParallels, int numberOfBasic) {
	OGDF_ASSERT(numberOfBasic > 0);
	OGDF_ASSERT(numberOfParallels >= 0);

	node root = m_G->newNode();
	Array<node> v(numberOfBasic * 2);
	edge e;

	for (int i = 0; i < numberOfBasic * 2; i++) {
		v[i] = m_G->newNode();
		e = m_G->newEdge(root, v[i]);
		for (int j = 0; j < numberOfBasic; j++) {
			m_GA->addSubGraph(e, j);
		}
	}

	for (int i = 0; i < numberOfBasic * 2; i++) {
		if ((i >= 0) && (i < (numberOfBasic * 2) - 1)) {
			e = m_G->newEdge(v[i], v[i + 1]);
			for (int j = 0; j < numberOfBasic; j++) {
				m_GA->addSubGraph(e, j);
			}
		}
		if (i == (numberOfBasic * 2) - 1) {
			e = m_G->newEdge(v[i], v[0]);
			for (int j = 0; j < numberOfBasic; j++) {
				m_GA->addSubGraph(e, j);
			}
		}

		if ((numberOfBasic + i) < (numberOfBasic * 2)) {
			for (int j = 0; j < numberOfParallels; j++) {
				node tmpNOP = m_G->newNode();
				e = m_G->newEdge(v[i], tmpNOP);
				m_GA->addSubGraph(e, i);
				e = m_G->newEdge(v[numberOfBasic + i], tmpNOP);
				m_GA->addSubGraph(e, i);
			}
		}
	}
}

// creates simultaneously planar simulatenous graph with n+1 basic graphs.
void SimDrawCreatorSimple::createExpo(int n) {
	OGDF_ASSERT(n > 0);
	OGDF_ASSERT(n < 31);

	Array<node> u(n + 1);
	Array<node> v(n + 1);
	Array<node> twinNodesU(n + 1);
	Array<node> outerNodes(6);
	edge e;

	for (int i = 0; i < n + 1; i++) {
		u[i] = m_G->newNode();
		v[i] = m_G->newNode();
		twinNodesU[i] = m_G->newNode();
	}

	for (int i = 0; i < 6; i++) {
		outerNodes[i] = m_G->newNode();
	}

	for (int i = 1; i < 3; i++) {
		e = m_G->newEdge(outerNodes[i], outerNodes[i + 1]);
		for (int j = 0; j < 4; j++) {
			m_GA->addSubGraph(e, j);
		}
	}

	e = m_G->newEdge(outerNodes[4], outerNodes[5]);
	for (int j = 0; j < 4; j++) {
		m_GA->addSubGraph(e, j);
	}

	e = m_G->newEdge(outerNodes[5], outerNodes[0]);
	for (int j = 0; j < 4; j++) {
		m_GA->addSubGraph(e, j);
	}

	for (int i = 0; i < n + 1; i++) {
		e = m_G->newEdge(u[i], twinNodesU[i]);
		for (int j = 0; j < 4; j++) {
			m_GA->addSubGraph(e, j);
		}
	}

	for (int i = 0; i < n; i++) {
		e = m_G->newEdge(twinNodesU[i], twinNodesU[i + 1]);
		for (int j = 0; j < 4; j++) {
			m_GA->addSubGraph(e, j);
		}

		if (i == 0) {
			e = m_G->newEdge(outerNodes[3], twinNodesU[i]);
			for (int j = 0; j < 4; j++) {
				m_GA->addSubGraph(e, j);
			}
		}
	}

	e = m_G->newEdge(outerNodes[4], twinNodesU[n]);
	for (int j = 0; j < 4; j++) {
		m_GA->addSubGraph(e, j);
	}

	e = m_G->newEdge(v[0], outerNodes[0]);
	for (int j = 0; j < 4; j++) {
		m_GA->addSubGraph(e, j);
	}

	e = m_G->newEdge(v[0], outerNodes[1]);
	for (int j = 0; j < 4; j++) {
		m_GA->addSubGraph(e, j);
	}

	for (int i = 0; i < n + 1; i++) {
		e = m_G->newEdge(u[i], v[i]);
		if (i == 0) {
			m_GA->addSubGraph(e, 0);
		} else {
			m_GA->addSubGraph(e, 1);
			if (i == 1) {
				m_GA->addSubGraph(e, 2);
			}
			if (i == 2) {
				m_GA->addSubGraph(e, 3);
			}
		}
	}

	e = m_G->newEdge(outerNodes[5], u[n]);
	m_GA->addSubGraph(e, 0);
	m_GA->addSubGraph(e, 2);
	m_GA->addSubGraph(e, 3);

	e = m_G->newEdge(outerNodes[2], v[1]);
	m_GA->addSubGraph(e, 0);

	for (int i = 1; i < n + 1; i++) {
		e = m_G->newEdge(v[i], u[i - 1]);
		m_GA->addSubGraph(e, 0);
		if (i == 3) {
			m_GA->addSubGraph(e, 2);
		}
	}

	for (int i = 0; i < 2; i++) {
		e = m_G->newEdge(u[i], v[i + 2]);
		m_GA->addSubGraph(e, 0);
		m_GA->addSubGraph(e, 2);
		if (i == 1) {
			m_GA->addSubGraph(e, 3);
		}
	}

	e = m_G->newEdge(u[n - 1], u[n]);
	for (int j = 0; j < 4; j++) {
		if (j != 1) {
			m_GA->addSubGraph(e, j);
		}
	}
}

}
