package org.etsi.t3d;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.util.LinkedList;

import org.apache.commons.lang.StringEscapeUtils;
import org.etsi.common.logging.LoggingInterface;
import org.etsi.common.logging.LoggingInterface.MessageClass;
import org.etsi.t3d.visitor.VisitorCommonFunctions;

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;

public class ImportPrinter {
	private PrintStream stream;
	private boolean firstModule = true;
	private LinkedList<String> modules = new LinkedList<String>();
	private LinkedList<String> locationsGlobal = new LinkedList<String>();
	private String currentTTCN3File;
	private LoggingInterface logger = null;
	
	public ImportPrinter() {
		this.logger = new LoggingInterface(T3D.activeProfile.getLoggingConfiguration());
		this.logger.setMaximumLogLevel(T3D.getLogLevel());
	}
	
	public void finishXML(){
		writeStream("\n</module>\n</imports>");		
	}

	public void setXMLPath(String xmlpath){
		FileOutputStream file;
		try {
			file = new FileOutputStream(xmlpath);
			stream = new PrintStream(file);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		writeStream("\n<imports t3dversion=\"" + T3D.versionNumber + "\">");
	}

	private void writeStream(String content){
		stream.print(content);
	}
	
	public void setCurrentTTCN3File(String currentTTCN3File) {
		this.currentTTCN3File = currentTTCN3File;
	}

	public String getCurrentTTCN3File() {
		return currentTTCN3File;
	}
	
	public void printImportView(LocationAST node){
		LinkedList<LocationAST> ImportDefs = ASTUtil.findTypeNodes(node, TTCN3ParserTokenTypes.ImportDef);
		for(LocationAST importDef : ImportDefs){
			LocationAST importModuleIdentifier = importDef;
			while(importModuleIdentifier.getFirstChild() != null)
				importModuleIdentifier = importModuleIdentifier.getFirstChild();			
			LocationAST importedModuleIdentifier = VisitorCommonFunctions.getDeclarationNodeFromIdentifier(importModuleIdentifier);
			
			if(!locationsGlobal.contains(importModuleIdentifier.getText())){
				listAdd(locationsGlobal,VisitorCommonFunctions.getName(node.getFirstChild()));
				
				String behaviour = getImportBehavior(importDef);
				
				writeStream("\n<import name=\"" + importModuleIdentifier.getText() + "\">"
					+ "\n\t\t<import_behaviour>" + behaviour + "\n\t\t</import_behaviour>");
				if(!ASTUtil.findTypeNodes(importDef, TTCN3ParserTokenTypes.AllKeyword).isEmpty()){
					listAdd(locationsGlobal, importModuleIdentifier.getText());
					if(importedModuleIdentifier != null)
						printImportView(VisitorCommonFunctions.getModule(importedModuleIdentifier));
				}
				
				writeStream("\n</import>");
			} else{
				String behaviour = getImportBehavior(importDef);
				writeStream("\n<import name=\"" + importModuleIdentifier.getText() + "\">"
						+ "\n\t\t<import_behaviour>" + behaviour + "\n\t\t</import_behaviour>\n</import>");
			}
		}
	}
	
	private String getImportBehavior(LocationAST importDef){
		String b = VisitorCommonFunctions.getWholeElement(importDef, true).replaceAll("\\r", "");
		b = StringEscapeUtils.escapeXml(b);
		b = b.replaceAll("\\n", "").trim();
		if(b.indexOf('{') > 0 && b.indexOf('}') > b.indexOf('{'))
			b = b.substring(b.indexOf('{') + 1, b.indexOf('}'));
		else
			b = b.replaceFirst("import from \\w* ", "");
		b = VisitorCommonFunctions.replaceReferences(b, VisitorCommonFunctions.getReferences(importDef));
		return b.trim();
	}
	
	private void listAdd(LinkedList<String> list, String string){
		if(!list.contains(string))
			list.add(string);
	}
	
	public void checkForCyclicImports(LocationAST node, LinkedList<String> locations){
		LinkedList<LocationAST> ImportDefs = ASTUtil.findTypeNodes(node, TTCN3ParserTokenTypes.ImportDef);	
		for(LocationAST importDef : ImportDefs){
			LocationAST importModuleIdentifier = importDef;
			while(importModuleIdentifier.getFirstChild() != null)
				importModuleIdentifier = importModuleIdentifier.getFirstChild();			
			LocationAST importedModuleIdentifier = VisitorCommonFunctions.getDeclarationNodeFromIdentifier(importModuleIdentifier);			
			if(!locations.contains(importModuleIdentifier.getText())){
				LinkedList<String> newLocations = new LinkedList<String>();
				for(String l : locations)
					newLocations.add(l);
				listAdd(newLocations, VisitorCommonFunctions.getName(node.getFirstChild()));
				listAdd(newLocations, importModuleIdentifier.getText());
				
				
				String behaviour = VisitorCommonFunctions.getBehaviour(importDef, 0, false, true);

				behaviour = behaviour.substring(behaviour.indexOf("</link>") + 7).trim();
				if(behaviour.charAt(0) == '{' && behaviour.charAt(behaviour.length()-1) == '}')
					behaviour = behaviour.substring(1, behaviour.length()-1);

				behaviour = behaviour.replaceAll("\\s;", "").replaceAll("<constructbody id=\".*\">", "").replaceAll("</constructbody>", "");;
				
				behaviour = behaviour.trim();
				if(behaviour.charAt(0) == '{')
					behaviour = behaviour.replaceAll("\\{", "").replaceAll("\\}", "");
				behaviour = behaviour.replaceAll("\\n", "");

				if(behaviour.startsWith("all") || behaviour.startsWith("recursive all"))
					checkForCyclicImports(VisitorCommonFunctions.getModule(importedModuleIdentifier), newLocations);
			} else if(locations.getFirst().equals(importModuleIdentifier.getText())){
				String warning = "";
				for(String l : locations) {
					warning += " -> " + l;
				}
				warning = "Cyclic imports found: " + warning.substring(4) + " -> " + locations.getFirst();
				this.getLoggingInterface().logWarning(importModuleIdentifier.getLine(),
						importModuleIdentifier.getLine(),
						MessageClass.DOCUMENTATION,
						warning);
				//TODO: revise XML usage
				//T3D.printLog(0, currentTTCN3File, importModuleIdentifier.getLine(), importModuleIdentifier.getLine(), 1, warning);
			}
		}
	}
	
	public void newModule(String name) {
		locationsGlobal = new LinkedList<String>();
		String moduleName = name;
		if (modules.contains(name)) {
			int repetitionIndex = 0; 
			while(modules.contains(name + "-"+repetitionIndex)){
				repetitionIndex++;
			}
			moduleName = name + "-" + repetitionIndex;
		}
		modules.add(moduleName);
		if(!firstModule)
			writeStream("\n</module>");
		else
			firstModule = false;
		writeStream("\n<module name=\"" + moduleName + "\">");		
	}

	public void setLoggingInterface(LoggingInterface logger) {
		this.logger = logger;
	}

	public LoggingInterface getLoggingInterface() {
		return logger;
	}
}
