/***************************************************************************
 * Copyright 2014 Kieker Project (http://kieker-monitoring.net)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ***************************************************************************/

package kieker.webgui.service.impl;

import kieker.webgui.common.exception.GraphLayoutException;
import kieker.webgui.service.IGraphLayoutService;
import kieker.webgui.service.impl.utility.KielerInterface;
import kieker.webgui.service.impl.utility.converter.GraphStringConverter;
import kieker.webgui.service.impl.utility.converter.ResultStringConverter;
import kieker.webgui.service.impl.utility.model.Graph;

import de.cau.cs.kieler.klay.layered.LayeredLayoutProvider;

import org.springframework.stereotype.Service;

/**
 * This class provides a single method to perform an auto layout on a given
 * graph from the GraphFlow. The JavaScript GraphFlow throws an
 * "autoLayout"-Event upon using the function autoLayout(). This event provides
 * two Strings which may be used by this class' layoutGraph(nodes, edges)
 * method. The return value can then be used as an argument for the GraphFlow's
 * loadPositions(positions) function.
 * 
 * @author Robin Weiss, Nils Christian Ehmke, Florian Fittkau
 */
@Service
public final class KielerGraphLayoutServiceImpl implements IGraphLayoutService {

	/**
	 * Default constructor.
	 */
	public KielerGraphLayoutServiceImpl() {
		// No code necessary
	}

	/**
	 * Takes two input strings and generates a {@link Graph} using the given information.
	 * This {@link Graph} is then layouted using a KIELER algorithm. The new positions
	 * of nodes, the bend points of edges and node dimensions are returned as a single string.
	 * 
	 * @param nodes
	 *            this string starts with a scaling factor for ports and is followed
	 *            by semicolon separated node information that is constructed like this:
	 *            "<node id> <parent-node id (unsupported)> <width> <height> <"f" if node has ports, else "c">
	 *            <number of input ports> <vertical space between topmost input port and top border of node>
	 *            <number of repository ports> <vertical space between topmost repository port and top border of node>
	 *            <number of output ports> <vertical space between topmost output port and top border of node>;"
	 *            without the brackets and linebreaks, but with the space separation
	 * @param edges
	 *            a semicolon separated string. Each semicolon separated part is constructed like this:
	 *            "<source node id> <source port index> <target node id> <target port index>;"
	 *            without the brackets but with the space separation
	 * @return
	 *         a result string, starting with "autolayout#" followed by semicolon separated node information,
	 *         that looks like this: "<node id> <x-coordinate> <y-coordinate> <width> <height>;",
	 *         followed by a "#" and semicolon separated edge information, that look loke this:
	 *         "<source node id> <source port index> <target node id> <target port index>
	 *         <bend point 1 x-coord> <bend point 1 y-coord> ... <bend point N x-coord> <bend point N y-coord>;"
	 *         for N bend points.
	 * 
	 * @throws GraphLayoutException
	 *             If something went wrong during the layouting.
	 */
	@Override
	public String layoutGraph(final String nodes, final String edges) throws GraphLayoutException {
		final Graph graph = KielerGraphLayoutServiceImpl.layoutGraph(GraphStringConverter.convert(nodes, edges));

		return ResultStringConverter.convertLayoutedGraphToString(graph);
	}

	/**
	 * Applies the KIELER layout to the {@link Graph}.
	 * 
	 * @param graph
	 *            the {@link Graph} that needs to be layouted
	 * @return
	 *         the layouted {@link Graph}
	 * @throws GraphLayoutException
	 */
	private static Graph layoutGraph(final Graph graph) throws GraphLayoutException {
		KielerInterface.applyLayout(graph, new LayeredLayoutProvider());
		return graph;
	}

}
