/* 
-*- 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.investigations;

import java.util.ArrayList;
import java.util.logging.Logger;

import com.ericsson.otp.erlang.OtpErlangAtom;
import com.ericsson.otp.erlang.OtpErlangInt;
import com.ericsson.otp.erlang.OtpErlangObject;
import com.ericsson.otp.erlang.OtpErlangPid;
import com.ericsson.otp.erlang.OtpErlangTuple;
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.DatabaseHashRequest;
import com.refactorerl.ui.logic.GraphQueryRequest;

/**
 * This class provides factory methods for instantiating Investigation and
 * InvestigationNode objects. Beside using the supplied parameters it will query 
 * even more data needed to properly construct these objects.
 * 
 * @author Daniel Lukacs, 2014 ELTE IK
 *
 */
public class InvestigationsFactory {
	private static OtpErlangObject retrieveFunctionNode(String mod, String fun, int arity, ReferlProxy proxy,
			Logger logger) throws CommunicationException, ConnectionException, RequestException {
		OtpErlangObject m = proxy.sendRPC("reflib_module", "find", //$NON-NLS-1$ //$NON-NLS-2$
				new OtpErlangAtom(mod));
		OtpErlangObject f = proxy.sendRPC("reflib_function", "find", //$NON-NLS-1$ //$NON-NLS-2$
				new OtpErlangAtom(fun), new OtpErlangInt(arity));
		OtpErlangObject q = proxy.sendRPC("reflib_query", "seq", //$NON-NLS-1$ //$NON-NLS-2$
				OtpErlangHelper.createList(m, f));
		OtpErlangObject funNodes = new GraphQueryRequest("reflib_query", //$NON-NLS-1$
				"exec", OtpErlangHelper.createList(q), proxy, logger).execute(); //$NON-NLS-1$
		return OtpErlangHelper.nth(funNodes, 0);
	}

	private static OtpErlangObject retrieveClauseNode(OtpErlangObject functionNode, ReferlProxy proxy, Logger logger)
			throws CommunicationException, ConnectionException, RequestException {
		OtpErlangObject d = proxy.sendRPC("reflib_function", "definition"); //$NON-NLS-1$ //$NON-NLS-2$
		OtpErlangObject c = proxy.sendRPC("reflib_form", "clauses"); //$NON-NLS-1$ //$NON-NLS-2$
		OtpErlangObject q = proxy.sendRPC("reflib_query", "seq", //$NON-NLS-1$ //$NON-NLS-2$
				OtpErlangHelper.createList(d, c));
		OtpErlangObject clauseNodes = new GraphQueryRequest("reflib_query", //$NON-NLS-1$
				"exec", OtpErlangHelper.createList(functionNode, q), proxy, //$NON-NLS-1$
				logger).execute();
		return OtpErlangHelper.nth(clauseNodes, 0);
	}

	private static OtpErlangObject retrieveFunctionNode(OtpErlangObject clauseNode, ReferlProxy proxy, Logger logger)
			throws CommunicationException, ConnectionException, RequestException {
		// Q3 = reflib_query:seq(reflib_clause:form(), reflib_form:func()).
		// reflib_ui_router:request(ReqId, {graph_query, reflib_query, exec,
		// [GraphNode, Q3]}).
		OtpErlangObject f1 = proxy.sendRPC("reflib_clause", "form"); //$NON-NLS-1$ //$NON-NLS-2$
		OtpErlangObject f2 = proxy.sendRPC("reflib_form", "func"); //$NON-NLS-1$ //$NON-NLS-2$
		OtpErlangObject q = proxy.sendRPC("reflib_query", "seq", //$NON-NLS-1$ //$NON-NLS-2$
				OtpErlangHelper.createList(f1, f2));
		OtpErlangObject funNodes = new GraphQueryRequest("reflib_query", //$NON-NLS-1$
				"exec", OtpErlangHelper.createList(clauseNode, q), proxy, //$NON-NLS-1$
				logger).execute();

		return OtpErlangHelper.nth(funNodes, 0);
	}

	private static String retrieveFilePath(OtpErlangObject node, ReferlProxy proxy, Logger logger)
			throws CommunicationException, ConnectionException, RequestException {
		OtpErlangObject leaves = NodeBasedRequest.createSynLeavesRequest(node, proxy, logger).execute();
		OtpErlangObject leave = OtpErlangHelper.nth(leaves, 0);
		OtpErlangObject t = proxy.sendRPC("reflib_token_gen", "file"); //$NON-NLS-1$ //$NON-NLS-2$
		OtpErlangObject fileNodes = new GraphQueryRequest("reflib_query", //$NON-NLS-1$
				"exec", OtpErlangHelper.createList(leave, t), proxy, logger) //$NON-NLS-1$
				.execute();
		OtpErlangObject fileNode = OtpErlangHelper.nth(fileNodes, 0);
		OtpErlangObject filePathOtp = new GraphQueryRequest("reflib_file", //$NON-NLS-1$
				"path", OtpErlangHelper.createList(fileNode), proxy, logger) //$NON-NLS-1$
				.execute();
		return OtpErlangHelper.toUnquotedString(filePathOtp);
	}

	private static String retrieveItemLabel(OtpErlangObject node, ReferlProxy proxy, Logger logger)
			throws CommunicationException, ConnectionException, RequestException {

		String type = OtpErlangHelper.toUnquotedString(OtpErlangHelper.nth(node, 1));

		if (type.equals("clause")) { //$NON-NLS-1$
			OtpErlangObject funNode = retrieveFunctionNode(node, proxy, logger);
			return retrieveItemLabel(funNode, proxy, logger); // one level
																// recursion
		} else if (type.equals("func")) { //$NON-NLS-1$
			return OtpErlangHelper.toUnquotedString(NodeBasedRequest.createFunctionTextRequest(node, proxy, logger)
					.execute());
		} else {
			return new SynTreeTextRequest(node, proxy, logger).execute();
		}

	}

	public static Investigation createNewInvestigation(String investigationName, String userName, String module,
			String function, int arity, ReferlProxy proxy, Logger logger) throws CommunicationException,
			ConnectionException, RequestException {

		OtpErlangObject funNode = InvestigationsFactory.retrieveFunctionNode(module, function, arity, proxy, logger);

		OtpErlangObject graphNode = InvestigationsFactory.retrieveClauseNode(funNode, proxy, logger);

		String text = new SynTreeTextRequest(graphNode, proxy, logger).execute();
		
		String filePath = retrieveFilePath(graphNode, proxy, logger);
		String itemLabel = retrieveItemLabel(graphNode, proxy, logger);
		GetContainerRequest.Container container = new GetContainerRequest(graphNode, proxy, logger).execute();
		
		final Pair<Integer, Integer> textPosition = new Pair<Integer, Integer>(container.startPosition,
				container.endPosition);
		final Pair<Integer, Integer> screenPosition = new Pair<Integer, Integer>(0, 0);

		InvestigationNode startInvNode = new InvestigationNode(proxy.generatePid(), null,
				new ArrayList<OtpErlangPid>(), "", //$NON-NLS-1$
				true, false, graphNode, text, "root", filePath, //$NON-NLS-1$
				container.positionOffset, textPosition, container.lineNumber, itemLabel, screenPosition);

		OtpErlangTuple hash = new DatabaseHashRequest(proxy, logger).execute();
		
		Investigation inv = new Investigation(investigationName, userName, hash);
		inv.addNode(startInvNode);

		return inv;
	}

	public static InvestigationNode createNewInvestigationNode(OtpErlangObject node, OtpErlangPid parentPid,
			String label, ReferlProxy proxy, Logger logger) throws RequestException, ConnectionException,
			CommunicationException {
		GetContainerRequest.Container container = new GetContainerRequest(node, proxy, logger).execute();

		OtpErlangObject graphNode = container.containerNode;

		String text = new SynTreeTextRequest(graphNode, proxy, logger).execute();

		String itemLabel = retrieveItemLabel(graphNode, proxy, logger);
		String filePath = retrieveFilePath(graphNode, proxy, logger);
		final Pair<Integer, Integer> textPosition = new Pair<Integer, Integer>(container.startPosition,
				container.endPosition);
		final Pair<Integer, Integer> screenPosition = new Pair<Integer, Integer>(0, 0);

		InvestigationNode invNode = new InvestigationNode(proxy.generatePid(), parentPid,
				new ArrayList<OtpErlangPid>(),
				"", //$NON-NLS-1$
				true, false, graphNode, text, label, filePath, container.positionOffset, textPosition,
				container.lineNumber, itemLabel, screenPosition);

		return invNode;
	}

}
