package edu.fichte.pbi.ide.views;

import java.io.File;

import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
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.util.CallbackVariableListener;
import edu.fichte.pbi.vm.IRunWatcher;
import edu.fichte.pbi.vm.Machine;
import edu.fichte.pbi.vm.var.IVariable;

public class DebugView extends ViewPart implements IRunWatcher {

	public static String ID = "edu.fichte.pbi.ide.debugView";

	private Machine machine;
	
	//GUI elements
	private Label[] label = new Label[8];
	private CLabel[] labelInOut = new CLabel[8];
	private CLabel[] labelHighLow = new CLabel[8];
	private Button[] buttonHighLow = new Button[8];
	private Composite[] widgetPin = new Composite[8];
	private StackLayout[] stackLayout = new StackLayout[8];
		
	//load icons
	private Image imageHigh = loadIcon("status_red.gif");
	private Image imageLow = loadIcon("status_black.gif");
	
	private Image imageInput = loadIcon("InputArrowSmall.gif");
	private Image imageOutput = loadIcon("OutputArrowSmall.gif");

	

	@Override
	public void createPartControl(Composite parent) {
		//create layout Parent with GridLayout
		Composite layoutParent = new Composite(parent, 0);
		layoutParent.setLayout(new GridLayout(3, true));
		
		//create Labels
		Label pin = new Label(layoutParent, 0);
		pin.setText("PIN");
		Label io = new Label(layoutParent, 0);
		io.setText("I/O");
		Label hl = new Label(layoutParent, 0);
		hl.setText("High/Low");

		for (int i = widgetPin.length - 1; i >= 0; i--) {
			//first column: Pin Label
			label[i] = new Label(layoutParent, 0);
			label[i].setText("P" + String.valueOf(i));
			
			//second column: Pin direction (Input/Output)
			labelInOut[i] = new CLabel(layoutParent, 0);
			labelInOut[i].setText("Input");
			labelInOut[i].setImage(imageInput);
			//default is input, see:
			//(http://www.parallax.com/go/PBASICHelp/Content\
			///LanguageTopics/Commands/INPUT.htm -> Explanation)
			
			//third column: Pin state (High/Low)
			//changes between CLabel and Button, depending on if an
			//input is accepted or not (uses StackLayout for that)
			stackLayout[i] = new StackLayout();
			//create new composite in which stackLayout is active
			widgetPin[i] = new Composite(layoutParent, 0);
			widgetPin[i].setLayout(stackLayout[i]);
			//create first "page" of stackLayout: label (if pin is output)
			labelHighLow[i] = new CLabel(widgetPin[i], 0);
			labelHighLow[i].setText("Low");
			labelHighLow[i].setImage(imageLow);
			labelHighLow[i].pack();
			//create second "page" of stackLayout: button (if pin is input)
			buttonHighLow[i] = new Button(widgetPin[i], 0);
			buttonHighLow[i].setText("Low");
			buttonHighLow[i].setImage(imageLow);
			buttonHighLow[i].pack();
			//show second "page" first as pin is initialized as input pin
			stackLayout[i].topControl = buttonHighLow[i];
			//actions listeners are defined outside of the loop
		}
		//action listeners as you are not allowed to use
		//non-final variables in an inner class
		buttonHighLow[0].addSelectionListener(new SelectionAdapter() {
			
			@Override
			public void widgetSelected(SelectionEvent arg0) {
				toggleVariable(0);
			}
		});
		buttonHighLow[1].addSelectionListener(new SelectionAdapter() {
			
			@Override
			public void widgetSelected(SelectionEvent arg0) {
				toggleVariable(1);
			}
		});
		buttonHighLow[2].addSelectionListener(new SelectionAdapter() {
			
			@Override
			public void widgetSelected(SelectionEvent arg0) {
				toggleVariable(2);
			}
		});
		buttonHighLow[3].addSelectionListener(new SelectionAdapter() {
			
			@Override
			public void widgetSelected(SelectionEvent arg0) {
				toggleVariable(3);
			}
		});
		buttonHighLow[4].addSelectionListener(new SelectionAdapter() {
			
			@Override
			public void widgetSelected(SelectionEvent arg0) {
				toggleVariable(4);
			}
		});
		buttonHighLow[5].addSelectionListener(new SelectionAdapter() {
			
			@Override
			public void widgetSelected(SelectionEvent arg0) {
				toggleVariable(5);
			}
		});
		buttonHighLow[6].addSelectionListener(new SelectionAdapter() {
			
			@Override
			public void widgetSelected(SelectionEvent arg0) {
				toggleVariable(6);
			}
		});
		buttonHighLow[7].addSelectionListener(new SelectionAdapter() {
			
			@Override
			public void widgetSelected(SelectionEvent arg0) {
				toggleVariable(7);
			}
		});
	}

	@Override
	public void setFocus() {

	}

	private void toggleVariable(int varNum) {
		IVariable variable = machine.parseIVariable("PIN" + varNum);
		long value = variable.getValue();
		if (value == 0) {
			value = 1;
		} else {
			value = 0;
		}
		variable.setValue(value);
	}

	public void setMachine(Machine machine) {
		this.machine = machine;
		
		IVariable[] listener = new IVariable[labelHighLow.length];
		for (int i = 0; i < labelHighLow.length; i++) {
			listener[i] = this.machine.parseIVariable("PIN" + String.valueOf(i));
			listener[i].setVariableListener(
					new CallbackVariableListener(
							new PinCommObject(labelHighLow[i], buttonHighLow[i])) {
				
				@Override
				public void variableChanged(IVariable variable) {
					final long value = variable.getValue();
					final boolean[] newState = new boolean[1];
					if (value == 1) {
						newState[0] = true;
					}
					getSite().getShell().getDisplay().syncExec(new Runnable() {
						@Override
						public void run() {
							PinCommObject obj = (PinCommObject) callback;
							if (newState[0]) {								
								obj.label.setText("High");
								obj.label.setImage(imageHigh);
								obj.button.setText("High");
								obj.button.setImage(imageHigh);
							} else {
								obj.label.setText("Low");
								obj.label.setImage(imageLow);
								obj.button.setText("Low");
								obj.button.setImage(imageLow);
							}
						}
						
					});
				}
			});
		}

		IVariable[] dirListener = new IVariable[labelInOut.length];
		for (int i = 0; i < labelInOut.length; i++) {
			dirListener[i] = this.machine.parseIVariable("DIR"
					+ String.valueOf(i));
			dirListener[i].setVariableListener(
					new CallbackVariableListener(
							new DirCommObject(
									labelInOut[i], 
									labelHighLow[i], 
									buttonHighLow[i],
									stackLayout[i],
									widgetPin[i])) {
				
				@Override
				public void variableChanged(IVariable variable) {
					final long value = variable.getValue();
					final boolean[] newState = new boolean[1];
					if (value == 1) {
						newState[0] = true;
					}
					getSite().getShell().getDisplay().syncExec(
							new Runnable() {
						@Override
						public void run() {
							DirCommObject obj = (DirCommObject) callback;
							if (newState[0]) {								
								obj.labelDir.setText("Output");
								obj.labelDir.pack();
								obj.labelDir.setImage(imageOutput);
								obj.labelState.setVisible(true);
								obj.buttonState.setVisible(false);
								obj.stackLayout.topControl = obj.labelState;
							} else {
								obj.labelDir.setText("Input");
								obj.labelDir.pack();
								obj.labelDir.setImage(imageInput);
								obj.labelState.setVisible(false);
								obj.buttonState.setVisible(true);
								obj.stackLayout.topControl = obj.buttonState;
							}
							obj.widgetPin.layout();
						}
						
					});
				}
			});
		}
	}
	
	private static Image loadIcon(String iconFilename) {
		Bundle bundle = Activator.getDefault().getBundle();
		File targetRoot = bundle.getDataFile("");
	
		File file = new File(targetRoot, "icons/" + iconFilename);
		if (!file.canRead()) {
			HelpView.copyPathFromBundleToFSRecursive(bundle, "icons",
					targetRoot);
		}
		return new Image(Display.getCurrent(), file.getAbsolutePath());
	}
	
	public void lockPinState() {
		for (int i = 0; i < buttonHighLow.length; i++) {
			buttonHighLow[i].setEnabled(false);
		}
	}
	
	public void unlockPinState() {
		for (int i = 0; i < buttonHighLow.length; i++) {
			buttonHighLow[i].setEnabled(true);
		}
	}
	
	private class PinCommObject {
		
		protected CLabel label;
		
		protected Button button;
		
		private PinCommObject(CLabel label, Button button) {
			this.label = label;
			this.button = button;
		}
	}
	
	private class DirCommObject {

		protected CLabel labelDir;
		
		protected CLabel labelState;
		
		protected Button buttonState;
		
		protected StackLayout stackLayout;
		
		protected Composite widgetPin;
		
		private DirCommObject(
				CLabel labelDir,
				CLabel labelState,
				Button buttonState,
				StackLayout stackLayout,
				Composite widgetPin) {
			this.labelDir = labelDir;
			this.labelState = labelState;
			this.buttonState = buttonState;
			this.stackLayout = stackLayout;
			this.widgetPin = widgetPin;
		}
		
	}

}
