package edu.fichte.pbi.ide.views;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

import org.eclipse.jface.action.Action;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.ui.part.ViewPart;
import org.osgi.framework.Bundle;

import edu.fichte.pbi.ide.Activator;
import edu.fichte.pbi.run.PluginRunner;
import edu.fichte.pbi.run.app.IConsole;
import edu.fichte.pbi.vm.IRunWatcher;
import edu.fichte.pbi.vm.Settings;
import edu.fichte.pbi.vm.storage.IEEPROMMonitor;

public class ConsoleView extends ViewPart implements IConsole {
	public static final String ID = "edu.fichte.pbi.ide.consoleView";
	public StyledText text;
	private StyledText inputText;

	
	@Override
	public void createPartControl(Composite parent) {
		parent = new Composite(parent, SWT.NULL);
		parent.setLayout(new GridLayout(1, false));
		Label debugLabel = new Label(parent, SWT.HORIZONTAL);
		debugLabel.setText("Console");
		text = new StyledText(parent, SWT.VERTICAL | SWT.HORIZONTAL | SWT.MULTI);
		text.setAlwaysShowScrollBars(false);
		text.setEditable(false);
		Label inputLabel = new Label(parent, SWT.HORIZONTAL);
		inputLabel.setText("Input");
		GridData textGridData = new GridData();
		textGridData.horizontalAlignment = GridData.FILL;
		textGridData.verticalAlignment = GridData.FILL;
		textGridData.grabExcessHorizontalSpace = true;
		textGridData.grabExcessVerticalSpace = true;
		text.setLayoutData(textGridData);
		setScheme();
		inputText = new StyledText(parent, SWT.SINGLE);

		GridData inputTextGridData = new GridData();
		inputTextGridData.horizontalAlignment = GridData.FILL;
		inputTextGridData.verticalAlignment = GridData.FILL;
		inputText.setLayoutData(inputTextGridData);
		inputText.setEditable(false);
		inputText.addTraverseListener(new TraverseListener() {

			@Override
			public void keyTraversed(TraverseEvent e) {
				if (e.keyCode == 13) {
					if (runnerLock != null) {
						inputText.setEditable(false);
						readlineText = inputText.getText();
						inputText.setText("");

						String formerText = text.getText();
//						int formerLength = formerText.length();
						String textAdditionWithNewLines = "\r\n" + readlineText
								+ "\r\n";
						String newText = formerText + textAdditionWithNewLines;
						text.setText(newText);
						synchronized (runnerLock) {
							runnerLock.notifyAll();
						}
					}
				}
			}
		});

		createPartMenu();
	}

	private void stopCurrentExecution() {
		if (isRunning()) {
//			runner.stop();
			runner = null;
			if (runStateListener != null) {
				runStateListener.runStateChanged(false);
			}
		}
	}

	public boolean isRunning() {
		return runner != null && runner.isAlive();
	}

	private void createPartMenu() {
		Action stopAction = new Action("Stop") {
			@Override
			public void run() {
				// Kill the runner
				stopCurrentExecution();
				text.setText(text.getText()+"\n[STOP]");
				
				//now lock the debugview => notify it to lock itself
				System.out.println("Locking Pins...");
				((DebugView) getViewSite().getPage().findView(DebugView.ID)).lockPinState();
			}
		};
		getViewSite().getActionBars().getToolBarManager().add(stopAction);
	}

	@Override
	public void setFocus() {

	}

	private Thread runner;
	private Object runnerLock;
	private String readlineText;
	private IRunStateListener runStateListener;

	public void run(final File file, final IEEPROMMonitor monitor,
			final IRunWatcher runWatcher,
			final VariableView varView,
			final IRunStateListener runStateListener) {
		if (runner != null && runner.isAlive()) {
			runner.interrupt();
		}
		clearConsole();
		Runnable runnable = new Runnable() {
			@Override
			public void run() {
				try {
					runStateListener.runStateChanged(true);
					PluginRunner pluginRunner = new PluginRunner();
					pluginRunner.run(file, ConsoleView.this, monitor,
							runWatcher,getSettings(), varView);
					runStateListener.runStateChanged(false);
				} catch (Throwable e) {
					e.printStackTrace();
					runStateListener.runStateChanged(false);
				}
			}
		};
		runner = new Thread(runnable);
		int priority = Thread.currentThread().getPriority() - 2;
		priority = Math.max(Thread.MIN_PRIORITY, priority);
		runnerLock = new Object();
		((DebugView) getViewSite().getPage().findView(DebugView.ID)).unlockPinState();
		runner.setPriority(priority);
		runner.start();
	}

	private Settings getSettings() {

		Bundle bundle = Activator.getDefault().getBundle();
		File targetRoot = bundle.getDataFile("");
		File file = new File(targetRoot, "config/set.cfg");
		if (!file.canRead()) {
			HelpView.copyPathFromBundleToFSRecursive(bundle, "config",
					targetRoot);
		}
		boolean emulate4Mhz = true, activateinfo = false, printInternComposition = false, nowarn = false;
		try {
			Scanner scanner = new Scanner(file);
			String line = "";
			while (scanner.hasNextLine()) {
				line = scanner.nextLine().trim();
				if (line.startsWith("##") || line.isEmpty())
					continue;
				if (line.startsWith("emulate 4Mhz")) {
					emulate4Mhz = evaluateBooleanExpression(line);
				} else if (line.startsWith("activate info")) {
					activateinfo = evaluateBooleanExpression(line);
				} else if (line.startsWith("print intern composition")) {
					printInternComposition = evaluateBooleanExpression(line);
				} else if (line.startsWith("ignore warnings")) {
					nowarn = evaluateBooleanExpression(line);
				} else {
					scanner.close();
					throw new IllegalArgumentException();
				}

			}
			scanner.close();
		} catch (FileNotFoundException | IllegalArgumentException e) {
			e.printStackTrace();
			err("[Error, run configurations]Couldn't read the configuration file -> using standard cfg\n");
		}
		return new Settings(activateinfo, emulate4Mhz, printInternComposition,
				nowarn);
	}

	public void setScheme() {

		Bundle bundle = Activator.getDefault().getBundle();
		File targetRoot = bundle.getDataFile("");
		File file = new File(targetRoot, "config/color.cfg");
		if (!file.canRead()) {
			HelpView.copyPathFromBundleToFSRecursive(bundle, "config",
					targetRoot);
		}
		try {
			Scanner scanner = new Scanner(file);
			String line = "";
			while (scanner.hasNextLine()) {
				line = scanner.nextLine().trim();
				if (line.startsWith("##") || line.isEmpty())
					continue;
				if (line.startsWith("console scheme")) {
					String[] split = line.split("=");
					if (split.length != 2) {
						scanner.close();
						throw new IllegalArgumentException();
					}
					if (split[1].trim().equals("blue")) {
						text.setBackground(Display.getCurrent().getSystemColor(
								SWT.COLOR_DARK_BLUE));
						text.setForeground(Display.getCurrent().getSystemColor(
								SWT.COLOR_WHITE));
					} else if (split[1].trim().equals("black")) {
						text.setBackground(Display.getCurrent().getSystemColor(
								SWT.COLOR_BLACK));
						text.setForeground(Display.getCurrent().getSystemColor(
								SWT.COLOR_WHITE));

					} else if (split[1].trim().equals("white")) {
						text.setBackground(Display.getCurrent().getSystemColor(
								SWT.COLOR_WHITE));
						text.setForeground(Display.getCurrent().getSystemColor(
								SWT.COLOR_BLACK));

					} else {
						scanner.close();
						throw new IllegalArgumentException();
					}

				} else {
					scanner.close();
					throw new IllegalArgumentException();
				}

			}
			scanner.close();
		} catch (FileNotFoundException | IllegalArgumentException e) {
			e.printStackTrace();
			err("[Error, run configurations]Couldn't read the configuration file -> using standard cfg\n");
		}
	}

	private boolean evaluateBooleanExpression(String s) {
		String[] split = s.split("=");
		if (split.length != 2) {
			throw new IllegalArgumentException();
		}
		return split[1].trim().equals("true");
	}

	@Override
	public void printLn(final String line) {
		print("\n" + line);
	}

	@Override
	public void print(final String s) {
		getSite().getShell().getDisplay().asyncExec(new Runnable() {

			@Override
			public void run() {

				text.setText(text.getText() + s);
				text.setTopIndex(text.getLineCount() - 1);
			}
		});
	}

	@Override
	public String readLn() {
		getSite().getShell().getDisplay().asyncExec(new Runnable() {

			@Override
			public void run() {
				ConsoleView.this.focusOnInput();
			}
		});

		synchronized (runnerLock) {
			try {
				runnerLock.wait();
			} catch (InterruptedException ie) {
				// do nothing here, we have been killed.
			}
		}
		return readlineText;
	}

	protected void focusOnInput() {
		inputText.setEditable(true);
		inputText.forceFocus();
	}

	@Override
	public void clearConsole() {
		getSite().getShell().getDisplay().asyncExec(new Runnable() {

			@Override
			public void run() {
				text.setText("");
				text.setTopIndex(text.getLineCount() - 1);
			}
		});

	}

	@Override
	public void err(final String err) {
		getSite().getShell().getDisplay().asyncExec(new Runnable() {

			@Override
			public void run() {
//				StyleRange styleRange = new StyleRange();
//				styleRange.start = text.getText().length();
//				styleRange.length = err.length();
//				styleRange.fontStyle = SWT.BOLD;
//				styleRange.foreground = Display.getCurrent().getSystemColor(
//						SWT.COLOR_RED);
				text.setText(text.getText() + err + "\n");
				text.setTopIndex(text.getLineCount() - 1);
//				text.setStyleRange(styleRange);
			}
		});
	}

	@Override
	public void warn(final String warn) {
		getSite().getShell().getDisplay().asyncExec(new Runnable() {

			@Override
			public void run() {
//				StyleRange styleRange = new StyleRange();
//				styleRange.start = text.getText().length();
//				styleRange.length = warn.length();
//				styleRange.fontStyle = SWT.BOLD;
//				styleRange.foreground = Display.getCurrent().getSystemColor(
//						SWT.COLOR_RED);
				text.setText(text.getText() + warn + "\n");
				text.setTopIndex(text.getLineCount() - 1);
//				text.setStyleRange(styleRange);
			}
		});
	}

	@Override
	public void info(final String info) {
		getSite().getShell().getDisplay().asyncExec(new Runnable() {

			@Override
			public void run() {
//				StyleRange styleRange = new StyleRange();
//				styleRange.start = text.getText().length();
//				styleRange.length = info.length();
//				styleRange.fontStyle = SWT.BOLD;
//				styleRange.foreground = Display.getCurrent().getSystemColor(
//						SWT.COLOR_RED);
				text.setText(text.getText() + info + "\n");
				text.setTopIndex(text.getLineCount() - 1);
//				text.setStyleRange(styleRange);
			}
		});
	}

}
