package gui;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.Vector;
import network.Command;
import network.NetPack;
import network.NetworkHandler;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.*;
import other.MsgBox;
import other.Parser;


/**
 * TabItemProgram Klasse 
 * Dient zum Schreiben von Steuerungsprogrammen in der Sprache
 * Roboscript, wobei alle Befehl als Link eingefügt werden, 
 * Syntaxhighlighting das Arbeiten erleichert und ein eigener
 * Parser die Scripte in Befehle umwandelt
 */
public class TabItemProgram extends Composite {

	public static final String SHELL_TITLE = "Robotix Client";
	
	// Attribute
	private SashForm sashForm = null;
	private StyledText styledText = null;
	private Composite composite = null;
	private Composite composite1 = null;
	private Composite composite2 = null;
	private Composite composite3 = null;
	private Group group = null;
	private Group group1 = null;
	private Group group2 = null;
	private Group group3 = null;
	private Group group4 = null;
	private Group group5 = null;
	private Link link = null;
	private Link link1 = null;
	private Link link2 = null;
	private Link link3 = null;
	private Link link4 = null;
	private Link link5 = null;
	private Link link6 = null;
	private Link link7 = null;
	private Link link8 = null;
	private Link link9 = null;
	private Link link10 = null;
	private Link link11 = null;
	private Link link12 = null;
	private Link link13 = null;
	private Link link15 = null;
	private Link link16 = null;
	private Link link17 = null;
	private Link link18 = null;
	private Link link20 = null;
	private Link link21 = null;
	private Link link22 = null;
	private Link link23 = null;
	private Link link24 = null;
	private Label label = null;
	private Label label2 = null;
	private Label label3 = null;
	private Shell sShell = null;
	private MainShell mainShell = null;
	
	// Keyword-Arrays
	String keywordCmd[] = { "moveposition", "grip", "halt", "offset",
			"motionspeed", "motionparam", "settransobj", "loadbitmap",
			"savebitmap", "defineobstaclecircle", "defineobstaclerect",
			"movemodule", "movemodulestep", "waithome", "wait", "home",
			"reset", "rampvel", "rampacc", "sleep", "camif",
			"camelse", "camendif", "refreshdatabase", "savedatabase"};
	String keywordsParam[] = { "true", "false", "open", "close",
			"param_lin", "param_direct"};
	
	private boolean bChange = false;
	private String sPath = "";
	private ToolTipHandler toolTipHandler = null;
	private NetworkHandler netHandler = null;
	
	/**
	 * Konstruktor
	 * @param mainSh Referenz auf die MainShell
	 * @param parent das Parent Composite
	 * @param style der SWT Style
	 * @param netH das vorherrschende NetworkHandler Objekt
	 */
	public TabItemProgram(MainShell mainSh, Composite parent, int style, NetworkHandler netH) {
		super(parent, style);
		mainShell = mainSh;
		netHandler = netH;
        setLayout(new FillLayout());
        createSashForm();
	}

	/* ##########################################################################
	 * 						VISUAL EDITOR
	 * ##########################################################################*/
	
	/**
	 * Die Methode initialiert die SashForm, fügt alle benötigten Komponenten
	 * ein und ordnet diese an
	 */
	private void createSashForm() {
		sashForm = new SashForm(this, SWT.NONE);
		styledText = new StyledText(sashForm, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER);
		styledText.setFont(new Font(Display.getDefault(), "Tahoma", 10, SWT.NORMAL));
		styledText.setToolTipText("");
		styledText.addModifyListener(new org.eclipse.swt.events.ModifyListener() {
			public void modifyText(org.eclipse.swt.events.ModifyEvent e) {
				if(!bChange){
					sShell.setText(sShell.getText()+"*");
					bChange=true;
				}
				// Wenn durch einen Parserfehler eine Zeile rot hinterlegt wurde,
				// wird sie dadurch wieder weiß:
				resetLineHighlight(styledText.getLineAtOffset(styledText.getSelection().x));
			}
		});
		styledText.addLineStyleListener(new org.eclipse.swt.custom.LineStyleListener() {
			public void lineGetStyle(org.eclipse.swt.custom.LineStyleEvent e) {
				// In dieser Methode müssen die Styles gesetzt werden
				Vector styles = new Vector();

				// nach Kommandos suchen
				int col=SWT.COLOR_BLUE;
				int off=e.lineOffset;
				String s = e.lineText.toLowerCase();
				for(int i=0;i<keywordCmd.length;i++)
					styleKeyWord(off,s,keywordCmd[i],styles,col,0);
				// Weitere Schlüsselwörter (andere Farbe)
				for(int i=0;i<keywordsParam.length;i++)
					styleKeyWord(off,s,keywordsParam[i],styles,SWT.COLOR_DARK_CYAN,SWT.BOLD);
				
				// Zahlen
				// ASCII-Codes: 32 Leerzeichen, 40 Klammer auf,
				// 41 Klammer zu, 44 Komma, 45 Minus, 46 Punkt,
				// 48-57 Zahlen
				// Zahl ist gültig, wenn:
				// Vor der Zahl: Leerzeichen, Klammer auf oder Komma
				// Erstes Zeichen: Minus, Punkt oder Zahl
				// Jedes Weitere Zeichen: Punkt oder Zahl,
				// wobei insgesamt nur ein Punkt zulässig
				// Nach der Zahl: Leerzeichen, Klammer zu oder Komma
				int start=-1;
				char c, ca=' ';
				boolean decimalsep=false;
				for(int i=0;i<e.lineText.length()+1;i++){
					// Zeichen holen (nach Ende der Zeile
					// wird noch Leerzeichen angehängt
					if(i<e.lineText.length()) c=e.lineText.charAt(i); else c=' ';
					
					if(start==-1){
						// Derzeit in keiner Zahl => schauen, ob eine anfängt
						if(c>=48&&c<=57||c==45||c==46){
							// schauen, ob nicht gerade in einem Text
							if(ca==32||ca==40||ca==44){
								start=i;
								decimalsep=(c==46); // erstes Zeichen Punkt?
							}
						}
					}else{
						// Derzeit in einer Zahl => schauen, ob sie aufhört
						if(c==46){
							// Buchstabe ist Punkt => Prüfung ob schon der 2.
							if(decimalsep) start=-1; else decimalsep=true;
						}else if(c>=48&&c<=57||c==46){
							// Zahl ist okay => nix machen
						}else{
							// keine Zahl mehr => Ende
							if(c==32||c==41||c==44){
								// Wenn Zeichen nach Zahl gültig:
								styles.add(new StyleRange(e.lineOffset+start, i-start,
										sShell.getDisplay().getSystemColor(SWT.COLOR_MAGENTA), null));
								start=-1;
							}else{
								// Sonst sofort abbrechen
								start=-1;
							}
						}
					}
					// Altes Zeichen merken
					ca=c;
				}
				
				// Kommentare
				int p = e.lineText.indexOf("//");
				if(p!=-1){
					styles.add(new StyleRange(e.lineOffset+p, e.lineText.length()-p,
							sShell.getDisplay().getSystemColor(SWT.COLOR_DARK_GREEN), null));
				}
				
				// Styles übernehmen
				e.styles = (StyleRange[])styles.toArray(new StyleRange[0]);
			}
		});
		createComposite();
	}
	
	/**
	 * Diese Methode initialisiert das Composite, erzeugt alle Komponenten und
	 * ordnet diese an
	 */
	private void createComposite() {
		RowLayout rowLayout = new RowLayout();
		rowLayout.type = org.eclipse.swt.SWT.VERTICAL;
		rowLayout.fill = true;
		rowLayout.justify = true;
		composite = new Composite(sashForm, SWT.NONE);
		composite.setLayout(rowLayout);
		createGroup();
		createGroup1();
		createGroup2();
		createGroup3();
		createGroup4();
		createGroup5();
		sashForm.setWeights(new int[]{11,2});
	}

	/**
	 * Diese Methode initialisiert die GroupBox für die Links, 
	 * erzeugt alle Komponenten und ordnet diese an
	 */
	private void createGroup() {
		FillLayout fillLayout11 = new FillLayout();
		fillLayout11.type = org.eclipse.swt.SWT.VERTICAL;
		fillLayout11.marginWidth = 15;
		fillLayout11.spacing = 5;
		fillLayout11.marginHeight = 5;
		group = new Group(composite, SWT.NONE);
		group.setText("Bewegungskommandos");
		group.setLayout(fillLayout11);
		link = new Link(group, SWT.NONE);
		link.setText("<a>MovePosition</a>");
		createComposite1();
		link6 = new Link(group, SWT.NONE);
		link6.setText("<a>Halt</a>");
		link6.addMouseListener(new org.eclipse.swt.events.MouseAdapter() {
			public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
				insertText("Halt()");
			}
		});
		link.addMouseListener(new org.eclipse.swt.events.MouseAdapter() {
			public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
				insertText("MovePosition(0,0,0,ka)");
			}
		});
	}

	/**
	 * Diese Methode initialisiert die GroupBox für die Links, 
	 * erzeugt alle Komponenten und ordnet diese an
	 */
	private void createGroup1() {
		FillLayout fillLayout1 = new FillLayout();
		fillLayout1.type = org.eclipse.swt.SWT.VERTICAL;
		fillLayout1.marginWidth = 15;
		fillLayout1.spacing = 5;
		fillLayout1.marginHeight = 5;
		group1 = new Group(composite, SWT.NONE);
		group1.setText("Bewegungsparameter");
		group1.setLayout(fillLayout1);
		link3 = new Link(group1, SWT.NONE);
		link3.setText("<a>Offest</a>");
		link3.addMouseListener(new org.eclipse.swt.events.MouseAdapter() {
			public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
				insertText("Offset(0,0,0,ka)");
			}
		});
		link4 = new Link(group1, SWT.NONE);
		link4.setText("<a>MotionSpeed</a>");
		link4.addMouseListener(new org.eclipse.swt.events.MouseAdapter() {
			public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
				insertText("MotionSpeed(0)");
			}
		});
		link5 = new Link(group1, SWT.NONE);
		link5.setText("<a>MotionParam</a>");
		link5.addMouseListener(new org.eclipse.swt.events.MouseAdapter() {
			public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
				insertText("MotionParam(0)");
			}
		});
		link7 = new Link(group1, SWT.NONE);
		link7.setText("<a>SetTransObj</a>");
		link7.addMouseListener(new org.eclipse.swt.events.MouseAdapter() {
			public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
				insertText("SetTransObj(1)");
			}
		});
	}

	/**
	 * Diese Methode initialisiert die GroupBox für die Links, 
	 * erzeugt alle Komponenten und ordnet diese an
	 */
	private void createGroup2() {
		FillLayout fillLayout2 = new FillLayout();
		fillLayout2.type = org.eclipse.swt.SWT.VERTICAL;
		fillLayout2.marginWidth = 15;
		fillLayout2.spacing = 5;
		fillLayout2.marginHeight = 5;
		group2 = new Group(composite, SWT.NONE);
		group2.setText("Hindernisse");
		group2.setLayout(fillLayout2);
		link8 = new Link(group2, SWT.NONE);
		link8.setText("<a>LoadBitmap</a>");
		link8.addMouseListener(new org.eclipse.swt.events.MouseAdapter() {
			public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
				insertText("LoadBitmap(bmp,true)");
			}
		});
		link9 = new Link(group2, SWT.NONE);
		link9.setText("<a>SaveBitmap</a>");
		link9.addMouseListener(new org.eclipse.swt.events.MouseAdapter() {
			public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
				insertText("SaveBitmap(bmp)");
			}
		});
		link10 = new Link(group2, SWT.NONE);
		link10.setText("<a>DefineObstacleCircle</a>");
		link10.addMouseListener(new org.eclipse.swt.events.MouseAdapter() {
			public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
				insertText("DefineObstacleCircle(0,0,0,ka,0)");
			}
		});
		link11 = new Link(group2, SWT.NONE);
		link11.setText("<a>DefineObstacleRect</a>");
		link11.addMouseListener(new org.eclipse.swt.events.MouseAdapter() {
			public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
				insertText("DefineObstacleRect(0,0,0,ka,0,0)");
			}
		});
	}

	/**
	 * Diese Methode initialisiert die GroupBox für die Links, 
	 * erzeugt alle Komponenten und ordnet diese an
	 */
	private void createGroup3() {
		FillLayout fillLayout3 = new FillLayout();
		fillLayout3.type = org.eclipse.swt.SWT.VERTICAL;
		fillLayout3.marginWidth = 15;
		fillLayout3.spacing = 5;
		fillLayout3.marginHeight = 5;
		group3 = new Group(composite, SWT.NONE);
		group3.setText("Modulkommandos");
		group3.setLayout(fillLayout3);
		link13 = new Link(group3, SWT.NONE);
		link13.setText("<a>MoveModule</a>");
		link13.addMouseListener(new org.eclipse.swt.events.MouseAdapter() {
			public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
				insertText("MoveModule(0,0)");
			}
		});
		link16 = new Link(group3, SWT.NONE);
		link16.setText("<a>Wait</a>");
		link16.addMouseListener(new org.eclipse.swt.events.MouseAdapter() {
			public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
				insertText("Wait(2,3,4)");
			}
		});
		createComposite2();
		link18 = new Link(group3, SWT.NONE);
		link18.setText("<a>Reset</a>");
		link18.addMouseListener(new org.eclipse.swt.events.MouseAdapter() {
			public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
				insertText("Reset(0)");
			}
		});
		createComposite3();
	}

	/**
	 * Diese Methode initialisiert die GroupBox für die Links, 
	 * erzeugt alle Komponenten und ordnet diese an
	 */
	private void createGroup4() {
		FillLayout fillLayout4 = new FillLayout();
		fillLayout4.type = SWT.VERTICAL;
		fillLayout4.marginWidth = 15;
		fillLayout4.spacing = 5;
		fillLayout4.marginHeight = 5;
		group4 = new Group(composite, SWT.NONE);
		group4.setText("Sonstige");
		group4.setLayout(fillLayout4);
		link20 = new Link(group4, SWT.NONE);
		link20.setText("<a>Sleep</a>");
		link20.addMouseListener(new org.eclipse.swt.events.MouseAdapter() {
			public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
				insertText("Sleep(1000)");
			}
		});
	}

	/**
	 * Diese Methode initialisiert die GroupBox für die Links, 
	 * erzeugt alle Komponenten und ordnet diese an
	 */
	private void createGroup5() {
		FillLayout fillLayout5 = new FillLayout();
		fillLayout5.type = org.eclipse.swt.SWT.VERTICAL;
		fillLayout5.marginWidth = 15;
		fillLayout5.spacing = 5;
		fillLayout5.marginHeight = 5;
		group5 = new Group(composite, SWT.NONE);
		group5.setText("Kamera");
		group5.setLayout(fillLayout5);
		link22 = new Link(group5, SWT.NONE);
		link22.setText("<a>CamIf</a>");
		link22.addMouseListener(new org.eclipse.swt.events.MouseAdapter() {
			public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
				insertText("CamIf(0,0,0,0,0)");
			}
		});
		link23 = new Link(group5, SWT.NONE);
		link23.setText("<a>CamElse</a>");
		link23.addMouseListener(new org.eclipse.swt.events.MouseAdapter() {
			public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
				insertText("CamElse()");
			}
		});
		link24 = new Link(group5, SWT.NONE);
		link24.setText("<a>CamEndIf</a>");
		link24.addMouseListener(new org.eclipse.swt.events.MouseAdapter() {
			public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
				insertText("CamEndIf()");
			}
		});
	}
	
	/**
	 * Diese Methode initialisiert das Composite für die Links, 
	 * erzeugt alle Komponenten und ordnet diese an
	 */
	private void createComposite1() {
		RowLayout rowLayout1 = new RowLayout();
		rowLayout1.marginBottom = 0;
		rowLayout1.marginLeft = 0;
		rowLayout1.marginRight = 0;
		rowLayout1.marginTop = 0;
		composite1 = new Composite(group, SWT.NONE);
		composite1.setLayout(rowLayout1);
		link1 = new Link(composite1, SWT.NONE);
		link1.setText("<a>Grip Close</a>");
		link1.addMouseListener(new org.eclipse.swt.events.MouseAdapter() {
			public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
				insertText("Grip(close)");
			}
		});
		label = new Label(composite1, SWT.NONE);
		label.setText("/");
		link2 = new Link(composite1, SWT.NONE);
		link2.setText("<a>Grip Open</a>");
		link2.addMouseListener(new org.eclipse.swt.events.MouseAdapter() {
			public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
				insertText("Grip(open)");
			}
		});
	}
	
	/**
	 * Diese Methode initialisiert das Composite für die Links, 
	 * erzeugt alle Komponenten und ordnet diese an
	 */
	private void createComposite2() {
		RowLayout rowLayout1 = new RowLayout();
		rowLayout1.marginBottom = 0;
		rowLayout1.marginLeft = 0;
		rowLayout1.marginRight = 0;
		rowLayout1.marginTop = 0;
		composite2 = new Composite(group3, SWT.NONE);
		composite2.setLayout(rowLayout1);
		link15 = new Link(composite2, SWT.NONE);
		link15.setText("<a>Home</a>");
		link15.addMouseListener(new org.eclipse.swt.events.MouseAdapter() {
			public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
				insertText("Home(0)");
			}
		});
		label2 = new Label(composite2, SWT.NONE);
		label2.setText("/");
		link12 = new Link(composite2, SWT.NONE);
		link12.setText("<a>WaitHome</a>");
		link12.addMouseListener(new org.eclipse.swt.events.MouseAdapter() {
			public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
				insertText("WaitHome()");
			}
		});
	}
	
	/**
	 * Diese Methode initialisiert das Composite für die Links, 
	 * erzeugt alle Komponenten und ordnet diese an
	 */
	private void createComposite3() {
		RowLayout rowLayout1 = new RowLayout();
		rowLayout1.marginBottom = 0;
		rowLayout1.marginLeft = 0;
		rowLayout1.marginRight = 0;
		rowLayout1.marginTop = 0;
		composite3 = new Composite(group3, SWT.NONE);
		composite3.setLayout(rowLayout1);
		link17 = new Link(composite3, SWT.NONE);
		link17.setText("<a>RampVel</a>");
		link17.addMouseListener(new org.eclipse.swt.events.MouseAdapter() {
			public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
				insertText("RampVel(0,0)");
			}
		});
		label3 = new Label(composite3, SWT.NONE);
		label3.setText("/");
		link21 = new Link(composite3, SWT.NONE);
		link21.setText("<a>RampAcc</a>");
		link21.addMouseListener(new org.eclipse.swt.events.MouseAdapter() {
			public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
				insertText("RampAcc(0,0)");
			}
		});
	}
	
	/* ##########################################################################
	 * 						ÖFFENTLICHE METHODEN
	 * ##########################################################################*/

	/**
	 * Hinterlegt eine bestimmte Zeile rot
	 * @param x Zeilennummer
	 */
	public void highlightLine(int x){
		styledText.setLineBackground(x,1,sShell.getDisplay().getSystemColor(SWT.COLOR_RED));
	}
	
	/**
	 * Hebt die Zeilenhinterlegung komplett auf
	 */
	public void resetCompleteLineHighlight(){
		styledText.setLineBackground(0,styledText.getLineCount(),
				sShell.getDisplay().getSystemColor(SWT.COLOR_WHITE));
	}
	
	/**
	 * Hebt die Zeilenhinterlegung für eine Zeile auf
	 * @param x Zeilenummer
	 */
	public void resetLineHighlight(int x){
		styledText.setLineBackground(x,1,sShell.getDisplay().getSystemColor(SWT.COLOR_WHITE));
	}
	
	/**
	 * Setzt die Shell und initialisiert alles,
	 * was man erst machen kann, wenn die Shell bekannt ist.
	 * Wichtig für die Dialoge.
	 * @param s die zu setzende SHell
	 */
	public void setShell(Shell s){
		sShell = s;
		toolTipHandler = new ToolTipHandler(sShell, this);
		toolTipHandler.activateHoverHelp(styledText);
	}
	
	/**
	 * Wird aufgerufen, wenn der Benutzer in der Tooltip-HTML-Seite
	 * auf einen Link klickt, der per JavaScript ein neues Fenster
	 * geöffnet hätte.
	 * @param page Seite, auf der der Link geklickt wurde
	 * @param line Zeile, in der das zu korrigierende Wort steht
	 */
	public void windowOpenEvent(String page, int line){
		// ".htm" abschneiden und nach oben weiterleiten
		mainShell.dynamicCommandSelect(page.substring(0, page.length()-4),line);
	}
	
	/**
	 * Ermöglicht das Ersetzen von Commands von außen her
	 * (notwendig, wenn ein Command dynamisch gewählt wurde)
	 * @param cmd das zu ersetzende Kommando
	 * @param line Zeile in der die Ersetzung durchgeführt werden soll
	 */
	public void replaceDynamicSelectedCommand(String cmd, int line){
		int start = styledText.getOffsetAtLine(line);
		int len = styledText.getOffsetAtLine(line+1) - 1 - start;
		 
		System.out.println("Start: "+start+", Len: "+len);
		try{
			styledText.replaceTextRange(start, len, cmd);
		}catch(Exception e){
			e.printStackTrace();  
		}
	}
	
	/**
	 * Veranlasst, dass sofort ein HALT-Kommando geschickt wird
	 */
	public void immediateHalt(){
		if(netHandler.isCmdConnected())
			netHandler.getComQueue().enqueueFirst(new Command(NetPack.HALT));
	}
	
	/**
	 * Veranlasst, dass das Programm geparst und anschließend gestartet wird
	 * @param repeatTime gibt an, ob eine Endlosschleife erfolgen soll
	 * 		  bei 0 wird das Programm nur einmal ausgeführt, ansonsten
	 * 		  wird die angegebene Anzahl an Millisekunden gewartet.
	 * @param xPwd Passwort für die Authentifizierung der CMD Verbindung (wird im PRG_START gesendet)
	 */
	public void parseProgramAndRun(int repeatTime,String sPwd){
		// neuen Parser erzeugen
		Parser parser = new Parser();
		// String parsen lassen
		if(parser.parseString(getBoxText())){
			if(netHandler.isCmdConnected()){
				if(MsgBox.showYesNoQuestion("Das Parsen war erfolgreich. " +
					"Sollen die Befehle nun übermittelt werden?")){
					// Programmstart hinzufügen
					netHandler.getComQueue().enqueue(new Command(NetPack.PRG_START,repeatTime,sPwd));
					// Programm hinzufügen
					for(int i=0;i<parser.getQueue().size();i++)
						netHandler.getComQueue().enqueue(parser.getQueue().elementAt(i));
				}
			}else{
				MsgBox.showInfo("Das Parsen war erfolgreich.\n\n" +
						"Es besteht aber keine Verbindung, worüber die " +
						"Befehle gesandt werden könnten.");
			}
		// Parsen war nicht erfolgreich
		} else {
			//System.out.println("Fehler in Zeile " + parser.getErrorLine());
			highlightLine(parser.getErrorLine());
			//System.out.println(parser.getErrorCode());
		}
	}
	
	/**
	 * Veranlasst nur das parsen des Programmes ohne Übertragung
	 */
	public void parseProgramOnly(){
		// neuen Parser erzeugen
		Parser parser = new Parser();
		// String parsen lassen
		if(!parser.parseString(getBoxText())){
			// Parsen war nicht erfolgreich
			System.out.println("Fehler in Zeile " + parser.getErrorLine());
			highlightLine(parser.getErrorLine());
			System.out.println(parser.getErrorCode());
		}else{
			MsgBox.showInfo("Parsen erfolgreich.");
		}
	}
	
	/**
	 * Liefert den gesamten Text
	 * @return den gesamten Text als STring
	 */
	public String getBoxText(){
		return styledText.getText();
	}
	
	/**
	 * Lädt ein Programm aus einer Datei
	 */
	public void loadFile(){
		// Vielleicht Änderungen speichern
		if(bChange){
			switch(askSave()){
			case SWT.CANCEL:	return;
			case SWT.YES:		saveFile();
			}
		}
		// Öffnen-Dialog anzeigen
		FileDialog fileDialog = new FileDialog(sShell, SWT.OPEN);
		fileDialog.setText("Programm auswählen");
		fileDialog.setFilterExtensions(new String[]{"*.prg","*.*"});
		String path = fileDialog.open();
		if(path==null) return;
		
		// Datei laden
		try{
			 BufferedReader bReader = new BufferedReader(new FileReader(path));
			 String s, sa="";
			 while((s=bReader.readLine())!=null) sa+=s+"\n";
			 styledText.setText(sa);
		}catch(Exception e){
			 return;
		}
		sPath=path;
	    sShell.setText(SHELL_TITLE + " - " + getFileName(sPath));
		bChange=false;
	}
	
	/**
	 * Speichert das aktuelle Programm in eine Datei, wenn der Pfad bereits
	 * bekannt ist
	 */
	public void saveFile(){
		if(sPath.length()<=1){
			if(!findSavePath()) return;
		}
		// Datei speichern
	    try {
	        BufferedWriter bWriter = new BufferedWriter(new FileWriter(sPath));
	        bWriter.write(styledText.getText());
	        bWriter.close();
	    } catch (java.io.IOException e) {
	    	return;
	    }
	    sShell.setText(SHELL_TITLE + " - " + getFileName(sPath));
		bChange=false;
	}
	
	/**
	 * Speichert das aktuelle Programm unter vorgabe des gewünschten Pfades
	 */
	public void saveFileAs(){
		if(findSavePath()) saveFile();
	}
	
	/**
	 * Eröffnet eine neue Datei
	 */
	public void newFile(){
		// Vielleicht Änderungen speichern
		if(bChange){
			switch(askSave()){
			case SWT.CANCEL:	return;
			case SWT.YES:		saveFile();
			}
		}
		sPath="";
		styledText.setText("");
		bChange=false;
		sShell.setText(SHELL_TITLE);
	}
	
	/**
	 * Wird aufgerufen, wenn das Programm beendet wird.
	 * @return true, wenn der Benutzer auf Abbrechen gedrückt hat
	 */
	public boolean closeAll(){
		// Vielleicht Änderungen speichern
		if(bChange){
			switch(askSave()){
			case SWT.YES:
				saveFile();
				return false;
			case SWT.NO:
				return false;
			case SWT.CANCEL:
				return true;
			default:
				return false;
			}
		}else{
			return(!MsgBox.showOKCancelQuestion("Möchten Sie das Programm wirklich beenden?"));
		}
	}
	
	/* ##########################################################################
	 * 						HILFSMETHODEN
	 * ##########################################################################*/
	
	/**
	 * Fragt per Dialog, ob Änderungen gespeichert werden sollen
	 * @return SWT.YES, SWT.NO oder SWT.CANCEL
	 */
	private int askSave(){
		return MsgBox.showYesNoCancelQuestion("Möchten Sie Ihre Änderungen speichern?");
	}
	
	/**
	 * Öffnet den Datei-Speichern-Dialog und speichert das
	 * Ergebnis bei Erfolg in sPath
	 * @return true, wenn erfolgreich
	 */
	private boolean findSavePath(){
		FileDialog fileDialog = new FileDialog(sShell, SWT.SAVE);
		fileDialog.setText("Programm speichern");
		fileDialog.setFilterExtensions(new String[]{"*.prg","*.*"});
		String path = fileDialog.open();
		if(path==null) return false;
		sPath=path;
		return true;
	}
	
	/**
	 * Liefert zu einem kompletten Pfad den Dateinamen
	 * @return den Dateinamen aus einer absoluten Pfadangabe
	 */
	private String getFileName(String p){
		int i=p.lastIndexOf("\\");
		if(i<0) i=p.lastIndexOf("/");
		if(i<0)
			return p;
		else
			return p.substring(i+1);
	}
	
	/**
	 * Sucht nach dem angegebenen Wort in der Textbox und färbt
	 * es entsprechend ein.
	 * @param off LineOffset
	 * @param text LineText in Kleinbuchstaben
	 * @param word zu suchendes Wort in Kleinbuchstaben
	 * @param styles Vector, in den der neue Style eingefügt werden soll
	 * @param col Farbe, die gesetzt werden soll
	 * @param style Style (z.B. SWT.BOLD)
	 */
	private void styleKeyWord(int off, String text, String word, Vector styles, int col, int style){
		int p,c=0;
		while((p=text.indexOf(word,c))!=-1){
			c=p+word.length()+1;
			styles.add(new StyleRange(off+p, word.length(),
				sShell.getDisplay().getSystemColor(col), null, style));
		}
	}
	
	/**
	 * Fügt Text in die Textbox ein
	 * @param s der einzufügende Text in den Editor
	 */
	private void insertText(String s){
		int p = styledText.getSelection().x;
		styledText.insert(s + "\n");
		styledText.setSelection(p + s.length() + 1);
	}
	
}
