package org.etsi.t3d.visitor;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import org.etsi.t3d.T3D;
import org.etsi.t3d.XMLPrinter;

import de.ugoe.cs.swe.trex.core.analyzer.rfparser.ASTUtil;
import de.ugoe.cs.swe.trex.core.analyzer.rfparser.LocationAST;
import de.ugoe.cs.swe.trex.core.analyzer.rfparser.TTCN3ParserTokenTypes;
import de.ugoe.cs.swe.trex.core.analyzer.rfparser.TokenWithIndex;
import de.ugoe.cs.swe.trex.core.visitor.AbstractVisitor;
import de.ugoe.cs.swe.trex.core.visitor.TTCN3BehaviorException;

import elements.*;

public class T3DVisitor extends AbstractVisitor {

	private XMLPrinter xmlPrinter;
	private String filename;

	public T3DVisitor(XMLPrinter xmlPrinter) {
		this.xmlPrinter = xmlPrinter;
	}

	@Override
	public void finish() {
	}

	@Override
	public void init() {
		xmlPrinter.getLoggingInterface().setLogSourceName(this.getFilename());
		xmlPrinter.setCurrentTTCN3File(this.getFilename());
	}

	// creates TTCN3Element instance from first child of Moduledefinition-node
	// and passes it to the XMLPrinter
	// TODO: Move content to element and pass on the module definition node
	// TODO: move calls from defNodes to moduleDefs
	// TODO: watchout for module definitions that shall be disregarded or
	// handled differently
	private void visitElementDefinition(LocationAST node) {
		if (node.getParent().getType() != TTCN3ParserTokenTypes.ModuleDefinition)
			return;
		LinkedList<LocationAST> parameterList = ASTUtil.findTypeNodes(node,
				TTCN3ParserTokenTypes.FormalValuePar);
		parameterList.addAll(ASTUtil.findTypeNodes(node,
				TTCN3ParserTokenTypes.FormalPortPar));
		parameterList.addAll(ASTUtil.findTypeNodes(node,
				TTCN3ParserTokenTypes.FormalTimerPar));
		parameterList.addAll(ASTUtil.findTypeNodes(node,
				TTCN3ParserTokenTypes.FormalTemplatePar));

		LinkedList<String> parNames = new LinkedList<String>();
		LocationAST tempAST;
		for (LocationAST parNode : parameterList) {
			tempAST = parNode.getFirstChild();
			while (tempAST.getNextSibling() != null
					&& tempAST.getType() != TTCN3ParserTokenTypes.Identifier)
				tempAST = tempAST.getNextSibling();
			parNames.add(tempAST.getFirstChild().getText()
					+ " "
					+ tempAST.getLine());
		}

		ArrayList<LocationAST> elList = LocationAST.getModuleDefinitionIdentifiersList(node.getParent());

		for (LocationAST elNode : elList) {
			String paramView = "";
			if (node.getType() == TTCN3ParserTokenTypes.ModuleParDef)
				paramView = getParamview(elNode.getFirstChild(),
						new LinkedList<String>());
			TTCN3Element element = new TTCN3Element(elNode	.getFirstChild()
															.getText(),
					LocationAST.getTTCN3ParserTokenTypeTypePrettyName(node.getType()),
					VisitorCommonFunctions.getLocationFromIdentifier(elNode.getFirstChild()),
					VisitorCommonFunctions.getBehaviour(node,
							0,
							true,
							T3D.activeProfile.isIncludeConstructBody()),
					getCommentFromNode(node),
					parNames,
					xmlPrinter.getCurrentTTCN3File(),
					paramView,
					getGroupNavigationPath(node),
					elNode.getLine());
			xmlPrinter.printElement(element, node.getLine());
		}
	}

	// returns XML representation of the group navigation path of a node
	private String getGroupNavigationPath(LocationAST node) {
		LocationAST currentNode = node.getParent();
		String path = "\n<path>";
		int groups = 0;
		while (currentNode.getParent() != null) {
			if (currentNode.getType() == TTCN3ParserTokenTypes.GroupDef) {
				path += "\n<path_group loc=\""
						+ VisitorCommonFunctions.getLocationFromIdentifier(VisitorCommonFunctions.getIdentifier(currentNode))
						+ "\" name=\""
						+ VisitorCommonFunctions.getName(currentNode)
						+ "\">";
				groups++;
			}
			currentNode = currentNode.getParent();
		}
		for (int i = 0; i < groups; i++)
			path += "</path_group>";
		return path + "</path>";
	}

	// returns XML representation of the Module Parameter View from a
	// ModuleParDef node
	private String getParamview(LocationAST node, LinkedList<String> locations) {
		String pview = "";
		LinkedList<LocationAST> refList = VisitorCommonFunctions.getReferenceList(node);
		String loc = VisitorCommonFunctions.getLocationFromIdentifier(node);
		String type = "mpview_"
				+ LocationAST.resolveParentsUntilType(node, TTCN3ParserTokenTypes.ModuleDefinition)
										.getFirstChild()
										.getText();
		pview += "\n<"
				+ type
				+ " loc=\""
				+ loc
				+ "\" name=\""
				+ VisitorCommonFunctions.getName(node)
				+ "\">";
		if (refList != null) {
			for (LocationAST n : refList) {
				LocationAST moduleDef = LocationAST.resolveParentsUntilType(n, TTCN3ParserTokenTypes.ModuleDefinition);
				if (moduleDef != null
						&& (moduleDef.getFirstChild().getType() == TTCN3ParserTokenTypes.TestcaseDef
								|| moduleDef.getFirstChild().getType() == TTCN3ParserTokenTypes.FunctionDef || moduleDef.getFirstChild()
																														.getType() == TTCN3ParserTokenTypes.AltstepDef)) {
					String loc1 = VisitorCommonFunctions.getLocationFromIdentifier(VisitorCommonFunctions.getIdentifier(moduleDef));

					if (!locations.contains(loc1)) {
						locations.add(loc1);
						pview += getParamview(VisitorCommonFunctions.getIdentifier(moduleDef),
								locations);
					}
				}
			}
		}
		pview += "</" + type + ">";

		return pview;
	}

	public ContinueStatus visitConstDef(LocationAST node)
			throws TTCN3BehaviorException {
		visitElementDefinition(node);
		return ContinueStatus.getInstance(true, true);
	}

	public ContinueStatus visitTemplateDef(LocationAST node)
			throws TTCN3BehaviorException {
		visitElementDefinition(node);
		return ContinueStatus.getInstance(true, true);
	}

	public ContinueStatus visitFunctionDef(LocationAST node)
			throws TTCN3BehaviorException {
		visitElementDefinition(node);
		return ContinueStatus.getInstance(true, true);
	}
	public ContinueStatus visitExtFunctionDef(LocationAST node)
		throws TTCN3BehaviorException {
		visitElementDefinition(node);
		return ContinueStatus.getInstance(true, true);
	}
	public ContinueStatus visitExtConstDef(LocationAST node)
		throws TTCN3BehaviorException {
		visitElementDefinition(node);
		return ContinueStatus.getInstance(true, true);
	}
	
	public ContinueStatus visitTestcaseDef(LocationAST node)
			throws TTCN3BehaviorException {
		visitElementDefinition(node);
		return ContinueStatus.getInstance(true, true);
	}

	public ContinueStatus visitAltstepDef(LocationAST node)
			throws TTCN3BehaviorException {
		visitElementDefinition(node);
		return ContinueStatus.getInstance(true, true);
	}

	public ContinueStatus visitTypeDef(LocationAST node)
			throws TTCN3BehaviorException {
		visitElementDefinition(node);
		return ContinueStatus.getInstance(true, true);
	}

	public ContinueStatus visitSignatureDef(LocationAST node)
			throws TTCN3BehaviorException {
		visitElementDefinition(node);
		return ContinueStatus.getInstance(true, true);
	}

	public ContinueStatus visitModuleParDef(LocationAST node)
			throws TTCN3BehaviorException {
		ArrayList<LocationAST> elList = LocationAST.getModuleDefinitionIdentifiersList(node.getParent());

		for (LocationAST elNode : elList) {
			String paramView = getParamview(elNode.getFirstChild(),
					new LinkedList<String>());
			TTCN3Element element = new TTCN3Element(elNode	.getFirstChild()
															.getText(),
					LocationAST.getTTCN3ParserTokenTypeTypePrettyName(node.getType()),
					VisitorCommonFunctions.getLocationFromIdentifier(elNode.getFirstChild()),
					VisitorCommonFunctions.getBehaviour(getModuleParNode(elNode),
							0,
							true,
							true),
					getCommentFromNode(node),
					new LinkedList<String>(),
					xmlPrinter.getCurrentTTCN3File(),
					paramView,
					getGroupNavigationPath(node),
					elNode.getLine());
			xmlPrinter.printElement(element, node.getLine());
		}
		return ContinueStatus.getInstance(true, true);
	}

	private LocationAST getModuleParNode(LocationAST node) {
		while (node.getType() != TTCN3ParserTokenTypes.ModulePar)
			node = node.getParent();
		return node;
	}

	public ContinueStatus visitTTCN3Module(LocationAST node)
			throws TTCN3BehaviorException {
		xmlPrinter.printModule(VisitorCommonFunctions.getName(node),
				getCommentFromNode(node),
				getModuleBehaviour(node, "module"),
				node.getLine());
		return ContinueStatus.getInstance(true, true);
	}

	public ContinueStatus visitGroupDef(LocationAST node)
			throws TTCN3BehaviorException {
		ArrayList<LocationAST> elList = LocationAST.getModuleDefinitionIdentifiersList(node.getParent());

		for (LocationAST elNode : elList) {
			xmlPrinter.printGroup(elNode.getFirstChild().getText(),
					VisitorCommonFunctions.getLocationFromIdentifier(elNode.getFirstChild()),
					getCommentFromNode(node),
					getModuleBehaviour(node, "group"),
					getGroupNavigationPath(node),
					node.getLine());
		}
		return ContinueStatus.getInstance(true, true);
	}

	// returns the XML representation(<behaviour>) of a ModuleDefinitionList
	// node
	private String getModDefListBehaviour(LocationAST node, int tabs,
			String controlPart) {
		String tabString = "";
		for (int i = 0; i < tabs; i++)
			tabString += "<tab/>";
		String b = "";
		LocationAST ModDefNode = node.getFirstChild();
		while (ModDefNode != null) {
			if (ModDefNode.getType() == TTCN3ParserTokenTypes.ModuleDefinition) {
				if (ModDefNode.getFirstChild().getType() == TTCN3ParserTokenTypes.GroupDef) {
					LocationAST modDefListNode = ModDefNode.getFirstChild();
					if (modDefListNode != null)
						modDefListNode = modDefListNode.getFirstChild();
					if (modDefListNode.getNextSibling() == null)
						b += "\n"
								+ tabString
								+ "<keyword>group</keyword> "
								+ "<link loc=\""
								+ VisitorCommonFunctions.getLocationFromIdentifier(VisitorCommonFunctions.getIdentifier(ModDefNode))
								+ "\">"
								+ VisitorCommonFunctions.getName(ModDefNode)
								+ "</link>"
								+ "<constructbody id=\"o_"
								+ ModDefNode.getOffset()
								+ "\">"
								+ " {\n"
								+ tabString
								+ "}</constructbody>\n";

					if (modDefListNode != null)
						modDefListNode = modDefListNode.getNextSibling();
					if (modDefListNode != null)
						modDefListNode = modDefListNode.getFirstChild();
					if (modDefListNode != null)
						b += "\n"
								+ tabString
								+ "<keyword>group</keyword> "
								+ "<link loc=\""
								+ VisitorCommonFunctions.getLocationFromIdentifier(VisitorCommonFunctions.getIdentifier(ModDefNode))
								+ "\">"
								+ VisitorCommonFunctions.getName(ModDefNode)
								+ "</link>"
								+ "<constructbody id=\"o_"
								+ ModDefNode.getOffset()
								+ "\">"
								+ " {\n"
								+ getModDefListBehaviour(modDefListNode,
										tabs + 1,
										"");
				} else {
					LocationAST elementDefNode = ModDefNode.getFirstChild();
					if (elementDefNode.getType() == TTCN3ParserTokenTypes.Visibility)
						elementDefNode = elementDefNode.getNextSibling();
					if (elementDefNode.getType() == TTCN3ParserTokenTypes.ModuleParDef)
						b += VisitorCommonFunctions.getBehaviour(elementDefNode,
								tabs,
								true,
								true);
					else
						b += VisitorCommonFunctions.getBehaviour(elementDefNode,
								tabs,
								true,
								T3D.activeProfile.isIncludeConstructBody());
				}
			}
			ModDefNode = ModDefNode.getNextSibling();
		}
		return b
				+ controlPart
				+ "\n"
				+ tabString.substring(6)
				+ "}</constructbody>\n";
	}

	// returns the XML representation(<behaviour>) of a TTCN3Module node
	private String getModuleBehaviour(LocationAST node, String type) {
		LocationAST modDefListNode = null;
		LinkedList<LocationAST> modDefListNodeList = ASTUtil.findTypeNodes(node,
				TTCN3ParserTokenTypes.ModuleDefinitionList);
		if (!modDefListNodeList.isEmpty())
			modDefListNode = modDefListNodeList.getFirst();

		if (modDefListNode == null)
			return "<keyword>"
					+ type
					+ "</keyword> "
					+ VisitorCommonFunctions.getName(node)
					+ "<constructbody id=\"modulebody\">{\n}</constructbody>\n";

		LinkedList<LocationAST> controlPartList = ASTUtil.findTypeNodes(node,
				TTCN3ParserTokenTypes.ModuleControlPart);
		LocationAST controlPartNode = null;
		if (!controlPartList.isEmpty())
			controlPartNode = controlPartList.getFirst();
		String controlPartBehaviour = "";
		if (controlPartNode != null)
			controlPartBehaviour = VisitorCommonFunctions.getBehaviour(controlPartNode,
					1,
					true,
					true);
		String b = "<keyword>"
				+ type
				+ "</keyword> "
				+ VisitorCommonFunctions.getName(node)
				+ " <constructbody id=\"modulebody\">{\n"
				+ getModDefListBehaviour(modDefListNode,
						1,
						controlPartBehaviour.replaceFirst("\\n<tab/><tab/>}\\n<tab/><tab/> ;",
								"\n<tab/>};"));
		modDefListNode = modDefListNode.getNextSibling();
		return b + " ";
	}

	// returns the TTCN3Comment representation of T3Doc documentation preceding
	// node
	private TTCN3Comment getCommentFromNode(LocationAST node) {
		List<TokenWithIndex> commentList = node.getCommentsBefore();
		String rawComments = "";
		for (TokenWithIndex c : commentList) {
			rawComments = c.getText() + rawComments;
		}
		return new TTCN3Comment(rawComments, node.getType());
	}

	public void setFilename(String filename) {
		this.filename = filename;
	}

	public String getFilename() {
		return filename;
	}

}
