package org.etsi.t3q.visitor;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.etsi.common.MiscTools;
import org.etsi.common.logging.LoggingInterface.MessageClass;
import org.etsi.t3q.T3Q;

import de.ugoe.cs.swe.trex.core.analyzer.astutil.ReferenceFinder;
import de.ugoe.cs.swe.trex.core.analyzer.astutil.ReferenceWithContext;
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.symboltable.Symbol;

public class QualityCheckerExtras extends QualityChecker {

	public QualityCheckerExtras(T3QVisitor visitor) {
		super(visitor);
		// TODO Auto-generated constructor stub
	}


	public void checkZeroReferencedTestCases(LocationAST node) {
		if (node.getFirstChild().getType() != TTCN3ParserTokenTypes.TestcaseDef){
			return;
		}
		ArrayList<LocationAST> identifiersList = LocationAST.getModuleDefinitionIdentifiersList(node);
		LocationAST identifierNode = null;
		String identifierText = null;
		for (int i = 0; i < identifiersList.size(); i++){
			identifierNode = identifiersList.get(i);
			identifierText = identifierNode.getFirstChild().getText();
	
			Symbol s = identifierNode.getFirstChild().getSymbol();
			//TODO: add safety check here 
			if (getReferenceFinder().countReferences(s) == 0){
				//TODO: add stats counters
				//TODO: add type information
				//TODO: groups shall probably be excluded
				this.getLoggingInterface().logWarning(identifierNode.getLine(),
						identifierNode.getEndLine(),
						MessageClass.LOGGING,
						"Testcase definition for \""+identifierText+"\" is never used!",
								MiscTools.getMethodName());
			}
		}
	
	}

	public void checkSetVerdictPrecededByLog(LocationAST node) {
			//TODO: duplicated and combined constraints (presence and content according to context)
			// Fixed: currently based on SetLocalVerdict node,
			// consider starting at the fail or inconc node instead, checking
			// whether it is
			// within a set verdict context and proceed from there
			// fix: it is specific to the set verdict and not to inconc or fail
			// alone
			boolean problemOccured = false;
			String warning = "";
			if (node.getFirstChild().getType() == TTCN3ParserTokenTypes.Fail){
				LocationAST functionStatementOrDefNode = LocationAST.resolveParentsUntilType(node, TTCN3ParserTokenTypes.FunctionStatement);
				LocationAST parentNode = functionStatementOrDefNode.getParent();
				LocationAST siblingNode = parentNode.getFirstChild();
				LocationAST prevSiblingNode = null;
				do {
					if (siblingNode.getType()!=TTCN3ParserTokenTypes.SemiColon){
						prevSiblingNode = siblingNode;
					}
					siblingNode = siblingNode.getNextSibling();
				} while ((siblingNode != functionStatementOrDefNode) && (siblingNode !=null));
	
				if (prevSiblingNode!=null && prevSiblingNode.getType()==TTCN3ParserTokenTypes.FunctionStatement){
					if (prevSiblingNode.getNthChild(3).getType()!=TTCN3ParserTokenTypes.LogStatement){
						problemOccured = true;
						warning = "Failing verdict not preceded by a log statement";
					} else {
						//check log statement format according to context
						LocationAST moduleDefinitionNode = LocationAST.resolveParentsUntilType(node, TTCN3ParserTokenTypes.ModuleDefinition);
						if (moduleDefinitionNode.getFirstChild().getType()==TTCN3ParserTokenTypes.AltstepDef){
							LocationAST altstepDefIdentifierNode = moduleDefinitionNode.getNthChild(3);
						
							//CHECK REFERENCING CONTEXTS
							int activatedAsDefault = 0;
							boolean mixedContext = false;
							Symbol altstepDefSymbol = altstepDefIdentifierNode.getSymbol();
	
							Map<String, List<ReferenceWithContext>> referenceMap = getReferenceFinder()
									.findReferences(altstepDefSymbol);
							if (referenceMap.isEmpty()) {
								activatedAsDefault = -1;
							} else {
								Iterator<Entry<String, List<ReferenceWithContext>>> referenceMapIterator = referenceMap
										.entrySet().iterator();
	
								while (referenceMapIterator.hasNext()) {
									Entry<String, List<ReferenceWithContext>> referenceEntry = referenceMapIterator
											.next();
									Iterator<ReferenceWithContext> referenceIterator = referenceEntry
											.getValue().iterator();
									while (referenceIterator.hasNext()) {
										ReferenceWithContext referenceWithContext = referenceIterator
												.next();
										LocationAST referenceNode = referenceWithContext
												.getReferenceNode();
										//HERE
										if (referenceNode.getNthParent(3).getType() != TTCN3ParserTokenTypes.ActivateOp) {
											if (activatedAsDefault==1){
												mixedContext = true;
											}
											activatedAsDefault = -1;
										} else {
											if (activatedAsDefault==-1){
												mixedContext = true;
											}
											activatedAsDefault = 1;
										}
									}
								}
							}
							if (mixedContext){
								this.getLoggingInterface().logInformation(node.getLine(),
										node.getEndLine(),
										MessageClass.LOGGING,
										"Alstep \""+altstepDefIdentifierNode.getText()+"\" is used in mixed contexts (both activated as default and not), therefore the results for the correct log statement format may be unreliable!",
												MiscTools.getMethodName());
							}
							if (activatedAsDefault > 0){
								this.checkLogStatementFormat(prevSiblingNode.getNthChild(3),false);
							} else {
								this.checkLogStatementFormat(prevSiblingNode.getNthChild(3),true);
							}
						}
					}
				} else {
					problemOccured = true;
					warning = "Failing verdict not preceded by a log statement!";
				}
			}
			if (node.getFirstChild().getType() == TTCN3ParserTokenTypes.None){
				//TODO: DUPLICATE BLOCK
				LocationAST functionStatementOrDefNode = LocationAST.resolveParentsUntilType(node, TTCN3ParserTokenTypes.FunctionStatement);
				LocationAST parentNode = functionStatementOrDefNode.getParent();
				LocationAST siblingNode = parentNode.getFirstChild();
				LocationAST prevSiblingNode = null;
				do {
					if (siblingNode.getType()!=TTCN3ParserTokenTypes.SemiColon){
						prevSiblingNode = siblingNode;
					}
					siblingNode = siblingNode.getNextSibling();
				} while ((siblingNode != functionStatementOrDefNode) && (siblingNode !=null));
	
				if (prevSiblingNode!=null && prevSiblingNode.getType()==TTCN3ParserTokenTypes.FunctionStatement){
					if (prevSiblingNode.getNthChild(3).getType()!=TTCN3ParserTokenTypes.LogStatement){
						problemOccured = true;
						warning = "Verdict \"none\" not preceded by a log statement!";
					} else {
						this.checkLogStatementFormat(prevSiblingNode.getNthChild(3),false);	
					}
				}
			} 
			
			
			if (problemOccured) {
				this.getLoggingInterface().logWarning(node.getLine(),
						node.getEndLine(),
						MessageClass.LOGGING,
						warning,
								MiscTools.getMethodName());
			}
			
	//		boolean problemOccured = false;
	//
	//		if (ASTUtil.findChild(node, TTCN3ParserTokenTypes.Fail) != null
	//				|| ASTUtil.findChild(node, TTCN3ParserTokenTypes.Inconc) != null) {
	//
	//			LocationAST parentNode = LocationAST.resolveParentsUntilType(node,
	//					TTCN3ParserTokenTypes.FunctionStatementOrDef);
	//
	//			int functionStatementOrDefsVisited = visitor
	//					.getFunctionStatementOrDefNodes().size();
	//			if (parentNode == visitor.getFunctionStatementOrDefNodes().get(
	//					functionStatementOrDefsVisited - 1)) {
	//				int i = functionStatementOrDefsVisited - 2;
	//				if (i > 0) {
	//					LocationAST next = null;
	//					// skip semicolons
	//					next = visitor.getFunctionStatementOrDefNodes().get(i)
	//							.getNextSibling();
	//					while ((next != null)
	//							&& (next.getType() == TTCN3ParserTokenTypes.SemiColon)) {
	//						next = next.getNextSibling();
	//					}
	//
	//					if (next == parentNode) {
	//						LocationAST resultNode = (LocationAST) ASTUtil
	//								.findChild(visitor
	//										.getFunctionStatementOrDefNodes()
	//										.get(i),
	//										TTCN3ParserTokenTypes.LogStatement);
	//						if (resultNode == null) {
	//							problemOccured = true;
	//						}
	//					} else {
	//						problemOccured = true;
	//					}
	//				} else {
	//					problemOccured = true;
	//				}
	//			}
	//		}
		}

	public void checkLogStatementFormat(LocationAST node, boolean withVerificationPoint) {
		boolean problemOccured = false;
		String warning = "";
		String logItemText = "";
		
		LocationAST logItem = node.getFirstChild();
		
		do {
			if (logItem.getFirstChild().getType()!=TTCN3ParserTokenTypes.TemplateInstance){
				logItemText += logItem.getFirstChild().getText();
			} else {
				//TODO: attempt to perform resolution
				logItemText += "<<VAR>>";
			}
			logItem = logItem.getNextSibling();
		} while (logItem!=null && logItem.getType()==TTCN3ParserTokenTypes.LogItem);
		
	
		Pattern logPattern = Pattern.compile(T3Q.activeProfile
				.getLogFormatRegExp());
		Matcher logMatcher = logPattern.matcher(logItemText);
		if (!logMatcher.matches()) {
			problemOccured = true;
			warning = "Invalid log format (\"" + logItemText + "\")!";
		} else if (logMatcher.group(1).equals("") && withVerificationPoint) {
			problemOccured = true;
			warning = "Invalid log format (\"" + logItemText + "\") - missing verification point!";
		}
		if (problemOccured) {
			this.getLoggingInterface().logWarning(node.getLine(),
					node.getEndLine(),
					MessageClass.LOGGING,
					warning,
							MiscTools.getMethodName());
		}
	
	}

}
