package de.visone.nlpext.deprecated;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Comparator;

import org.apache.log4j.Logger;

import y.base.Edge;
import y.base.EdgeCursor;
import y.base.EdgeList;
import y.base.Node;
import y.base.NodeCursor;
import y.view.EdgeRealizer;
import de.visone.attributes.AttributeInterface;
import de.visone.attributes.AttributeManager;
import de.visone.attributes.AttributeStructure.AttributeType;
import de.visone.base.Mediator;
import de.visone.base.Network;
import de.visone.gui.util.LineStroke;
import de.visone.io.AttributeIOHandler;
import de.visone.io.ImageOutputHandler;
import de.visone.io.ImageOutputHandler.FileFormat;
import de.visone.util.ConfigurationManager;

/**
 * Utilities for graph IO and modification.
 * 
 * @author <a href="mailto:endele@inf.uni-konstanz.de">Simon Endele</a>
 * @author matthias
 */
public class NetworkUtil extends de.visone.ext.NetworkUtil {
	private static final ImageOutputHandler PDF_IO_HANDLER = new ImageOutputHandler(
			FileFormat.PDF);
	private static final ImageOutputHandler SVG_IO_HANDLER = new ImageOutputHandler(
			FileFormat.SVG);
	private static final AttributeIOHandler<Node> ATTRIBUTE_IO_HANDLER = new AttributeIOHandler<Node>();

	public static final String NAME_ATTRIBUTE = AttributeManager.NAME_ATTRIBUTE;
	public static final File VISONE_CONFIG_DIR = new File(
			ConfigurationManager.CONFIG_DIR);

	private static final Logger logger = Logger.getLogger(NetworkUtil.class);

	private NetworkUtil() {
	}

	/**
	 * Merges all multi-edges to a single edge with its width representing the
	 * multiplicity.
	 */
	public static void simplifyEdges(final Network network) {
		final EdgeComparator edgeComparator = new EdgeComparator();
		final EdgeList list = new EdgeList(network.getGraph2D().edges());
		list.sort(edgeComparator);

		Edge lastEdge = null;
		for (final EdgeCursor ec = list.edges(); ec.ok(); ec.next()) {
			final Edge edge = ec.edge();
			if (lastEdge == null || edgeComparator.compare(lastEdge, edge) != 0) {
				lastEdge = edge;
			} else {
				network.getGraph2D().removeEdge(edge);
				final EdgeRealizer realizer = network.getGraph2D().getRealizer(
						lastEdge);
				LineStroke.setLineWidth(realizer, realizer.getLineType()
						.getLineWidth() + 1);
			}
		}
	}

	/**
	 * Removes all nodes from the network, whose centrality is less than
	 * minValue (or equal if pruneAlsoEqualCheckBox is checked).
	 */
	public static void removeNodes(final Network network,
			final String centralityName, final double minValue,
			final boolean pruneEqual) {
		final AttributeInterface<Node> centrality = network
				.getNodeAttributeManager().getAttribute(centralityName);
		for (final NodeCursor nc = network.getGraph2D().nodes(); nc.ok(); nc
				.next()) {
			final Node node = nc.node();
			final double value = centrality.getDouble(node);
			if (value < minValue || pruneEqual && value == minValue)
				network.remove(node);
		}
	}

	/** Compares two edges by comparing the indices of their incident nodes. */
	private static class EdgeComparator implements Comparator<Object> {
		@Override
		public int compare(final Object o1, final Object o2) {
			if (!(o1 instanceof Edge) || !(o2 instanceof Edge)) {
				return 0;
			} else {
				final Edge e1 = (Edge) o1;
				final Edge e2 = (Edge) o2;
				final int min1 = Math.min(e1.source().index(), e1.target()
						.index());
				final int max1 = Math.max(e1.source().index(), e1.target()
						.index());
				final int min2 = Math.min(e2.source().index(), e2.target()
						.index());
				final int max2 = Math.max(e2.source().index(), e2.target()
						.index());
				if (min2 - min1 != 0) {
					return min2 - min1;
				} else {
					return max2 - max1;
				}
			}
		}

		@Override
		public boolean equals(final Object obj) {
			return false;
		}
	}

	/**
	 * Loads a network from a file, either graphml or anything else that visone
	 * can read.
	 * 
	 * @param file
	 *            source file
	 * @return the new network
	 */
	public static Network loadNetwork(final File file) throws Exception {
		final Network network = Mediator.getInstance().getIOManager()
				.read(file, true);
		logger.info("Loaded network: n=" + network.getGraph2D().N() + ", m="
				+ network.getGraph2D().E());
		return network;
	}

	/**
	 * Imports attribute values from CSV, sorted by ID. The attribute names are
	 * taken from the CSV file.
	 * 
	 * @param network
	 *            network to import attributes into
	 * @param input
	 *            source file
	 * @param attrName
	 *            attribute used to identify the nodes, must exist in both CSV
	 *            and the network
	 */
	public static void loadAttribute(final Network network, final File input,
			final String attrName) throws IOException {
		final AttributeManager<Node> nodeAttrManager = network
				.getNodeAttributeManager();
		ATTRIBUTE_IO_HANDLER.setSortByAttribute(attrName);
		ATTRIBUTE_IO_HANDLER.getInputOptions().setFile(input);
		ATTRIBUTE_IO_HANDLER.read(new FileInputStream(input), nodeAttrManager);
	}

	/**
	 * Exports all attributes to a file.
	 * 
	 * @param network
	 *            network whose attributes to export
	 * @param output
	 *            target file
	 */
	public static void saveAttributes(final Network network, final File output)
			throws IOException {
		ATTRIBUTE_IO_HANDLER.write(new FileOutputStream(output),
				network.getNodeAttributeManager());
	}

	/**
	 * Exports a network as a PDF document.
	 * 
	 * @param network
	 *            network to export
	 * @param output
	 *            target file
	 */
	public static void exportAsPDF(final Network network, final File output)
			throws IOException {
		logger.info("Saving network...");
		final FileOutputStream out = new FileOutputStream(output);
		PDF_IO_HANDLER.getYHandler().write(network.getGraph2D(), out);
		logger.info("Network saved to " + output + ".");
	}
}
