package org.deft.extension.append;

import static org.junit.Assert.fail;

import java.util.List;

import org.deft.extension.XPath;
import org.deft.extension.append.syntax.SyntaxCheckerVisitor;
import org.deft.extension.tokenlayouter.TokenLayouter;
import org.deft.repository.ast.Token;
import org.deft.repository.ast.TreeNode;

//TODO fr alles mal Beispiele schreiben.
//TODO Protected Template

public class TokenAppender {

	private SyntaxCheckerVisitor syntaxChecker;

	public TokenAppender(SyntaxCheckerVisitor sc) {
		syntaxChecker = sc;
	}

	// TODO Mehr Test
	public void appendAfter(TreeNode ast, TreeNode insert, XPath location,
			int emptyLinesBefore, int emptyLinesAfter, int spacesBefore,
			int spacesAfter) {
		TokenLayouter tla = new TokenLayouter(ast.serialize());

		for (TreeNode target : ast.executeXPathQuery(location.toString())) {

			TreeNode parent = target.getParent();
			checkSyntax(parent, insert);
			TreeNode insertCopy = insert;// TODO deep copy
			insertCopy.addInformation(new AppendInformation());
			TokenLayouter tli = new TokenLayouter(insertCopy.serialize());
			tli.reset();

			Token targetToken = target.getLastToken();
			Token nextToken = tla.getNextTokenInSameLine(targetToken);
			int insertAtIndex = target.getSiblingIndex() + 1;

			int linesToInsert = tli.getEndLine() + emptyLinesBefore
					+ emptyLinesAfter - 1;// eine Zeile darf es sein

			if (linesToInsert > 0) {
				tla.insertLineBreakAfter(targetToken);
				tla.insertLinesAfter(targetToken, linesToInsert - 1);
				// minus den Linebreak
			}

			tli.insertLinesBefore(target.getEndLine() - 1 + emptyLinesBefore);

			int baseIndent;
			int startCol = tla.getColOfLine(targetToken);
			if (emptyLinesBefore == 0)
				baseIndent = targetToken.getEndCol() - 1;
			// neue Zeile am "Anfang" beginnen
			else {
				baseIndent = startCol - 1;
			}

			tli.indentRelative(baseIndent + spacesBefore);

			if (nextToken != null) {
				int tcol;
				int ccol = nextToken.getCol();

				if (emptyLinesAfter == 0) {
					tcol = tli.getEndCol() + spacesAfter;

				} else {
					tcol = startCol + spacesAfter;
				}
				int offset = tcol - ccol;
				tla.insertSpacesBefore(nextToken, offset);
			}

			parent.addChild(insertAtIndex, insertCopy);
			tla.reload(ast.serialize());// OPT Neueinlesen der Tokenliste
		}
		tla.repairOffset();
	}

	public void appendBefore(TreeNode ast, TreeNode insert, XPath location,
			int emptyLinesBefore, int emptyLinesAfter, int spacesBefore,
			int spacesAfter) {
		TokenLayouter tla = new TokenLayouter(ast.serialize());

		for (TreeNode target : ast.executeXPathQuery(location.toString())) {

			TreeNode parent = target.getParent();
			checkSyntax(parent, insert);
			TreeNode insertCopy = insert;// TODO deep copy
			insertCopy.addInformation(new AppendInformation());
			TokenLayouter tli = new TokenLayouter(insertCopy.serialize());
			tli.reset();

			Token targetToken = target.getFirstToken();
			
		    Token prevToken = tla.getPrevTokenInSameLine(targetToken);
			int insertAtIndex = target.getSiblingIndex();

			int linesToInsert = tli.getEndLine() + emptyLinesBefore
					+ emptyLinesAfter - 1;// eine Zeile darf es sein

			tli.insertLinesBefore(target.getStartLine() - 1 + emptyLinesBefore);

			int startCol = tla.getColOfLine(targetToken); // TODO
			if (linesToInsert > 0) {
				tla.insertLineBreakBefore(targetToken);
				tla.insertLinesBefore(targetToken, linesToInsert - 1);
				// minus den Linebreak
			}

			int baseIndent;

			if (emptyLinesBefore == 0)
			{
				//baseIndent = targetToken.getCol() - 1;
				if (prevToken!=null)   baseIndent= prevToken.getEndCol()-1;
				else  baseIndent=0;
			}
			// neue Zeile am "Anfang" beginnen
			else {
				baseIndent = startCol - 1;
			}

			tli.indentRelative(baseIndent + spacesBefore);

			int tcol;
			int ccol = targetToken.getCol();
			if (emptyLinesAfter == 0) {
				tcol = tli.getEndCol() + spacesAfter;

			} else {
				tcol = startCol + spacesAfter;
			}
			int offset = tcol - ccol;
			tla.insertSpacesBefore(targetToken, offset);

			parent.addChild(insertAtIndex, insertCopy);
			tla.reload(ast.serialize());// OPT Neueinlesen der Tokenliste
		}
		tla.repairOffset();
	}

	public void replace(TreeNode ast, TreeNode insert, XPath location) {
		fail("Not implemented");
	}

	// TODO Test
	private void checkSyntax(TreeNode parent, TreeNode insert) {
		if (!syntaxChecker.check(parent, insert))
			throw new AppendException(insert.toString() + "not allowed under "
					+ parent.toString());
	}

}
