/* 
-*- coding: latin-1 -*-

This file is part of RefactorErl.

RefactorErl is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

RefactorErl 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with RefactorErl.  If not, see <http://plc.inf.elte.hu/erlang/>.

The Original Code is RefactorErl.

The Initial Developer of the Original Code is Eötvös Loránd University.
Portions created  by Eötvös Loránd University and ELTE-Soft Ltd.
are Copyright 2007-2025 Eötvös Loránd University, ELTE-Soft Ltd.
and Ericsson Hungary. All Rights Reserved.
*/

package com.refactorerl.ui.logic.dependency;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import com.ericsson.otp.erlang.OtpErlangAtom;
import com.ericsson.otp.erlang.OtpErlangBoolean;
import com.ericsson.otp.erlang.OtpErlangObject;
import com.ericsson.otp.erlang.OtpErlangString;
import com.refactorerl.ui.common.OtpErlangHelper;
import com.refactorerl.ui.common.Pair;
import com.refactorerl.ui.communication.ReferlProxy;
import com.refactorerl.ui.communication.exceptions.CommunicationException;
import com.refactorerl.ui.communication.exceptions.ConnectionException;
import com.refactorerl.ui.communication.exceptions.RequestException;
import com.refactorerl.ui.logic.DataDirRequest;

/**
 * This class holds the factory methods for creating instances of
 * DependencyRequest class.
 * 
 * @author Daniel Lukacs, 2014 ELTE IK
 *
 */
public class DependencyRequestFactory {

	public enum DepLevel {
		MODULE, FUNCTION, MODULE_BLOCK
	}

	public enum DepType {
		ALL, CYCLIC
	}

	public enum DepOutputFormat {
		DOT, HTML, ERL
	}

	/**
	 * Creates a DependencyRequest parameterized to draw module block dependency
	 * graph in SVG format. This method accepts a list of starting directory
	 * paths as parameter. This method will execute a DataDirRequest to create
	 * the full path of the file which will represent the dependency graph on
	 * the server.
	 * 
	 * @param fileName
	 *            The string of the desired filename without extension.
	 * @return The created DependencyRequest and the full remote path to the
	 *         file which will represent the dependency graph on the server
	 *         after executing the request.
	 */
	public static Pair<DependencyRequest, String> createSVGDrawModuleBlockRequest(
			DepType type, String fileName, List<String> starts,
			ReferlProxy proxy, Logger logger) throws RequestException,
			ConnectionException, CommunicationException {
		String requestName = "draw_dep_graph";

		String filePathWithExtension = concatDataDirFileExt(fileName, "dot",
				proxy);
		Map<String, OtpErlangObject> props = new HashMap<String, OtpErlangObject>();
		props.put("level", new OtpErlangAtom("mb"));
		props.put("file_path", new OtpErlangString(filePathWithExtension)); //$NON-NLS-1$


		if (type == DepType.ALL) {
			props.put("type", new OtpErlangAtom("all")); //$NON-NLS-1$
		} else if (type == DepType.CYCLIC) {
			props.put("type", new OtpErlangAtom("cycles")); //$NON-NLS-1$
		}

		OtpErlangObject[] otpStarts = new OtpErlangObject[starts.size()];
		for (int i = 0; i < starts.size(); i++) {
			otpStarts[i] = new OtpErlangString(starts.get(i));
		}
		props.put("groups", OtpErlangHelper.createList(otpStarts)); //$NON-NLS-1$

		return new Pair<>(new DependencyRequest(requestName,
				OtpErlangHelper.createPropList(props), proxy, logger),
				filePathWithExtension);
	}

	/**
	 * Creates a DependencyRequest parameterized to draw module block dependency
	 * graph in SVG format. This methods accepts a regular expression as
	 * parameter. This method will execute a DataDirRequest to create the full
	 * path of the file which will represent the dependency graph on the server.
	 * 
	 * @param fileName
	 *            The string of the desired filename without extension.
	 * @return The created DependencyRequest and the full remote path to the
	 *         file which will represent the dependency graph on the server
	 *         after executing the request.
	 */
	public static Pair<DependencyRequest, String> createSVGDrawModuleBlockRequest(
			DepType type, String fileName, String regexp, ReferlProxy proxy,
			Logger logger) throws RequestException, ConnectionException,
			CommunicationException {

		String requestName = "draw_dep_graph"; //$NON-NLS-1$

		String filePathWithExtension = concatDataDirFileExt(fileName, "dot",
				proxy);
		Map<String, OtpErlangObject> props = new HashMap<String, OtpErlangObject>();
		props.put("level", new OtpErlangAtom("mb"));
		props.put("file_path", new OtpErlangString(filePathWithExtension)); //$NON-NLS-1$
		props.put("groups", OtpErlangHelper.createList(new OtpErlangString(regexp))); //$NON-NLS-1$

		return new Pair<>(new DependencyRequest(requestName,
				OtpErlangHelper.createPropList(props), proxy, logger),
				filePathWithExtension);
	}

	/**
	 * Creates a DependencyRequest parameterized to draw module dependency graph
	 * in HTML format. This method will execute a DataDirRequest to create the
	 * full path of the file which will represent the dependency graph on the
	 * server.
	 * 
	 * @param fileName
	 *            The string of the desired filename without extension.
	 * @return The created DependencyRequest and the full remote path to the
	 *         file which will represent the dependency graph on the server
	 *         after executing the request.
	 */
	public static Pair<DependencyRequest, String> createHTMLDrawModuleRequest(
			DepType type, String fileName, boolean excludeOtp,
			List<String> starts, List<String> excludes,
			List<String> excludeLeaves, ReferlProxy proxy, Logger logger)
			throws RequestException, ConnectionException,
			CommunicationException {

		String filePath = concatDataDirFileExt(fileName, "html", proxy);

		Map<String, OtpErlangObject> props = new HashMap<>();
		Map<String, OtpErlangObject> depOpts = createProps(filePath,
				excludeOtp, starts, excludes, excludeLeaves);
		depOpts.put("level", new OtpErlangAtom("mod")); //$NON-NLS-1$ //$NON-NLS-2$
		props.put("dependency_level", new OtpErlangAtom("mod")); //$NON-NLS-1$ //$NON-NLS-2$

		return new Pair<>(createHTMLDrawRequest(type, filePath, proxy, logger,
				props, depOpts), filePath);

	}

	/**
	 * Creates a DependencyRequest parameterized to draw function dependency
	 * graph in HTML format. This method will execute a DataDirRequest to create
	 * the full path of the file which will represent the dependency graph on
	 * the server.
	 * 
	 * @param fileName
	 *            The string of the desired filename without extension.
	 * @return The created DependencyRequest and the full remote path to the
	 *         file which will represent the dependency graph on the server
	 *         after executing the request.
	 */
	public static Pair<DependencyRequest, String> createHTMLDrawFunctionRequest(
			DepType type, String fileName, boolean excludeOtp,
			List<String> starts, List<String> excludes,
			List<String> excludeLeaves, ReferlProxy proxy, Logger logger)
			throws RequestException, ConnectionException,
			CommunicationException {

		String filePath = concatDataDirFileExt(fileName, "html", proxy);

		Map<String, OtpErlangObject> props = new HashMap<>();
		Map<String, OtpErlangObject> depOpts = createProps(filePath,
				excludeOtp, starts, excludes, excludeLeaves);
		depOpts.put("level", new OtpErlangAtom("func")); //$NON-NLS-1$ //$NON-NLS-2$
		props.put("dependency_level", new OtpErlangAtom("func")); //$NON-NLS-1$ //$NON-NLS-2$

		return new Pair<>(createHTMLDrawRequest(type, filePath, proxy, logger,
				props, depOpts), filePath);

	}

	private static DependencyRequest createHTMLDrawRequest(DepType type,
			String filePathwithExtension, ReferlProxy proxy, Logger logger,
			Map<String, OtpErlangObject> props,
			Map<String, OtpErlangObject> depOpts) {
		String requestName = "generate_smart_graph"; //$NON-NLS-1$

		depOpts.put(
				"type", new OtpErlangAtom(type == DepType.CYCLIC ? "cyclic" : "all")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		depOpts.remove("file_path"); //$NON-NLS-1$

		props.put("output_type", new OtpErlangAtom("js_with_html")); //$NON-NLS-1$ //$NON-NLS-2$
		props.put("output_path", new OtpErlangString(filePathwithExtension)); //$NON-NLS-1$
		props.put("dependency_options", OtpErlangHelper.createPropList(depOpts)); //$NON-NLS-1$

		return new DependencyRequest(requestName,
				OtpErlangHelper.createPropList(props), proxy, logger);
	}

	/**
	 * Creates a DependencyRequest parameterized to draw module dependency graph
	 * in SVG format. This method will execute a DataDirRequest to create the
	 * full path of the file which will represent the dependency graph on the
	 * server.
	 * 
	 * @param fileName
	 *            The string of the desired filename without extension.
	 * @return The created DependencyRequest and the full remote path to the
	 *         file which will represent the dependency graph on the server
	 *         after executing the request.
	 */
	public static Pair<DependencyRequest, String> createSVGDrawModuleRequest(
			DepType type, String fileName, boolean excludeOtp,
			List<String> starts, List<String> excludes,
			List<String> excludeLeaves, ReferlProxy proxy, Logger logger)
			throws RequestException, ConnectionException,
			CommunicationException {

		String filePath = concatDataDirFileExt(fileName, "dot", proxy);

		String requestName = "draw_dep_graph"; //$NON-NLS-1$
		Map<String, OtpErlangObject> props = createProps(filePath, excludeOtp,
				starts, excludes, excludeLeaves);
		
		props.put("level", new OtpErlangAtom("mod"));
		// TODO create a dictionary for types, to use like: props.put("type",
				// typeDict.get(type));
		if (type == DepType.ALL) {
			props.put("type", new OtpErlangAtom("all")); //$NON-NLS-1$
		} else if (type == DepType.CYCLIC) {
			props.put("type", new OtpErlangAtom("cycles")); //$NON-NLS-1$
		}

		return new Pair<>(new DependencyRequest(requestName,
				OtpErlangHelper.createPropList(props), proxy, logger), filePath);
	}

	/**
	 * Creates a DependencyRequest parameterized to draw function dependency
	 * graph in SVG format. This method will execute a DataDirRequest to create
	 * the full path of the file which will represent the dependency graph on
	 * the server.
	 * 
	 * @param fileName
	 *            The string of the desired filename without extension.
	 * @return The created DependencyRequest and the full remote path to the
	 *         file which will represent the dependency graph on the server
	 *         after executing the request.
	 */
	public static Pair<DependencyRequest, String> createSVGDrawFunctionRequest(
			DepType type, String fileName, boolean excludeOtp,
			List<String> starts, List<String> excludes,
			List<String> excludeLeaves, ReferlProxy proxy, Logger logger)
			throws RequestException, ConnectionException,
			CommunicationException {

		String filePath = concatDataDirFileExt(fileName, "dot", proxy);

		String requestName = "draw_dep_graph"; //$NON-NLS-1$
		Map<String, OtpErlangObject> props = createProps(filePath, excludeOtp,
				starts, excludes, excludeLeaves);
		props.put("level", new OtpErlangAtom("mod"));
		// TODO create a dictionary for types, to use like: props.put("type",
				// typeDict.get(type));
		if (type == DepType.ALL) {
			props.put("type", new OtpErlangAtom("all")); //$NON-NLS-1$
		} else if (type == DepType.CYCLIC) {
			props.put("type", new OtpErlangAtom("cycles")); //$NON-NLS-1$
		}

		return new Pair<>(new DependencyRequest(requestName,
				OtpErlangHelper.createPropList(props), proxy, logger), filePath);
	}

	private static Map<String, OtpErlangObject> createProps(
			String filePathWithExtension, boolean excludeOtp,
			List<String> starts, List<String> excludes,
			List<String> excludeLeaves) {
		Map<String, OtpErlangObject> props = new HashMap<String, OtpErlangObject>();

		props.put("file_path", new OtpErlangString(filePathWithExtension)); //$NON-NLS-1$
		props.put("exclude_otp", new OtpErlangBoolean(excludeOtp)); //$NON-NLS-1$

		OtpErlangObject[] otpStarts = new OtpErlangObject[starts.size()];
		for (int i = 0; i < starts.size(); i++) {
			otpStarts[i] = new OtpErlangString(starts.get(i));
		}
		
		OtpErlangObject[] otpExcludes = new OtpErlangObject[excludes.size()];
		for (int i = 0; i < excludes.size(); i++) {
			otpExcludes[i] = new OtpErlangString(excludes.get(i));
		}

		OtpErlangObject[] otpExcludeLeaves = new OtpErlangObject[excludeLeaves
				.size()];
		for (int i = 0; i < excludeLeaves.size(); i++) {
			otpExcludeLeaves[i] = new OtpErlangString(excludeLeaves.get(i));
		}

		props.put("starting_nodes", OtpErlangHelper.createList(otpStarts)); //$NON-NLS-1$
		props.put("exclude", OtpErlangHelper.createList(otpExcludes)); //$NON-NLS-1$
		props.put("exclude_children", OtpErlangHelper.createList(otpExcludeLeaves)); //$NON-NLS-1$
		return props;
	}

	/**
	 * Creates a PrintDependencyRequest parameterized to draw module dependency
	 * graph in text format. No remote file will be created on the server, the
	 * execute method of PrintDependencyRequest will return the text
	 * representation of the dependency graph.
	 */
	public static PrintDependencyRequest createPrintModuleRequest(
			List<String> modules, ReferlProxy proxy, Logger logger) {

		Map<String, OtpErlangObject> props = new HashMap<String, OtpErlangObject>();
		props.put("level", new OtpErlangAtom("mod"));
		fillPrintProps(modules, props);	
		
		return new PrintDependencyRequest("draw_dep_graph", OtpErlangHelper.createPropList(props), proxy, logger);  //$NON-NLS-1$
	}

	/**
	 * Creates a PrintDependencyRequest parameterized to draw module dependency
	 * graph in text format. No remote file will be created on the server, the
	 * execute method of PrintDependencyRequest will return the text
	 * representation of the dependency graph.
	 */
	public static PrintDependencyRequest createPrintFunctionRequest(
			List<String> functions, ReferlProxy proxy, Logger logger) {

		Map<String, OtpErlangObject> props = new HashMap<String, OtpErlangObject>();
		props.put("level", new OtpErlangAtom("func"));
		fillPrintProps(functions, props);	
		
		return new PrintDependencyRequest("draw_dep_graph", OtpErlangHelper.createPropList(props), proxy, logger);  //$NON-NLS-1$
	}

	private static void fillPrintProps(List<String> starts,
			Map<String, OtpErlangObject> props) {
		props.put("type", new OtpErlangAtom("all"));
		props.put("output", new OtpErlangAtom("name_terms"));
		
		
		OtpErlangObject[] otpStarts = new OtpErlangObject[starts.size()];
		for (int i = 0; i < starts.size(); i++) {
			otpStarts[i] = new OtpErlangString(starts.get(i));
		}
		props.put("starting_nodes", OtpErlangHelper.createList(otpStarts));
	}

	private static String concatDataDirFileExt(String fileName,
			String extension, ReferlProxy proxy) throws RequestException,
			ConnectionException, CommunicationException {
		String dataDirPath = new DataDirRequest(proxy, null).execute();
		// FIXME File.separator is the sep of the local system, 
		// here we need the sep of the remote system.
		// maybe we can analyse what kind of separators are in
		// dataDir
		// (/ or
		// \\)
		return dataDirPath + File.separator + fileName + "." + extension;
	}

}
