package workerthreads.fitnessescriptcorrector;

import java.util.Calendar;

import data.Settings;

import workerthreads.changedates.ChangeDatesWorker;

public class FitnesseTimeMachine {

	private TokenList tokenList;

	private boolean modified = false;

	private boolean baseSchoolyearSet = false;

	private int lastPositionToComment = 0;

	private static String COMMENTMARKER = "fitnesse-time-machine changed";
	private static String BASESCHOOLYEARCOMMENTMARKER = "basisschuljahr";
	private static String TIMEMACHINEFLAGS = "Zeitmaschine:";

	private int destinationSchoolyear = 2009;

	private boolean timeMachineOn = true;

	private int oldBaseSchoolyear = Settings
			.getSchuljahrFuerDateienOhneMarker();

	private int pos = 0;

	public FitnesseTimeMachine(TokenList tokenList, int destinationSchoolyear) {
		super();
		this.tokenList = tokenList;
		this.destinationSchoolyear = destinationSchoolyear;
	}

	public boolean run() {
		Token t;
		boolean lastLineWasPipeLine = thisLineIsPipeline();

		boolean headerOver = false;

		while ((t = peek(0)) != null
				&& destinationSchoolyear != oldBaseSchoolyear) {

			switch (t.getTokenType()) {
			case Einsatz:
				if (timeMachineOn) {
					shiftEinsatz(t);
					modified = true;					
				}
				getToken();
				break;
			case Date:
				if (headerOver && timeMachineOn) {
					shiftDate(t);
					modified = true;
				}
				getToken();
				break;
			case Year:
				if (headerOver && timeMachineOn) {
					shiftYear(t);
					modified = true;
				}
				getToken();
				break;
			case Schoolyear:
				if (headerOver && timeMachineOn) {
					shiftSchoolyear(t);
					modified = true;
				}
				getToken();
				break;
			case PKZ:
				if(timeMachineOn){
				shiftPKZ(t);
				modified = true;
				}
				getToken();
				break;
			case NewLine:
				if (!lastLineWasPipeLine) {
					lastPositionToComment = pos + 1; // There we have to
					// insert comment +
					// newline...
				}
				getToken();
				lastLineWasPipeLine = thisLineIsPipeline();
				break;
			case Hash:

				boolean removeLine = false;

				if (comesBaseSchoolyearCommentmarker()) {
					String s = peek(1).getText().substring(
							BASESCHOOLYEARCOMMENTMARKER.length());
					while (s.length() > 0 && !isDigit(s.charAt(0))) {
						s = s.substring(1);
					}

					if (s.length() >= 4) {

						int sj = 0;

						try {
							sj = Integer.parseInt(s.substring(0, 4));
						} catch (Exception ex) {

						}

						if (sj > 2000 && sj < 3000) {
							oldBaseSchoolyear = sj;

							peek(1).setText(
									BASESCHOOLYEARCOMMENTMARKER + ": "
											+ destinationSchoolyear);
							baseSchoolyearSet = true;

						}

					}

				}

				String timemachineFlags = comesTimemachineFlagsMarker();

				if (timemachineFlags != null) {
					timemachineFlags = stripWhitespace(timemachineFlags);

					if (timemachineFlags.compareToIgnoreCase("ein") == 0 || timemachineFlags.compareToIgnoreCase("an") == 0) {
						timeMachineOn = true;
					}

					if (timemachineFlags.compareToIgnoreCase("aus") == 0) {
						timeMachineOn = false;
					}

				}

				if (comesCommentmarker()) {
					removeLine = true;
				}

				if (removeLine) {
					while (peek(0) != null
							&& peek(0).getTokenType() != TokenType.NewLine) {
						tokenList.remove(pos);
					}

					if (peek(0) != null) {
						tokenList.remove(pos);
					}

					lastLineWasPipeLine = thisLineIsPipeline();
					lastPositionToComment = pos;
					modified = true;
				} else {
					getToken();
				}

				break;
			case Text:
				getToken();
				if (t.getText()
						.contains("de.isb.svp.fixture.SvpAdapterFixture")
						|| t.getText().contains("!define")) {
					headerOver = true;
				}
				break;
			default:
				getToken();
				break;
			}
		}

		if (modified && !baseSchoolyearSet) {
			tokenList.add(0, new Token(TokenType.Hash, "#"));
			tokenList
					.add(1, new Token(TokenType.Text,
							BASESCHOOLYEARCOMMENTMARKER + ": "
									+ destinationSchoolyear));
			tokenList.add(2, new Token(TokenType.NewLine, "\r\n"));
		}

		return modified;
	}

	private String stripWhitespace(String s) {
		while (s.startsWith(" ")) {
			s = s.substring(1);
		}
		while (s.endsWith(" ")) {
			
			s = s.substring(0, s.length() - 1);
		}
		return s;
	}

	public int getOldBaseSchoolyear() {
		return oldBaseSchoolyear;
	}

	private boolean isDigit(char c) {
		return (c >= '1' && c <= '9') || c == '0';
	}

	private boolean comesCommentmarker() {

		String s = "";

		if (peek(1) != null) {
			s = peek(1).getText();
		}

		return (isNewLine(lastToken()) || pos == 0)
				&& s.length() >= COMMENTMARKER.length()
				&& s.substring(0, COMMENTMARKER.length()).compareToIgnoreCase(
						COMMENTMARKER) == 0;

	}

	private String comesTimemachineFlagsMarker() {

		String s = "";

		if (peek(1) != null) {
			s = peek(1).getText();
		}

		if ((isNewLine(lastToken()) || pos == 0)
				&& s.length() >= TIMEMACHINEFLAGS.length()
				&& s.substring(0, TIMEMACHINEFLAGS.length())
						.compareToIgnoreCase(TIMEMACHINEFLAGS) == 0) {

			return s.substring(TIMEMACHINEFLAGS.length());
		} else
			return null;

	}

	private boolean comesBaseSchoolyearCommentmarker() {

		String s = "";

		if (peek(1) != null) {
			s = peek(1).getText();
		}

		return (isNewLine(lastToken()) || pos == 0)
				&& s.length() >= BASESCHOOLYEARCOMMENTMARKER.length()
				&& s.substring(0, BASESCHOOLYEARCOMMENTMARKER.length())
						.compareToIgnoreCase(BASESCHOOLYEARCOMMENTMARKER) == 0;

	}

	private boolean thisLineIsPipeline() {
		boolean ret = false;

		Token p0 = peek(0);
		Token p1 = peek(1);

		if (p0 != null && p0.getTokenType() == TokenType.Pipe) {
			ret = true;
		}

		if (p0 != null && p1 != null
				&& p0.getTokenType() == TokenType.Exclamationmark
				&& p1.getTokenType() == TokenType.Pipe) {
			ret = true;
		}
		return ret;
	}

	private void insertComment(String text) {
		Token t1 = new Token(TokenType.Hash, "#");
		tokenList.add(lastPositionToComment, t1);

		if (lastPositionToComment <= pos) {
			pos++;
		}

		Token t2 = new Token(TokenType.Text, COMMENTMARKER + " " + text);
		tokenList.add(lastPositionToComment + 1, t2);

		if (lastPositionToComment + 1 <= pos) {
			pos++;
		}

		Token t3 = new Token(TokenType.NewLine, "\r\n");
		tokenList.add(lastPositionToComment + 2, t3);

		if (lastPositionToComment + 2 <= pos) {
			pos++;
		}

	}

	private void shiftPKZ(Token t) {
		String oldPKZ = t.getText();
		String newPKZ = ChangeDatesWorker.changePKZ(destinationSchoolyear
				- oldBaseSchoolyear, oldPKZ);

		t.setText(newPKZ);

		insertComment("PKZ " + oldPKZ + " to " + newPKZ + ".");

	}

	private void shiftSchoolyear(Token t) {
		String oldSchoolyear = t.getText();
		int year = Integer.parseInt(oldSchoolyear.substring(0, 4));
		int newYear = year + destinationSchoolyear - oldBaseSchoolyear;
		String newSchoolyear = "" + newYear + "/" + (newYear + 1) % 100;

		t.setText(newSchoolyear);

		insertComment("Schuljahr " + oldSchoolyear + " to " + newSchoolyear
				+ ".");

	}

	private void shiftYear(Token t) {

		String oldYear = t.getText();
		String newYear = ""
				+ (destinationSchoolyear - oldBaseSchoolyear + Integer
						.parseInt(oldYear));

		t.setText(newYear);

		insertComment("year " + oldYear + " to " + newYear + ".");

	}

	private void shiftEinsatz(Token t) {

		/**
		 * e.g. "10/11"
		 */

		String oldEinsatzString = t.getText();

		String s2 = oldEinsatzString.substring(0, 2);

		int i1 = Integer.parseInt(s2);

		int newi1 = (i1 + destinationSchoolyear - oldBaseSchoolyear) % 100;
		int newi2 = (newi1 + 1) % 100;

		String newEinsatzString = intToSchoolyear2Digits(newi1) + "/"
				+ intToSchoolyear2Digits(newi2);

		t.setText(newEinsatzString);

		insertComment("Einsatz \"" + oldEinsatzString + "\" to \""
				+ newEinsatzString + "\".");

	}

	private String intToSchoolyear2Digits(int i) {
		if (i < 0) {
			i = 100 + i;
		}

		String s = "" + i;

		while (s.length() < 2) {
			s = "0" + s;
		}

		return s;

	}

	private void shiftDate(Token t) {
		Calendar cal = t.getCalendar();

		String oldDate = t.getText();

		int year = cal.get(Calendar.YEAR);
		int month = cal.get(Calendar.MONTH);
		int day = cal.get(Calendar.DAY_OF_MONTH);

		int newYear = year + destinationSchoolyear - oldBaseSchoolyear;

		if (month == 1 && day == 29) {
			day = 28;
		}

		if (month == 1 && day == 28 && newYear % 4 == 0) {
			day = 29;
		}

		String dayString = "" + ((day < 10) ? "0" : "") + day;
		String monthString = "" + ((month + 1 < 10) ? "0" : "") + (month + 1);

		t.setText(dayString + "." + monthString + "." + newYear);

		cal.set(newYear, month, day);

		insertComment("date " + oldDate + " to " + t.getText() + ".");

	}

	private boolean isNewLine(Token t) {
		return t != null && t.getTokenType() == TokenType.NewLine;
	}

	private boolean isExclamationmark(Token t) {
		return t != null && t.getTokenType() == TokenType.Exclamationmark;
	}

	private boolean isPipe(Token t) {
		return t != null && t.getTokenType() == TokenType.Pipe;
	}

	private Token peek(int n) {

		if (pos + n < tokenList.size()) {
			return tokenList.get(pos + n);
		} else {
			return null;
		}

	}

	private Token getToken() {

		if (pos < tokenList.size()) {
			pos++;
			return tokenList.get(pos - 1);
		}

		else
			return null;
	}

	private Token lastToken() {
		if (pos > 0) {
			return tokenList.get(pos - 1);
		} else {
			return null;
		}
	}

}
