/******************************************************************************
    PcpPuzzle v0.0.1 - (C) 2000 Heiko Stamer <stamer@informatik.uni-leipzig.de>
                       Dr. Johannes Waldmann <joe@informatik.uni-leipzig.de>

    A little puzzle-game based on Post Correspondence Problem (PCP)

  http://stinfwww.informatik.uni-leipzig.de/~mai97ixb
  http://www.informatik.uni-leipzig.de/~joe/edu/spielplatz/pcp

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*******************************************************************************/
import java.lang.*;
import java.util.*;
import java.applet.*;
import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;			// Java 1.1 Event Handling

/** 
PCP-Puzzle
@author Heiko Stamer
@version 0.0.1

Please ignore the teribble coding style below this line!
**/

public class PcpPuzzle extends Applet 
	implements MouseListener, MouseMotionListener {
	// ---------------------------------------------------------------
	// allgemeine Konstanten
	// ---------------------------------------------------------------
	public static final int MAX_PCP_LSG_LEN = 500;
	public static final int MAX_PCP_BOARD_WIDTH = 8000;
	public static final String MSG_VER = "PCP - Puzzle 0.0.1 " +
		"http://stinfwww.informatik.uni-leipzig.de/~mai97ixb";
	public static final String MSG_UNI = "http://www.informatik.uni-leipzig.de";	
	public static final String[] MSG_START = new String[]{
		"Herzlich Willkommen beim PCP-Puzzle! Beginnen Sie das Spiel durch " +
		"Auswahl eines der obigen Puzzleteile.",
		"Welcome to PCP-Puzzle! Start the game by choosing tiles from above."};
	public static final String[] MSG_CHOOSE_TILE = new String[]{
		"Versuchen Sie durch geschickte Wahl der Teile, oben und unten das " +
		"gleiche Binärwort zu konstruieren.",
		"Try to construct the same binary word. You could do that through " +
		"intelligent choosing of tiles."};
	public static final String[] MSG_SCROLLPANE_LEFT = new String[]{
		"Klicken Sie mit der linken Maustaste, um den Anzeigeausschnitt des " +
		"Puzzles nach links zu verschieben.",
		"Click left mouse button for scrolling to the left."};
	public static final String[] MSG_SCROLLPANE_RIGHT = new String[]{	
		"Klicken Sie mit der linken Maustaste, um den Anzeigeausschnitt des " +
		"Puzzles nach rechts zu verschieben.",
		"Click left mouse button for scrolling to the right."};
	public static final String[] MSG_CANNOT_UNDO = new String[]{
		"Kann keinen Schritt rückgängig machen, da keine Puzzleteile gelegt " +
		"wurden.",
		"Can't undo previous move until tiles are played."};
	public static final String[] MSG_OUR_SOLUTION = new String[]{
		"Herzlichen Glückwunsch! Wir haben eine Lösung gefunden.",
		"Congratulation! We have found a solution."};
	public static final String[] MSG_YOUR_SOLUTION = new String[]{
		"Herzlichen Glückwunsch! Sie haben eine Lösung gefunden.",
		"Congratulation! You have found a solution."};
	public static final String[] MSG_YOUR_ALLREADY_SOLUTION = new String[]{
		"Herzlichen Glückwunsch! Sie haben schon eine Lösung gefunden.",
		"Congratulation! You have allready found a solution."};
	public static final String[] MSG_NO_HINT = new String[]{
		"Leider keine Hinweise vorhanden!",
		"No hint available!"};
	public static final String[] MSG_WRONG_TILE = new String[]{
		"Dieses Puzzleteil passt leider nicht!",
		"Wrong tile!"};
	public static final String[] MSG_MAX_TILE = new String[]{
		"Maximalzahl an Puzzleteilen erreicht! Kein weiteres Anlegen möglich.",
		"No more tiles possible!"};
	public static final String[] MSG_NEW_BUTTON = new String[]{
		"Die Funktion 'New' startet ein neues Spiel.",
		"The function 'New' starts a new game."};
	public static final String[] MSG_UNDO_BUTTON = new String[]{
		"Mit 'Undo' können Sie den letzten Spielzug rückgängig machen.",
		"You can 'Undo' the previous move in the game."};
	public static final String[] MSG_HINT_BUTTON = new String[]{
		"Ihr Rechner hilft Ihnen mit der Vorgabe eines richtigen Spielzuges.",
		"Your computer can help you with one right move."};
	// ---------------------------------------------------------------
	// Daten der Klasse
	// ---------------------------------------------------------------
	protected Image logo;
	protected int logo_width = 0, logo_height = 0, language = 0;
	protected boolean tile_text = false, tile_border = false;
	protected Dimension dim;
	protected String[][] pcp;
	protected int pcp_i = -1;
	protected int[] pcp_l = new int[MAX_PCP_LSG_LEN], pcp_solution = null;
	protected String pcp_u = "", pcp_v = "";
	protected PcpTile[] smalltile, bigtile;
	protected PcpBoard board;
	protected ScrollPane boardPane;
	protected Color[] pcpcolor = new Color[]
		{(Color.orange).darker(),
		 (Color.red).darker(),
		 (Color.yellow).darker(),
		 (Color.green).darker(),
		 (Color.cyan).darker()
		};
	protected boolean update_logo = false, update_newButton = false,
						update_undoButton = false, update_hintButton = false,
						update_textConsole = false, logo_text1 = false,
						logo_text2 = false;
	protected boolean[] update_smalltile;
	protected static Color background = new Color(178, 178, 178);
	
	// ---------------------------------------------------------------
	// WUI Komponenten (Konstanten + Daten + innere Klassen)
	// ---------------------------------------------------------------
	public static final Color WuiRootColor = new Color(120, 100, 220);
	public static final int WuiButtonWidth = 60;
	public static final int WuiConsoleWidth = 360;
	public static final Font WuiTitleFont = new Font("Serif", Font.BOLD, 30);
	public static final Font WuiButtonFont = new Font("SansSerif", Font.BOLD, 14);
	public static final Font WuiConsoleFont = new Font("SansSerif", Font.PLAIN, 10);
	protected WuiButton newButton, undoButton, hintButton;
	protected WuiConsole textConsole;
	
	public class WuiConsole {
		private String[] lines = null;
		protected int rw = 4, rh = 2;
		protected int x, y, width, height;
		protected Font font = null;
		protected FontMetrics metric = null;
		protected Color background = null;
		
		public WuiConsole(FontMetrics metric, Font font, int lines, Color b, int x, int y) {
			this.lines = new String[lines];
			this.font = font;
			this.metric = metric;
			this.x = x;
			this.y = y;
			this.width = WuiConsoleWidth;
			this.height = (lines * rh) + (lines * this.metric.getHeight());
			this.background = b;
		}
		
		private boolean space(String[] array) {
			boolean tmp = false;
			for (int i = 0; i < array.length; i++) {
				if (array[i] == null) {
					tmp = true;
					break;
				}
			}
			return tmp;
		}
		
		public void println(String text) {
			Vector textlines = new Vector();
			Enumeration enum = null;
			String tmp = text.trim();
			
			do {
				while (metric.stringWidth(tmp) > width) {
					if (tmp.indexOf(" ") == -1) break;
					tmp = tmp.substring(0,tmp.lastIndexOf(" "));
				}
				textlines.addElement(tmp);
				text = text.substring(tmp.length());
				text = text.trim();
				tmp = text.trim();
			} while (tmp.length() != 0);
			
			enum = textlines.elements();	
			while (enum.hasMoreElements()) {
				if (!space(lines)) {
					for (int i = 0; i < (lines.length - 1); i++) {
						lines[i] = lines[i + 1];
					}
					lines[lines.length - 1] = null;
				}
				for (int i = 0; i < lines.length; i++) {
					if (lines[i] == null) {
						lines[i] = (String)enum.nextElement();
						break;
					}
				}
			}
		}
		
		public void clear() {
			for (int i = 0; i < lines.length; i++) lines[i] = null;
		}
		
		public void paint(Graphics g) {
			g.setColor(background);
			g.fillRect(x,y,width,height);
			g.setColor(WuiRootColor.darker());
			g.setFont(this.font);
			for (int i = 0; i < lines.length; i++) {
				if (lines[i] != null)
					g.drawString(lines[i],x,y + (i * rh) + ((i + 1) *
						metric.getHeight()));
			}
		}
	
	}
	
	public class WuiButton {
		public boolean light = false;
		protected int rw = 4, rh = 4;
		protected Font font = null;
		protected FontMetrics metric = null;
		protected String text = null;
		protected int x, y, width, height, textwidth;
		protected Polygon edge = null;
						
		public WuiButton(FontMetrics metric, Font font, String text, int x, int y) {
			this.font = font;
			this.metric = metric;
			this.text = text;
			this.x = x;
			this.y = y;
			this.width = WuiButtonWidth; 
			this.textwidth = this.metric.stringWidth(this.text);
			this.height = this.metric.getHeight() + (2 * rh);
			this.edge = new Polygon(new int[]{x,x + rw + 2,x+rw+2,x},
				new int[]{y, y, y + rh + 2,y+rh+2},4);
		}
		
		public void paint(Graphics g) {
			if (light) g.setColor(WuiRootColor.brighter());
				else g.setColor(WuiRootColor);
			g.fillRect(x,y,width,height);
			g.setColor(WuiRootColor.darker());
			g.drawRect(x,y,width,height);
			g.fillPolygon(edge);
			if (light) g.setColor(WuiRootColor.darker());
				else g.setColor(WuiRootColor.darker());
			g.setFont(this.font);
			g.drawString(text,x + (width - textwidth) / 2, y + height - rh - rh);
		}
		
			
		public boolean inside(int mx, int my) {
			return(mx >= x && mx < (x + width) && my >= y && my < (y + height));
		}
	}
	
	public class PcpTile {
		protected int code_width = 40, code_height0 = 20, code_height1 = 40;
		protected int my_x, my_y, my_diff;
		protected boolean my_text = false, my_border = false;
		public boolean light = false;
		protected Color color = null, background = null;
		protected FontMetrics metric = null;
		protected String u = null, v = null;
		protected Polygon U = new Polygon();
		protected Polygon V = new Polygon();
		
		public PcpTile(String u, String v, Color c, Color b, FontMetrics metric, 
			boolean	text, boolean border) {
			this.color = c; this.background = b;
			this.metric = metric;
			this.my_text = text;
			this.my_border = border;
			this.u = u; this.v = v;			
			this.U.addPoint(0, 0); 
			this.U.addPoint(0, code_height0);
			for (int i=0; i<u.length(); i++) {
				if (u.charAt(i) == '0') {
					this.U.addPoint((i * code_width), code_height0 +
						code_height1);
					this.U.addPoint(((i + 1) * code_width), code_height0);
				} else if (u.charAt(i) == '1') {
					this.U.addPoint(((i + 1) * code_width), code_height0 +
						code_height1);
					if (!( ((i + 1) < u.length()) && (u.charAt(i + 1) == '0')))
						this.U.addPoint(((i + 1) * code_width), code_height0);
				} else { System.err.println("nur 0 und 1 erlaubt (PCP-Alphabet)"); }
			}
			this.U.addPoint((u.length() * code_width), 0);
						
			this.V.addPoint(0, (2 * code_height0) + code_height1);
			this.V.addPoint(0, code_height0 + code_height1);
			for (int i=0; i<v.length(); i++) {
				if (v.charAt(i) == '0') {
					this.V.addPoint(((i + 1) * code_width), code_height0);
					if (!( ((i + 1) < v.length()) && (v.charAt(i + 1) == '1')))
						this.V.addPoint(((i + 1) * code_width), code_height0 + 
						code_height1);
				} else if (v.charAt(i) == '1') {
					this.V.addPoint((i * code_width), code_height0);
					this.V.addPoint(((i + 1) * code_width), code_height0 +
						code_height1);
				} else { System.err.println("nur 0 und 1 erlaubt (PCP-Alphabet)"); }
			}
			this.V.addPoint((v.length() * code_width), (2 * code_height0) +
				code_height1);
		}
		
		public PcpTile(String u, String v, Color c, Color b, int x, int y, 
			int scale, int diff, FontMetrics metric) {
			this.color = c; this.background = b; this.my_diff = diff;
			this.metric = metric;
			this.my_x = x; this.my_y = y;
			this.u = u; this.v = v;
			this.code_width = this.code_width / scale;
			this.code_height0 = this.code_height0 / scale;
			this.code_height1 = this.code_height1 / scale;
			
			this.U.addPoint(x, y);
			this.U.addPoint(x, code_height0 + y);
			for (int i=0; i<u.length(); i++) {
				if (u.charAt(i) == '0') {
					this.U.addPoint((i * code_width) + x, code_height0 +
						code_height1 + y);
					this.U.addPoint(((i + 1) * code_width) + x, code_height0 + y);
				} else if (u.charAt(i) == '1') {
					this.U.addPoint(((i + 1) * code_width) + x, code_height0 +
						code_height1 + y);
					this.U.addPoint(((i + 1) * code_width) + x, code_height0 + y);
				} else { System.err.println("nur 0 und 1 erlaubt (PCP-Alphabet)"); }
			}
			this.U.addPoint((u.length() * code_width) + x, y);
			
			this.V.addPoint(x, (2 * code_height0) + code_height1 + diff + y);
			this.V.addPoint(x, code_height0 + code_height1 + diff + y);
			for (int i=0; i<v.length(); i++) {
				if (v.charAt(i) == '0') {
					this.V.addPoint(((i + 1) * code_width) + x, code_height0 + diff + y);
					this.V.addPoint(((i + 1) * code_width) + x, code_height0 + 
						code_height1 + diff + y);
				} else if (v.charAt(i) == '1') {
					this.V.addPoint((i * code_width) + x, code_height0 + diff + y);
					this.V.addPoint(((i + 1) * code_width) + x, code_height0 +
						code_height1 + diff + y);
				} else { System.err.println("nur 0 und 1 erlaubt (PCP-Alphabet)"); }
			}
			this.V.addPoint((v.length() * code_width) + x, (2 * code_height0) +
				code_height1 + diff + y);
		}
		
		public boolean inside(int x, int y) {
			return (U.contains(x,y) || V.contains(x,y));
		}
		
		public void paintXyU(Graphics g, int x, int y, boolean back) {
			Polygon p = new Polygon(U.xpoints,U.ypoints,U.npoints);
			p.translate(x,y);
						
			if (back) g.setColor(background); else g.setColor(color);
			g.fillPolygon(p);
			
			if ((my_text) && !(back)) {
				g.setColor(Color.black);
				g.setFont(WuiConsoleFont);
				for (int i=0; i<u.length(); i++) {
					if (u.charAt(i) == '0') {
						g.drawString("0", x + (i * code_width) + ((code_width -
						metric.stringWidth("0")) / 2), y + ((code_height0 - 
						metric.getHeight()) / 2) + metric.getHeight());
					} else if (u.charAt(i) == '1') {
						g.drawString("1", x + (i * code_width) + ((code_width -
						metric.stringWidth("1")) / 2), y + ((code_height0 - 
						metric.getHeight()) / 2) + metric.getHeight());
					} else { 
						System.err.println("nur 0 und 1 erlaubt	(PCP-Alphabet)"); 
					}
				}
			}
			if ((my_border) && !(back)) {
				g.setColor(Color.black);
				g.drawPolygon(p);
			} else if ((my_border) && (back)) {
				g.drawPolygon(p);
			}
		}
		
		public void paintXyUb(Graphics g, int x, int y) {
			if (my_border) {
				Polygon p = new Polygon(U.xpoints,U.ypoints,U.npoints);
				p.translate(x,y);
				g.setColor(Color.black);
				g.drawPolygon(p);
			}
		}
		
		public void paintXyV(Graphics g, int x, int y, boolean back) {
			Polygon p = new Polygon(V.xpoints,V.ypoints,V.npoints);
			p.translate(x,y);
			
			if (back) g.setColor(background); else g.setColor(color);
			g.fillPolygon(p);
			
			if ((my_text) && !(back)) {
				g.setColor(Color.black);
				g.setFont(WuiConsoleFont);
				for (int i=0; i<v.length(); i++) {
					if (v.charAt(i) == '0') {
						g.drawString("0", x + (i * code_width) + ((code_width -
						metric.stringWidth("0")) / 2), y + ((code_height0 - 
						metric.getHeight()) / 2) + metric.getHeight() + code_height1
						+ code_height0);
					} else if (v.charAt(i) == '1') {
						g.drawString("1", x + (i * code_width) + ((code_width -
						metric.stringWidth("1")) / 2), y + ((code_height0 - 
						metric.getHeight()) / 2) + metric.getHeight() + code_height1
						+ code_height0);
					} else { 
						System.err.println("nur 0 und 1 erlaubt	(PCP-Alphabet)"); 
					}
				}
			}
			if ((my_border) && !(back)) {
				g.setColor(Color.black);
				g.drawPolygon(p);
			} else if ((my_border) && (back)) {
				g.drawPolygon(p);
			}
		}
		
		public void paintXyVb(Graphics g, int x, int y) {
			if (my_border) {
				Polygon p = new Polygon(V.xpoints,V.ypoints,V.npoints);
				p.translate(x,y);
				g.setColor(Color.black);
				g.drawPolygon(p);
			}
		}
		
		public void paint(Graphics g) {
			if (light) g.setColor(color.brighter()); else g.setColor(color);
			g.fillPolygon(U);	
			g.fillPolygon(V);
		}
		
		public int getWidthU() {
			return ((U.getBounds()).width);
		}
		
		public int getWidthV() {
			return ((V.getBounds()).width);
		}
		
		public int getHeightU() {
			return ((U.getBounds()).height);
		}
		
		public int getHeightV() {
			return ((V.getBounds()).height);
		}
	}
	
	public class PcpBoard extends Panel 
		implements MouseListener, MouseMotionListener {
		protected int[] playedTiles = new int[MAX_PCP_LSG_LEN];
		protected int idxTiles = -1;
		protected int width, height;
		protected boolean scroll_text_left = false, scroll_text_right = false;
		protected boolean clicked = false;
		protected PcpTile[] my_tiles;
		protected Color background;
		protected Applet applet;
		protected ScrollPane pane;
		
		public PcpBoard(Applet applet, ScrollPane pane, int width, int height,
			PcpTile[] tiles, Color b) {
			this.applet = applet;
			this.pane = pane;
			this.width = width; this.height = height;
			this.my_tiles = tiles;
			this.background = b;
			setSize(width, height);
			setBackground(b);
			this.addMouseListener(this);
			this.addMouseMotionListener(this);
		}
		
		public void add(int tile) {
			if ((tile >= 0) && (tile < my_tiles.length) && 
			(idxTiles < (playedTiles.length - 1))) {
				idxTiles++;
				playedTiles[idxTiles] = tile;
				paintTile(false);
				
				int xU = 0;
				int xV = 0;
				for (int i=0; i < (idxTiles + 1); i++) {
					xU += my_tiles[playedTiles[i]].getWidthU();
					xV += my_tiles[playedTiles[i]].getWidthV();
				}
				int poi = Math.min(xU, xV);
				int poe = Math.max(xU, xV);
				if (poe > (pane.getScrollPosition().x + pane.getSize().width)) {
					if ((poi < width) && (poi > (pane.getSize().width / 2))) {
						pane.setScrollPosition(poi - (pane.getSize().width / 2), 0);
					}
				}
			}
		}
		
		public void removeLast() {
			if (idxTiles >= 0) {
				paintTile(true);
				playedTiles[idxTiles] = -1;
				idxTiles--;
				paintBorder();
				
				int xU = 0;
				int xV = 0;
				for (int i=0; i < (idxTiles + 1); i++) {
					xU += my_tiles[playedTiles[i]].getWidthU();
					xV += my_tiles[playedTiles[i]].getWidthV();
				}
				int poi = Math.min(xU, xV);
				int poe = Math.max(xU, xV);
				if (poi < (pane.getScrollPosition().x + pane.getSize().width / 2)) {
					if (poi > (pane.getSize().width / 2)) {
						pane.setScrollPosition(poi - (pane.getSize().width / 2), 0);
					} else {
						pane.setScrollPosition(0, 0);
					}
				}
			}
		}
		
		public void paint(Graphics g) {
			int xU = 0;
			int xV = 0;

			for (int i=0; i < (idxTiles + 1); i++) {
				my_tiles[playedTiles[i]].paintXyU(g, xU, 0, false);
				my_tiles[playedTiles[i]].paintXyV(g, xV, 0, false);
				xU += my_tiles[playedTiles[i]].getWidthU();
				xV += my_tiles[playedTiles[i]].getWidthV();
			}
		}
		
		public void update(Graphics g) {
			paint(g);
		}
		
		public Dimension getPreferredSize() { 
			return new Dimension(width, height);
		}
		
		public void paintTile(boolean back) {
			int xU = 0;
			int xV = 0;
			
			for (int i=0; i < (idxTiles + 1); i++) {
				if (i == idxTiles) { 
					my_tiles[playedTiles[i]].paintXyU(this.getGraphics(), xU, 0, back);
				}
				if (i == idxTiles) {
					my_tiles[playedTiles[i]].paintXyV(this.getGraphics(), xV, 0, back);
				}
				xU += my_tiles[playedTiles[i]].getWidthU();
				xV += my_tiles[playedTiles[i]].getWidthV();
			}
		}
		
		public void paintBorder() {
			int xU = 0;
			int xV = 0;
			
			for (int i=0; i < (idxTiles + 1); i++) {
				my_tiles[playedTiles[i]].paintXyUb(this.getGraphics(), xU, 0);
				my_tiles[playedTiles[i]].paintXyVb(this.getGraphics(), xV, 0);
				xU += my_tiles[playedTiles[i]].getWidthU();
				xV += my_tiles[playedTiles[i]].getWidthV();
			}
		}
		
		public boolean light_boolean_array (PcpTile[] array) {
			boolean tmp = false;
			for (int i = 0; i < array.length; i++) {
				if (array[i].light) {
					tmp = true;
					break;
				}
			}
			return tmp;
		}
		
		public void mousePressed(MouseEvent e) { }
		public void mouseReleased(MouseEvent e) { }
		public void mouseClicked(MouseEvent e) {
			int sx = (pane.getScrollPosition()).x;
						
			if ((e.getX() - (pane.getScrollPosition()).x) 
			<= ((pane.getSize()).width / 2)) {
				if (sx >= 40) {
					sx = sx - 40;
					pane.setScrollPosition(sx, 0);
					if (sx >= 40) clicked = true;
				}
			} else {
				if (sx <= (width - 40)) {
					sx = sx + 40;
					pane.setScrollPosition(sx, 0);
					if (sx <= (width - 40)) clicked = true;
				}
			}
			e.consume();
		}
		public void mouseEntered(MouseEvent e) { 
			if (newButton.light) {
				newButton.light = false;
				update_newButton = true;
				textConsole.clear();
				update_textConsole = true;
				applet.repaint();
			} else if (undoButton.light) {
				undoButton.light = false;
				update_undoButton = true;
				textConsole.clear();
				update_textConsole = true;
				applet.repaint();
			} else if (hintButton.light) {
				hintButton.light = false;
				update_hintButton = true;
				textConsole.clear();
				update_textConsole = true;
				applet.repaint();
			} else if (light_boolean_array(smalltile)) {
				for (int i=0; i<smalltile.length; i++) {
					if (smalltile[i].light) {
						smalltile[i].light = false;
						update_smalltile[i] = true;
						applet.repaint();
					} 
				}
			}
			e.consume();
		}
		public void mouseExited(MouseEvent e) {
			if (!clicked) {
				textConsole.clear();
				update_textConsole = true;
				applet.repaint();
				scroll_text_left = false;
				scroll_text_right = false;
			} else {
				clicked = false;
			}
		}
		
		public void mouseDragged(MouseEvent e) { }
		public void mouseMoved(MouseEvent e) {
			int sx = (pane.getScrollPosition()).x;
						
			if ((e.getX() - (pane.getScrollPosition()).x) 
			<= ((pane.getSize()).width / 2)) {
				if (!scroll_text_left) {
					textConsole.clear();
					if (sx >= 40) {
						textConsole.println(MSG_SCROLLPANE_LEFT[language]);
					}
					update_textConsole = true;
					applet.repaint();
					scroll_text_left = true;
					scroll_text_right = false;
				}
			} else {
				if (!scroll_text_right) {
					textConsole.clear();
					if (sx <= (width - 40)) {
						textConsole.println(MSG_SCROLLPANE_RIGHT[language]);
					}
					update_textConsole = true;
					applet.repaint();
					scroll_text_left = false;
					scroll_text_right = true;
				}
			}			
		
			e.consume();
		}	
	}

	// ---------------------------------------------------------------
	// Methoden der Klasse
	// ---------------------------------------------------------------
	// Initalisierung
	// ---------------------------------------------------------------
	public void init () {
		dim = getSize();
		logo = this.getImage(this.getDocumentBase(), this.getParameter("logo"));
		logo_width = (new Integer(this.getParameter("logo_width"))).intValue();
		logo_height = (new Integer(this.getParameter("logo_height"))).intValue();
		
		pcp = new String[(new
			Integer(this.getParameter("pcp_length"))).intValue()][2];
		String pcp_sol = this.getParameter("pcp_solution");
		if (pcp_sol != null) { 
			pcp_solution = new int[pcp_sol.length()];
			for (int i=0; i<pcp_sol.length(); i++) 
				pcp_solution[i] = new Integer(new String(new
					char[]{pcp_sol.charAt(i)})).intValue() - 1;
		}
		
		smalltile = new PcpTile[pcp.length];
		update_smalltile = new boolean[pcp.length];
		for (int i=0; i<pcp.length; i++) update_smalltile[i] = false;
		bigtile = new PcpTile[pcp.length];
		
		String tile_txt = this.getParameter("tile_text");
		if (tile_txt != null) tile_text = (new Boolean(tile_txt)).booleanValue();
			else tile_text = false;
		String tile_bor = this.getParameter("tile_border");
		if (tile_bor != null) tile_border = (new Boolean(tile_bor)).booleanValue();
			else tile_border = false;
		
		String lang = this.getParameter("language");
		if (lang == null) language = 0;
			else if (lang.equals("de")) language = 0;
				else if (lang.equals("en")) language = 1;
			
		for (int i=0; i<pcp.length; i++) {
			pcp[i][0] = this.getParameter("pcp_u" + (i + 1));
			pcp[i][1] = this.getParameter("pcp_v" + (i + 1));
			smalltile[i] = new PcpTile(pcp[i][0], pcp[i][1], 
				pcpcolor[i % pcpcolor.length], background, 20 + i * (dim.width / 6),
				20 + logo_height, 2, 25, getFontMetrics(WuiConsoleFont));

			bigtile[i] = new PcpTile(pcp[i][0], pcp[i][1],
				pcpcolor[i % pcpcolor.length].brighter(), background,
				getFontMetrics(WuiConsoleFont), tile_text, tile_border);
		}
		
		newButton = new WuiButton(getFontMetrics(WuiButtonFont),
			WuiButtonFont, "New", 20 + 0 * (dim.width / 8), dim.height - 40);
		undoButton = new WuiButton(getFontMetrics(WuiButtonFont),
			WuiButtonFont, "Undo", 20 + 1 * (dim.width / 8), dim.height - 40);
		hintButton = new WuiButton(getFontMetrics(WuiButtonFont),
			WuiButtonFont, "Hint", 20 + 2 * (dim.width / 8), dim.height - 40);
		textConsole = new WuiConsole(getFontMetrics(WuiConsoleFont),
			WuiConsoleFont, 3, background, 20 + 3 * (dim.width / 8), dim.height	- 40);
		
		this.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 200));
		
		boardPane = new ScrollPane(ScrollPane.SCROLLBARS_NEVER);
		boardPane.setSize(dim.width - 40, 84);
		boardPane.setBackground(background);
		board = new PcpBoard(this, boardPane, MAX_PCP_BOARD_WIDTH, 80, bigtile, background);
		boardPane.add(board);
		
		this.add(boardPane);
		this.addMouseListener(this);
		this.addMouseMotionListener(this);
		
		textConsole.clear();
		textConsole.println(MSG_START[language]);
		update_textConsole = true;
		repaint();
	}
	// ---------------------------------------------------------------
	// Beendigung
	// ---------------------------------------------------------------
	public void destroy () {
		logo.flush();
	}
	// ---------------------------------------------------------------
	// Paintfunktion des Applets
	// ---------------------------------------------------------------
	public void paint (Graphics g) {
		g.setColor(background);
		g.fillRect(0,0,dim.width,dim.height);
		g.drawImage(logo,dim.width-logo.getWidth(this),0,this);
		g.setColor((WuiRootColor.darker()).darker());
		g.setFont(WuiTitleFont);
		g.drawString("PCP - Puzzle", (dim.width - logo_width -
			(getFontMetrics(WuiTitleFont)).stringWidth("PCP - Puzzle")) / 2, 
			logo_height / 3 +
			(getFontMetrics(WuiTitleFont)).getHeight() / 2);
		g.setFont(WuiConsoleFont);
		g.drawString("Dr. Johannes Waldmann  &  Heiko Stamer", (dim.width - logo_width -
			(getFontMetrics(WuiConsoleFont)).
			stringWidth("Dr. Johannes Waldmann 	&  Heiko Stamer")) / 2,
			logo_height / 2 + logo_height / 4 +
			(getFontMetrics(WuiConsoleFont)).getHeight() / 2);
				
		newButton.paint(g);
		undoButton.paint(g);
		hintButton.paint(g);
		textConsole.paint(g);
		
		for (int i=0; i<smalltile.length; i++) smalltile[i].paint(g);
	}
	// ---------------------------------------------------------------
	// Updatefunktion des Applets
	// ---------------------------------------------------------------
	public boolean update_boolean_array (boolean[] array) {
		boolean tmp = false;
		for (int i = 0; i < array.length; i++) {
			if (array[i]) {
				tmp = true;
				break;
			}
		}
		return tmp;
	}
	
	public boolean light_boolean_array (PcpTile[] array) {
		boolean tmp = false;
		for (int i = 0; i < array.length; i++) {
			if (array[i].light) {
				tmp = true;
				break;
			}
		}
		return tmp;
	}
	
	public void update (Graphics g) {
		if (update_logo || update_newButton || update_undoButton ||
			update_hintButton || update_textConsole ||
			update_boolean_array(update_smalltile)) {
			if (update_logo) {
				g.drawImage(logo,dim.width-logo.getWidth(this),0,this);
				update_logo = false;
			}
			if (update_newButton) {
				newButton.paint(g);
				update_newButton = false;
			}
			if (update_undoButton) {
				undoButton.paint(g);
				update_undoButton = false;
			}
			if (update_hintButton) {
				hintButton.paint(g);
				update_hintButton = false;
			}
			if (update_textConsole) {
				textConsole.paint(g);
				update_textConsole = false;
			}
			if (update_boolean_array(update_smalltile)) {
				for (int i = 0; i<smalltile.length; i++) {
					if (update_smalltile[i]) {
						smalltile[i].paint(g);
						update_smalltile[i] = false;
					}
				}
			}
		} else {
			g.drawImage(logo,dim.width-logo.getWidth(this),0,this);
			g.setColor((WuiRootColor.darker()).darker());
			g.setFont(WuiTitleFont);
			g.drawString("PCP - Puzzle", (dim.width - logo_width -
				(getFontMetrics(WuiTitleFont)).stringWidth("PCP - Puzzle")) / 2, 
				logo_height / 3 +
				(getFontMetrics(WuiTitleFont)).getHeight() / 2);
			newButton.paint(g);
			undoButton.paint(g);
			hintButton.paint(g);
			textConsole.paint(g);
			for (int i=0; i<smalltile.length; i++) smalltile[i].paint(g);
		}
	}
	// ---------------------------------------------------------------
	// Informationsfunktionen des Applets
	// ---------------------------------------------------------------
	public String getAppletInfo () {
		return(
"\nPcpPuzzle v0.0.1 - (C) 2000 Heiko Stamer <stamer@informatik.uni-leipzig.de>\n"+
"                       Dr. Johannes Waldmann <joe@informatik.uni-leipzig.de>\n"+
"\n"+
"    A little puzzle-game based on Post Correspondence Problem (PCP)\n"+
"\n"+    
"	 http://stinfwww.informatik.uni-leipzig.de/~mai97ixb\n"+
"	 http://www.informatik.uni-leipzig.de/~joe/edu/spielplatz/pcp\n"+
"\n"+
"    This program is free software; you can redistribute it and/or modify\n"+
"    it under the terms of the GNU General Public License as published by\n"+
"    the Free Software Foundation; either version 2 of the License, or\n"+
"    (at your option) any later version.\n"+
"\n"+
"    This program is distributed in the hope that it will be useful,\n"+
"    but WITHOUT ANY WARRANTY; without even the implied warranty of\n"+
"    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"+
"    GNU General Public License for more details.\n"+
"\n"+
"    You should have received a copy of the GNU General Public License\n"+
"    along with this program; if not, write to the Free Software\n"+
"    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n");
	}
	public String[][] getParameterInfo () {
		return(new String[][] {
		{"language","[String] = \"de\", \"en\"","language of help messages"},
		{"logo", "[String]", "filename of an logo image"},
		{"logo_width", "[integer]", "width of the logo image"},
		{"logo_height", "[integer]", "height of the logo image"},
		{"tile_text", "[boolean]", "show binary orientation on tiles"},
		{"tile_border", "[boolean]", "show black border around tiles"},
		{"pcp_length", "[integer]", "size of the PCP"},
		{"pcp_solution", "[String]", "one solution of the PCP"},
		{"pcp_u1","[String]", "first string of first pair (1)"},
		{"pcp_v1","[String]", "second string of first pair (1)"},
		{"...", "...", "..."},
		{"pcp_uN","[String]", "first string of last pair (N)"},
		{"pcp_vN","[String]", "second string of last pair (N)"}
		});
	}
	
	public void mousePressed(MouseEvent e) { }
	public void mouseReleased(MouseEvent e) { }
	public void mouseClicked(MouseEvent e) {
		if (newButton.inside(e.getX(),e.getY())) {
			while (pcp_i != -1) {
				board.removeLast();
				pcp_u = pcp_u.substring(0,pcp_u.length() -
					pcp[pcp_l[pcp_i]][0].length());
				pcp_v = pcp_v.substring(0,pcp_v.length() -
					pcp[pcp_l[pcp_i]][1].length());
				pcp_l[pcp_i] = -1;
				pcp_i--;
			}
			boardPane.setScrollPosition(0,0);
			textConsole.clear();
			textConsole.println(MSG_START[language]);
			update_textConsole = true;
			repaint();
		} else if (undoButton.inside(e.getX(),e.getY())) {
			if (pcp_i >= 0) {
				board.removeLast();
				pcp_u = pcp_u.substring(0,pcp_u.length() -
					pcp[pcp_l[pcp_i]][0].length());
				pcp_v = pcp_v.substring(0,pcp_v.length() -
					pcp[pcp_l[pcp_i]][1].length());
				pcp_l[pcp_i] = -1;
				pcp_i--;
			} else {
				textConsole.clear();
				textConsole.println(MSG_CANNOT_UNDO[language]);
				update_textConsole = true;
				repaint();
				(this.getToolkit()).beep();
			}
		} else if ((hintButton.inside(e.getX(),e.getY())) && 
		(pcp_solution != null)) {
			if (pcp_i == -1) {
				// naechstes richtiges Teil zeigen
				board.add(pcp_solution[0]);
				pcp_i++;
				pcp_u = pcp_u + pcp[pcp_solution[0]][0];
				pcp_v = pcp_v + pcp[pcp_solution[0]][1];
				pcp_l[pcp_i] = pcp_solution[0];
				if (pcp_u.length() == pcp_v.length()) {
					textConsole.clear();
					textConsole.println(MSG_OUR_SOLUTION[language]);
					update_textConsole = true;
					repaint();
					(this.getToolkit()).beep();
				}
			} else if ((pcp_i < (pcp_solution.length - 1)) || 
			pcp_i == (pcp_solution.length - 1)){
				int j = 0;
				for (j = 0; j < pcp_i; j++) 
					if (pcp_l[j] != pcp_solution[j]) break;
				if (pcp_i > j) {
					// letztes Teil entfernen
					board.removeLast();
					pcp_u = pcp_u.substring(0,pcp_u.length() -
						pcp[pcp_l[pcp_i]][0].length());
					pcp_v = pcp_v.substring(0,pcp_v.length() -
						pcp[pcp_l[pcp_i]][1].length());
					pcp_l[pcp_i] = -1;
					pcp_i--;
				} else if (pcp_i == j) {
					if (pcp_l[j] != pcp_solution[j]) {
						// letztes Teil entfernen
						board.removeLast();
						pcp_u = pcp_u.substring(0,pcp_u.length() -
							pcp[pcp_l[pcp_i]][0].length());
						pcp_v = pcp_v.substring(0,pcp_v.length() -
							pcp[pcp_l[pcp_i]][1].length());
						pcp_l[pcp_i] = -1;
						pcp_i--;
					} else if (j == (pcp_solution.length - 1)) {
						// nix tun
						if (pcp_u.length() == pcp_v.length()) {
							textConsole.clear();
							textConsole.println(MSG_YOUR_ALLREADY_SOLUTION[language]);
							update_textConsole = true;
							repaint();
							(this.getToolkit()).beep();
						} else {
							System.err.println("Hierhin kommen wir bestimmt nicht!");
						}
					} else {
						// naechstes richtiges Teil zeigen
						board.add(pcp_solution[j + 1]);
						pcp_i++;
						pcp_u = pcp_u + pcp[pcp_solution[j + 1]][0];
						pcp_v = pcp_v + pcp[pcp_solution[j + 1]][1];
						pcp_l[pcp_i] = pcp_solution[j + 1];
						if (pcp_u.length() == pcp_v.length()) {
							textConsole.clear();
							textConsole.println(MSG_OUR_SOLUTION[language]);
							update_textConsole = true;
							repaint();
							(this.getToolkit()).beep();
						}
					}
				} else {
					// naechstes richtiges Teil zeigen
					board.add(pcp_solution[j + 1]);
					pcp_i++;
					pcp_u = pcp_u + pcp[pcp_solution[j + 1]][0];
					pcp_v = pcp_v + pcp[pcp_solution[j + 1]][1];
					pcp_l[pcp_i] = pcp_solution[j + 1];
					if (pcp_u.length() == pcp_v.length()) {
						textConsole.clear();
						textConsole.println(MSG_OUR_SOLUTION[language]);
						update_textConsole = true;
						repaint();
						(this.getToolkit()).beep();
					}
				}
			} else if (pcp_i > (pcp_solution.length - 1)) {
				// letztes Teil entfernen
				board.removeLast();
				pcp_u = pcp_u.substring(0,pcp_u.length() -
					pcp[pcp_l[pcp_i]][0].length());
				pcp_v = pcp_v.substring(0,pcp_v.length() -
					pcp[pcp_l[pcp_i]][1].length());
				pcp_l[pcp_i] = -1;
				pcp_i--;
			} else {
				System.err.println("Das darf auch nicht passieren!");
			}
		} else if ((hintButton.inside(e.getX(),e.getY())) && 
		(pcp_solution == null)) {
			textConsole.clear();
			textConsole.println(MSG_NO_HINT[language]);
			update_textConsole = true;
			repaint();
			(this.getToolkit()).beep();
		} else {
		for (int i=0; i<smalltile.length; i++) {
			if (smalltile[i].inside(e.getX(),e.getY())) {
				if (pcp_i < (MAX_PCP_LSG_LEN - 1)) {
					String tmp_u = pcp_u + pcp[i][0];
					String tmp_v = pcp_v + pcp[i][1];
					boolean check = false;
					if (tmp_u.length() < tmp_v.length()) {
						check = tmp_v.startsWith(tmp_u);
					} else {
						check = tmp_u.startsWith(tmp_v);
					} 
					if (check) {
						board.add(i);
						pcp_i++;
						pcp_u = pcp_u + pcp[i][0];
						pcp_v = pcp_v + pcp[i][1];
						pcp_l[pcp_i] = i;
						if (pcp_u.length() == pcp_v.length()) {
							textConsole.clear();
							textConsole.println(MSG_YOUR_SOLUTION[language]);
							update_textConsole = true;
							repaint();
							(this.getToolkit()).beep();
						} else {
							textConsole.clear();
							update_textConsole = true;
							repaint();
						}
					} else {
						textConsole.clear();
						textConsole.println(MSG_WRONG_TILE[language]);
						update_textConsole = true;
						repaint();
						(this.getToolkit()).beep();
					}
				} else {
					textConsole.clear();
					textConsole.println(MSG_MAX_TILE[language]);
					update_textConsole = true;
					repaint();
					(this.getToolkit()).beep();
				}
			} 
		}
		}
		
		e.consume();
	}
	public void mouseEntered(MouseEvent e) { }
	public void mouseExited(MouseEvent e) { 
		if (newButton.light) {
			newButton.light = false;
			update_newButton = true;
			textConsole.clear();
			update_textConsole = true;
			repaint();
		} else if (undoButton.light) {
			undoButton.light = false;
			update_undoButton = true;
			textConsole.clear();
			update_textConsole = true;
			repaint();
		} else if (hintButton.light) {
			hintButton.light = false;
			update_hintButton = true;
			textConsole.clear();
			update_textConsole = true;
			repaint();
		} else if (light_boolean_array(smalltile)) {
			for (int i=0; i<smalltile.length; i++) {
				if (smalltile[i].light) {
					smalltile[i].light = false;
					update_smalltile[i] = true;
					textConsole.clear();
					update_textConsole = true;
					repaint();
				} 
			}
		} else {
			textConsole.clear();
			update_textConsole = true;
			repaint();
		}
		e.consume();
	}
	
	public void mouseDragged(MouseEvent e) { }
	public void mouseMoved(MouseEvent e) {
		if (newButton.inside(e.getX(),e.getY()) && !(newButton.light))  {
			newButton.light = true;
			update_newButton = true;
			textConsole.clear();
			textConsole.println(MSG_NEW_BUTTON[language]);
			update_textConsole = true;
			repaint();
		} else if (!newButton.inside(e.getX(),e.getY()) && newButton.light) {
			newButton.light = false;
			update_newButton = true;
			textConsole.clear();
			update_textConsole = true;
			repaint();
		}
		
		if (undoButton.inside(e.getX(),e.getY()) && !(undoButton.light))  {
			undoButton.light = true;
			update_undoButton = true;
			textConsole.clear();
			textConsole.println(MSG_UNDO_BUTTON[language]);
			update_textConsole = true;
			repaint();
		} else if (!undoButton.inside(e.getX(),e.getY()) && undoButton.light) {
			undoButton.light = false;
			update_undoButton = true;
			textConsole.clear();
			update_textConsole = true;
			repaint();
		}
		
		if (hintButton.inside(e.getX(),e.getY()) && !(hintButton.light))  {
			hintButton.light = true;
			update_hintButton = true;
			textConsole.clear();
			textConsole.println(MSG_HINT_BUTTON[language]);
			update_textConsole = true;
			repaint();
		} else if (!hintButton.inside(e.getX(),e.getY()) && hintButton.light) {
			hintButton.light = false;
			update_hintButton = true;
			textConsole.clear();
			update_textConsole = true;
			repaint();
		}
		
		for (int i=0; i<smalltile.length; i++) {
			if (smalltile[i].inside(e.getX(),e.getY()) && !(smalltile[i].light)) {
				smalltile[i].light = true;
				update_smalltile[i] = true;
				textConsole.clear();
				textConsole.println(MSG_CHOOSE_TILE[language]);
				update_textConsole = true;
				repaint();
			} else if (!smalltile[i].inside(e.getX(),e.getY()) && smalltile[i].light){
				smalltile[i].light = false;
				update_smalltile[i] = true;
				textConsole.clear();
				update_textConsole = true;
				repaint();
			}
		}
		
		if ((e.getY() <= logo_height) && (e.getX() < dim.width-logo.getWidth(this))
		    && !(logo_text1)) {
			logo_text1 = true;
			logo_text2 = false;
			textConsole.clear();
			textConsole.println(MSG_VER);
			update_textConsole = true;
			repaint();
		} else if ((e.getY() <= logo_height) && (e.getX() >= dim.width-logo.getWidth(this))
		    && !(logo_text2)) {
			logo_text2 = true;
			logo_text1 = false;
			textConsole.clear();
			textConsole.println(MSG_UNI);
			update_textConsole = true;
			repaint();
		} else if (((e.getY() > logo_height) && (logo_text1)) || 
			((e.getY() > logo_height) && (logo_text2))){
			logo_text1 = false;
			logo_text2 = false;
			textConsole.clear();
			update_textConsole = true;
			repaint();
		}
		
		e.consume();
	}	
}

