/* -------------------------------------------------------------
JAVA ORVILLE  copyright 1997 Jim Bumgardner
jbum@thepalace.com

2/14/97 JAB
	Added support for yes/no questions using ";".
	Added support for hidden answers using //
------------------------------------------------------------- */

import java.applet.Applet;
import java.util.*;
import java.awt.*;

 
public class Orville extends Applet {
	Label 			screenVis,outputLabel,title;
	int				cardRank;
	int				cardSuit;
	int				qState,mState;
	int				lastX,lastY;
	String[]		suitStrs;
	String[]		rankStrs;
	Panel			menuButtons,screens,textScreen;
	Panel			screenMainMenu;
	TextField		inputFld;
	// Button			debugBut;
	ButtonCanvas	exitBut;
	ButtonCanvas	continueBut;
	ButtonCanvas	mainBut;
	ButtonCanvas	textualBut,visualBut;
	// Fake question stuff
	int				charNbr;
	String			queryStr = "What exactly is it that we are thinking of?";
	String			answerStr="";
	boolean			isHidden,wasHidden,negateFlag;


	public void init() {

		// BorderLayout layout = new BorderLayout();
		suitStrs = new String[4];
		suitStrs[0] = "Heart";
		suitStrs[1] = "Spade";
		suitStrs[2] = "Diamond";
		suitStrs[3] = "Club";
		rankStrs = new String[13];
		rankStrs[0] = "Ace";
		rankStrs[1] = "Two";
		rankStrs[2] = "Three";
		rankStrs[3] = "Four";
		rankStrs[4] = "Five";
		rankStrs[5] = "Six";
		rankStrs[6] = "Seven";
		rankStrs[7] = "Eight";
		rankStrs[8] = "Nine";
		rankStrs[9] = "Ten";
		rankStrs[10] = "Jack";
		rankStrs[11] = "Queen";
		rankStrs[12] = "King";

		charNbr = 0;
		isHidden = false;
		wasHidden = false;
		negateFlag = false;

		// this.setBackground(Color.black);
		// this.setForeground(Color.green);
		this.setFont( new Font("Helvetica",Font.PLAIN,18));

		this.setLayout(new BorderLayout());

		/*
		title = new Label("CARD RECOGNITION EXPERIMENT");
		title.setAlignment(Label.CENTER);
		title.setBackground(Color.black);
		title.setForeground(Color.white);
		this.add("North",title);
		*/

		menuButtons = new Panel();
		menuButtons.setFont( new Font("Helvetica",Font.PLAIN,12));

		menuButtons.setLayout(new GridLayout(1,3,60,0));
		this.add("South",menuButtons);

		exitBut = new ButtonCanvas("EXIT");
		menuButtons.add(exitBut);

		continueBut = new ButtonCanvas("CONTINUE");
		menuButtons.add(continueBut);

		mainBut = new ButtonCanvas("MENU");
		menuButtons.add(mainBut);

		// !! TEST TEST
		// debugBut = new Button("100,100  200,200  i=?");
		// debugBut.setFont( new Font("Helvetica",Font.PLAIN,10));
		// menuButtons.add("Debug",debugBut);

		screens = new Panel();
		screens.setLayout(new CardLayout());
		this.add("Center",screens);

		// Set up main menu...
		screenMainMenu = new Panel();
		screenMainMenu.setFont( new Font("Helvetica",Font.PLAIN,18));

		screenMainMenu.setLayout(new GridLayout(3,1,32,32));
		textualBut = new ButtonCanvas("Verbal Recognition");
		screenMainMenu.add(textualBut);
		visualBut = new ButtonCanvas("Visual Recognition");
		screenMainMenu.add(visualBut);
		screens.add("MAIN",screenMainMenu);

		// Set up the visual screen..
		screenVis = new Label("Hold up the Card...");
		screenVis.setAlignment(Label.CENTER);
		screens.add("VIS1",screenVis);

		// Set up the textual input screen..
		textScreen = new Panel();
		textScreen.setLayout(new GridLayout(4,1,32,0));
		outputLabel = new Label("What is your request?");
		outputLabel.setAlignment(Label.CENTER);
		textScreen.add(outputLabel);
		inputFld = new TextField();
		textScreen.add(inputFld);
		screens.add("TXT1",textScreen);

		qState = 0;
		// inputFld.selectAll();
		SetScreenState(0);
	}

	public Insets insets() {
		return new Insets(20,20,20,20);
	}

	public boolean strStr(String a, String b, boolean ignoreCase) {
		int	x;
		int sLen,bLen,aLen;
		aLen = a.length();
		bLen = b.length();
		if (aLen < bLen)
			return false;
		sLen = aLen - bLen;
		for (x = 0; x <= sLen; ++x) {
			if (a.regionMatches(ignoreCase,x,b,0,bLen))
				return true;
		}
		return false;
	}
	
	public boolean isYesNoQuestion(String s)
	{
		if (s.startsWith("am ") ||
			s.startsWith("are ") ||
			s.startsWith("is ") ||
			s.startsWith("do ") ||
			s.startsWith("does ") ||
			s.startsWith("did ") ||
			s.startsWith("will ") ||
			s.startsWith("was ") ||
			s.startsWith("were ") ||
			s.startsWith("can ") ||
			s.startsWith("shall ") ||
			s.startsWith("shall ") ||
			s.startsWith("could ") ||
			s.startsWith("would ") ||
			s.startsWith("should "))
			return true;
		else
			return false;
		
	}

	
	public void SetScreenState(int state)
	{
		switch (state) {
		case 0:
			((CardLayout) screens.getLayout()).show(screens,"MAIN");
			break;
		case 1:	// Graphical
			screenVis.setText("Hold up the Card...");
			screenVis.setAlignment(Label.CENTER);
			((CardLayout) screens.getLayout()).show(screens,"VIS1");
			qState = 0;
			break;
		case 2:	// Textual
			outputLabel.setText("What is your request?");
			((CardLayout) screens.getLayout()).show(screens,"TXT1");
			qState = 0;
			break;
		}
		mState = state;
	}
	
	public int ComputeComponentInput(double xd,double yd, int range)
	{
		int	retVal = -1;
		if (range <= 4) {
			retVal = (int) (xd*range);
		}
		else if (range <= 8) {
			if (yd < .5)
				retVal = (int) (xd*range/2);
			else
				retVal = (int) (range/2+xd*range/2);
		}
		return retVal;
	}

	public boolean handleEvent(Event e) {
		switch (e.id) {
		case Event.KEY_PRESS:
			if (e.target == inputFld) {
				if (e.modifiers > 1 || e.key < ' ')
					return false;
				else if (e.key == '/') {
					if (isHidden) {
						isHidden = false;
						e.key = queryStr.charAt(charNbr);
						return false;
					}
					else {
						isHidden = true;
						wasHidden = true;
						charNbr = 0;
						answerStr = "";
						e.key = queryStr.charAt(charNbr);
						charNbr++;
						return false;
					}
				}
				else if (e.key == ';') {
					negateFlag = true;
					return true;
				}
				else {
					if (isHidden) {
						answerStr = answerStr + (char) e.key;
						e.key = queryStr.charAt(charNbr);
						++charNbr;
						return false;
					}
				}
			}
			break;

		case Event.MOUSE_MOVE:
		case Event.MOUSE_DRAG:
			lastX = e.x;
			lastY = e.y;
			// This receives events for MOUSE_MOVE... but not DRAG (component probably gets it)
			break;

		case Event.ACTION_EVENT:
			if (e.target == textualBut) {
				int	input;
				input = ComputeComponentInput(textualBut.lastDX,textualBut.lastDY,8);
				cardRank = (input >= 4? 6 : 0);
				cardSuit = input % 4;
				SetScreenState(2);
			}
			else if (e.target == visualBut) {
				int	input;
				input = ComputeComponentInput(visualBut.lastDX,visualBut.lastDY,8);
				cardRank = (input >= 4? 6 : 0);
				cardSuit = input % 4;
				SetScreenState(1);
			}
			else if (e.target == continueBut) {
				int	input;
				input = ComputeComponentInput(continueBut.lastDX,continueBut.lastDY,8);

				switch (mState) {
				case 1:	// Visual Mode
					if (qState == 0) {
						cardRank += input;
						RevealCard("I see a ",screenVis);
						qState = 1;
					}
					else {
						cardRank = (input >= 4? 6 : 0);
						cardSuit = input % 4;
						qState = 0;
						SetScreenState(1);
					}
					break;
				case 2:	// Textual Mode
					switch (qState) {
					case 0:
					case 1:
					case 2:
						// Don't clear it... - need an excuse to continue...
						cardRank += input;
						RevealCard("Your card is the ",outputLabel);
						qState = 3;
						break;
					case 3:
						// Clear it... so we can ask the question...
						cardRank = (input >= 4? 6 : 0);
						cardSuit = input % 4;
						qState = 0;
						inputFld.setText("");
						SetScreenState(2);
						break;
					}
					break;
				}
			}
			else if (e.target == mainBut) {
				SetScreenState(0);
			}
			else if (e.target == exitBut) {
				SetScreenState(0);
			}
			else if (e.target == inputFld) {
				if (wasHidden) {
					wasHidden = false;
					outputLabel.setText(answerStr);
					answerStr = "";
					return true;
				}
				else {
					String	txt;
					int		nSpaces;
					txt = inputFld.getText();
					if (isYesNoQuestion(txt))
					{
						if (negateFlag)
							outputLabel.setText("No");
						else
							outputLabel.setText("Yes");
						negateFlag = false;
						return true;
					}
					else {
						switch (qState) {
						case 0:
							cardRank = 0;
							cardSuit = 0;
							if (strStr(txt," card",true)) {
								if (strStr(txt,"now",true))
									cardSuit += 1;
								if (strStr(txt,"tell us",true))
									cardSuit += 2;
								if (strStr(txt,"the name",true))
									cardRank += 6;
								if (strStr(txt,"me the card",true))
									cardRank = 12;
								if (strStr(txt,"us the card",true))
									cardRank = 12;
								if (cardRank == 12) {
									outputLabel.setText("I see the King of " + suitStrs[cardSuit] + "s");
									qState = 0;
								}
								else if (cardSuit == 1 || cardSuit == 3) {
									outputLabel.setText("I am seeing the color black...");
									qState = 1;
								}
								else {
									outputLabel.setText("I am seeing the color red...");
									qState = 1;
								}
							}
							else {
								outputLabel.setText("Go on...");
								qState = 0;
							}
							break;
						case 1:
							if (txt.length() == 0)
								cardRank += 3;
							outputLabel.setText("Yes, the card is definitely a " + suitStrs[cardSuit] + "...");
							qState = 2;
							break;
						case 2:
							if (txt.length() == 0)
								cardRank += 2;
							else if (txt.indexOf(' ') >= 0)
								cardRank += 1;
							RevealCard("Your card is the ",outputLabel);
							qState = 3;
							break;
						}
					}
				} // end if
				inputFld.setText("");
				negateFlag = false;
				return true;
			} // end if
		} // end switch
		return false;
	} // end handleEvent

	public void RevealCard(String prefix,Label lab)
	{
		if (cardRank > 12)
			lab.setText(prefix+"Joker");
		else
			lab.setText(prefix+rankStrs[cardRank] +
						" of " +
						suitStrs[cardSuit] + "s");
	}
}

/*
 * a pure Java button
 */
public class ButtonCanvas extends Canvas {
	Color stableColor;
	String text;
	Font font;
	Dimension preferredSize;
	boolean state = true;
	double	lastDX,lastDY;

	ButtonCanvas() {
		this("");
	}
	ButtonCanvas(String l) {
		text = l;	// remember the text
	}

	/*
	 * this draws the text 
	 */
	public void paint(Graphics g) {
		Dimension d = size();
		FontMetrics fm;
		int x;
		int y;

		fm = g.getFontMetrics();
		g.setColor(getBackground());
		g.draw3DRect(0,0,d.width-1,d.height-1,state);

		g.setColor(getForeground());
		x = (d.width - fm.stringWidth(text))/2;
		g.drawString(text,x,(d.height-fm.getHeight())/2 + fm.getAscent());
	}
	/*
	 * min and preferred are always same
	 */
    public Dimension minimumSize() {
		int x,y;
		int h,w;
		FontMetrics fm = getFontMetrics(getFont());
		x = fm.stringWidth(text) + fm.getMaxAdvance() + 8;
		y = fm.getLeading() + fm.getAscent() + fm.getDescent() + 8;
		preferredSize = new Dimension(x,y);
		return preferredSize;
	}

	public Dimension preferredSize() {
		return minimumSize();
	}

	/*
	 * tell parent, and adjust for us 
	 */
	public void setFont(Font f) {
		super.setFont(f);
		resize(preferredSize());
	}

	/*
	 * show button in depressed state
	 */
	public boolean mouseDown(Event evt, int x, int y) {
		if (evt.target == this)  {
			state = false;
			repaint();
			return true;
		}
		return false;
	}
	/*
	 * show button in normal state and create new 
	 * ACTION_EVENT for our container
	 */
	public boolean mouseUp(Event evt, int x, int y) {
		Dimension d = size();
		if (evt.target == this)  {
			lastDX = x / (double) d.width;
			lastDY = y / (double) d.height;
			state = true;
			repaint();
			Event e = new Event(this, evt.when, Event.ACTION_EVENT,
				evt.x,evt.y,evt.key,evt.modifiers,(Object)text);
			// System.out.println("after creating event");
			deliverEvent(e);	// ship off ACTION_EVENT
			return true;
		}
		return false;
	}

	/*
	 * weird fakeout for disable/enable
	 */
	public void setForeGround(Color c) {
		stableColor = c;
		super.setForeground(c);
	}

	public Color getForeGround() {
//		super.getForeground();
		return stableColor;
	}

	public void enable() {
		super.setForeground(stableColor);
		super.enable();
		repaint();
	}

	public void disable() {
		stableColor = super.getForeground();
		super.disable();
		super.setForeground(Color.gray);
		repaint();
	}
}
