/*
 * @(#)WordMatch.java	1.5  10/25/95 11:07:56
 * @author Patrick Chan
 *
 * added Czech, Esperanto and Italian:
 * reinhard.foessmeier@ixos.de, 1996-12-30
 *
 * Copyright (c) 1994-1995 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and
 * without fee is hereby granted. 
 * Please refer to the file http://java.sun.com/copy_trademarks.html
 * for further important copyright and trademark information and to
 * http://java.sun.com/licensing.html for further important licensing
 * information for the Java (tm) Technology.
 * 
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 * 
 * THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE
 * CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE
 * PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT
 * NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE
 * SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
 * SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE
 * PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES").  SUN
 * SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR
 * HIGH RISK ACTIVITIES.
 */

import java.awt.*;
import java.awt.image.ImageObserver;

public class WordMatch extends java.applet.Applet
        implements Runnable {
    /* Number of displayed cards and word. */
    final int numCards = 4;

    /* Currently selected language. */
    int curLanguage;

    /* Width and size().height of language box. */
    int languageBoxW, languageBoxH;

    /* Size of border around language box. */
    int languageBoxB;

    /* Line spacing for wordFont. */
    int languageBoxSp;

    /* Font of languageBox. */
    Font languageFont;
    FontMetrics languageFontMt;

    /** 
     * Cards
     */ 

    /* card[i] refers to the i'th word. */
    int cards[];

    /* Currently selected card. */
    int curCard;

    /* Size of cards. */
    int cardW, cardH;

    /* Location and size of card box. */
    int cardBoxX, cardBoxY, cardBoxW, cardBoxH;

    /* Sp between cards. */
    int cardBoxSp;

    /** 
     * Sound box
     */ 

    /* Location of sound box. */
    int soundBoxX, soundBoxY;

    /* Sound image. */
    Image soundImage;
    final int soundImageWidth = 32;
    final int soundImageHeight = 32;

    /** 
     * Words
     */ 

    /* word[i] refers to the i'th card. */
    int words[];

    /* Location and size of word box. */
    int wordBoxX, wordBoxY, wordBoxW;

    /* Font used to paint words.. */
    Font wordFont;
    FontMetrics wordFontMt;

    /**
     * Scoring
     */

    /* Location and size of score box. */
    int scoreBoxX, scoreBoxY, scoreBoxW, scoreBoxH;

    /* Message to be displayed next to the score box. */
    String msg;
    
    /* If true, the user has successfully matched all the words. */
    boolean done;
    
    /** 
     * Links
     */ 

    /* links[i] == j -> cards[i] is linked to words[j]. */
    int links[];

    /** 
     * Misc
     */ 

    /* Images of pictures.  There is one image for each different word. 
       -1 => not loaded. */
    Image images[];
    int imageWidth[];
    int imageHeight[];

    /* Average size().width and size().height of an M character in the font. */
    int charW, charH;

    /**
     * Initialize the applet. Resize and load images.
     */
    public void init() {
        int maxWidth = 0;

        cards = new int[numCards];
        words = new int[numCards];
        links = new int[numCards];
        images = new Image[englishWords.length];
        imageWidth = new int[englishWords.length];
        imageHeight = new int[englishWords.length];
        curLanguage = 0;

        // Setup the language selection box.
        languageFont = new java.awt.Font("Helvetica", Font.PLAIN, 14);
        languageFontMt = getFontMetrics(languageFont);
        languageBoxSp = 3;

        for (int i=0; i<languages.length; i++) {
            int w = languageFontMt.stringWidth(languages[i]);
            if (w > maxWidth) {
                maxWidth = w;
            }
        }
        languageBoxB = 2;
        languageBoxW = maxWidth + 2 * languageFontMt.charWidth('M') 
                        + 2 * languageBoxB;
        languageBoxH = languages.length
            * (languageFontMt.getHeight() + languageBoxSp) 
            + languageBoxSp + 2 * languageBoxB;

	cardW = cardH = 64;
        cardBoxSp = 5;
        cardBoxX = languageBoxW + cardW;
        cardBoxY = 0;
        cardBoxW = cardW;
        cardBoxH = (cardH + cardBoxSp) * (numCards - 1) + cardH;

        // Setup sound box.
        soundImage = getImage(getCodeBase(), "rsrc/sound.gif");
        soundBoxX = (languageBoxW - soundImageWidth) / 2;
        soundBoxY = languageBoxH + 10;
        soundBoxX = languageBoxW + 10;
        soundBoxY = (languageBoxH/2);

        // Setup word box.
        wordFont = new java.awt.Font("Helvetica", Font.PLAIN, 18);
        wordFontMt = getFontMetrics(wordFont);
        charW = wordFontMt.charWidth('M');
        charH = wordFontMt.getHeight();
        wordBoxX = cardBoxX + cardBoxW * 2;
        wordBoxY = 0;
        wordBoxW = 0;
        for (int i=0; i<languages.length; i++) {
            for (int j=0; j<englishWords.length; j++) {
                int w = wordFontMt.stringWidth(dictionary[i][j]);
                if (w > wordBoxW) {
                    wordBoxW = w;
                }
            }
        }
        wordBoxW += 2 * charW;
        
        // Setup score box.
        scoreBoxX = 0;
        scoreBoxY = languageBoxH + charH;
        scoreBoxW = wordFontMt.stringWidth("Score") + 2 * charW;
        scoreBoxH = charH + 4;
        newRound();

        resize(wordBoxX + wordBoxW + 1, scoreBoxY + scoreBoxH + 1);

        new Thread( this ).start();
    }

    /**
     * run(): preload images in background
     */
    public void run() {
        for (int i=0; i<images.length; ++i) {
            LoadImage( i );
        }
    }

    private void LoadImage( int i ) {
        if( images[ i ] == null ) {
            images[ i ] = getImage(getCodeBase(), "rsrc/"
                + englishWords[ i ] + ".gif");
            imageWidth[i]  = images[i].getWidth(this);
            imageHeight[i] = images[i].getHeight(this);
        }
    }

    public boolean imageUpdate(Image img, int infoflags,
			       int x, int y, int width, int height) {
        if ((infoflags & (WIDTH | HEIGHT)) != 0) {
            // This is a waste of cycles.
            // Maybe I should use a hash table.
	    for (int i=0; i<images.length; ++i) {
		if (img == images[i]) {
                    if ((infoflags & WIDTH) != 0) {
                        imageWidth[i] = width;
                    }
                    if ((infoflags & HEIGHT) != 0) {
                        imageHeight[i] = height;
                    }
                }
            }
        }
        return super.imageUpdate(img, infoflags, x, y, width, height);
    }

    /**
     * Paint the screen.
     */
    public void paint(Graphics g) {
        int x, y;

        // Paint language box.
        g.setFont(languageFont);
        y = languageFontMt.getHeight() + languageBoxB;
        for (int i=0; i<languages.length; i++) {
            if (i == curLanguage) {
                g.setColor(Color.red);
            } else {
		g.setColor(Color.black);
            } 
            x = languageFontMt.stringWidth(languages[i]);
            x = (languageBoxW - x) / 2 + languageBoxB;
            g.drawString(languages[i], x, y);
            y += languageFontMt.getHeight() + languageBoxSp;
        }
	g.setColor(Color.black);
        g.draw3DRect(0, 0, languageBoxW, languageBoxH, true);
        
        // Paint sound box.
	if (soundImage != null
            && !languages[curLanguage].equals("Geekspeak")) {
	    g.drawImage(soundImage, soundBoxX, soundBoxY, this);
        }

        // Paint words.
        g.setFont(wordFont);
        x = wordBoxX;
        y = (cardH - charH) / 2 + charH;
        for (int i=0; i<numCards; i++) {
            g.drawString(dictionary[curLanguage][words[i]], x, y);
            y += cardH + cardBoxSp;
        }

        // Paint links.
        g.setColor(Color.black);
        for (int i=0; i<numCards; i++) {
            if (links[i] >= 0) {
                int y2 = links[i] * (cardH + cardBoxSp) + cardH / 2;

		y = i * (cardH + cardBoxSp) + cardH / 2;
                g.drawLine(cardBoxX + cardBoxW + 2, y,
                    wordBoxX - 2, y2);
            }
	}

        // Paint score box.
        g.draw3DRect(scoreBoxX, scoreBoxY, scoreBoxW, scoreBoxH, true);
        g.setColor( Color.white );
        g.drawLine( scoreBoxX, scoreBoxY, scoreBoxX, scoreBoxY+scoreBoxH );
        g.drawLine( scoreBoxX, scoreBoxY, scoreBoxX+scoreBoxW, scoreBoxY );
        g.setColor( Color.black );
        g.drawLine( scoreBoxX, scoreBoxY+scoreBoxH, scoreBoxX+scoreBoxW, scoreBoxY+scoreBoxH );
        g.drawLine( scoreBoxX+scoreBoxW, scoreBoxY, scoreBoxX+scoreBoxW, scoreBoxY+scoreBoxH );

        g.drawString(done ? "New" : "Score", scoreBoxX + charW, scoreBoxY + charH);
        if (msg != null) {
            g.setFont(new java.awt.Font("Helvetica", Font.PLAIN, 16));
            g.setColor(Color.blue);
            g.drawString(msg, scoreBoxW + charW, scoreBoxY + charH);
            g.setFont(wordFont);
        }
    
        // Paint cards.
        x = cardBoxX;
        y = 0;
        for (int i=0; i<numCards; i++) {
	    if (images[cards[i]] == null) {
                // need to load the image.
                LoadImage( cards[ i ] );
            } 
            if (images[cards[i]] == null) {
		g.draw3DRect(x, y, cardW, cardH, true);
                g.drawLine(x, y, x+cardW, y+cardH);
            } else if (imageWidth[cards[i]]>0 && imageHeight[cards[i]]>0) {
                int offsetX = (cardW - imageWidth[cards[i]]) / 2;
                int offsetY = (cardH - imageHeight[cards[i]]) / 2;

		g.drawImage(images[cards[i]], x + offsetX, y + offsetY, this);
            }

            if (i == curCard) {
                g.setColor(Color.red);
                // g.drawRect(x, y, cardW, cardH);
                g.drawLine(x, y, x, y+cardH);
                g.drawLine(x, y+cardH, x+cardW, y+cardH);
                g.drawLine(x, y, x+cardW, y);
                g.drawLine(x+cardW, y, x+cardW, y+cardH);
                g.setColor(Color.black);
            }
            y += cardH + cardBoxSp;
        }
    }


    /**
     * mouseDown
     */
    public boolean mouseDown(java.awt.Event evt, int x, int y) {
	int sel;

        if (!done) {
            msg = null;
        }
        if (x < languageBoxW && y < languageBoxH) {
	    // In language box.
            y -= languageBoxB;
            sel = y / (languageFontMt.getHeight() + languageBoxSp);
	    sel = Math.min(sel, languages.length - 1);
            if (curLanguage == sel) {
		newRound();
            }
	    curLanguage = sel;
	    repaint();
        } else if (x > soundBoxX && y > soundBoxY 
                    && x < soundBoxX + soundImageWidth
                    && y < soundBoxY + soundImageHeight) {
	    // In sound box.
            if (languages[curLanguage].equals("Geekspeak")) {
                // No voices for these languages yet.
                play(getCodeBase(), "rsrc/ding.au");
            } else {
		play(getCodeBase(), "rsrc/" + englishWords[cards[curCard]] 
		    + "." + languages[curLanguage] + ".au");
            }
        } else if (x > cardBoxX && y > cardBoxY 
                    && x < cardBoxX + cardBoxW
                    && y < cardBoxY + cardBoxH) {
	    // In card box.
            y -= cardBoxY;
            sel = y / (cardH + cardBoxSp);
	    sel = Math.min(sel, numCards - 1);
	    if (sel != curCard) {
                repaint( cardBoxX, curCard * (cardH + cardBoxSp), cardW+1, cardH+1 );
		curCard = sel;
                repaint( cardBoxX, curCard * (cardH + cardBoxSp), cardW+1, cardH+1 );
	    }
        } else if (!done && x > wordBoxX && y > wordBoxY 
                    && x < wordBoxX + wordBoxW
                    && y < wordBoxY + cardBoxH) {
            // In word box.
            sel = y / (cardH + cardBoxSp);
	    sel = Math.min(sel, numCards - 1);
            if (links[curCard] != sel) {
                // Break an old link if necessary.
                for (int i=0; i<numCards; i++) {
                    if (links[i] == sel) {
                        links[i] = -1;
                    }
                }
		links[curCard] = sel;
                // repaint only the affected part in the middle:
                repaint( cardBoxX + cardBoxW, 0,
                     wordBoxX - cardBoxX - cardBoxW, languageBoxH );
            }
        } else if (x > scoreBoxX && y > scoreBoxY 
                    && x < scoreBoxX + scoreBoxW
                    && y < scoreBoxY + scoreBoxH) {
            // In score box.
            if (done) {
                newRound();
                // repaint everything except language box:
                repaint( languageBoxW, 0, size().width, size().height );
                repaint( 0, languageBoxH, size().width, size().height );
                return true;
            }
            int count = 0;
            for (int i=0; i<numCards; i++) {
                if (links[i] == -1) {
                    msg = "You have not yet matched all the words.";
                    // repaint message line:
                    repaint( 0, scoreBoxY, size().width, 2*charH );
                    return true;
                }
                if (cards[i] == words[links[i]]) {
                    count++;
                }
            }
            if (count == numCards) {            
		msg = "Congratulations, they're all right!";
                done = true;
            } else if (count == 1) {
		msg = "There is only 1 correct match.";
            } else if (count == 0) {
		msg = "There are no correct matches.";
            } else {
		msg = "There are " + count + " correct matches.";
            }
            // repaint message line:
            repaint( 0, scoreBoxY, size().width, 2*charH );
        }
        return true;
    }

    /**
     * Returns an integer in the interval [0..max).
     */
    int random(int max) {
	return (int)Math.floor(Math.random() * max);
    }

    /**
     * Starts a new round.
     */
    public void newRound() {
        msg = null;
        done = false;

        // Pick new cards.
        for (int i=0; i<numCards; i++) {
            int r;
            boolean found;
    
	    do {
		r = random(englishWords.length);
		found = false;
		for (int j=0; j<i; j++) {
		    if (cards[j] == r) {
			found = true;
		    }
		}
	    } while (i > 0 && found);
	    cards[i] = r;
            words[i] = r;
            links[i] = -1;
	}
        curCard = 0;

        // Now mix up the words.
        for (int i=0; i<50; i++) {
            int r1 = random(numCards);
            int r2 = random(numCards);
            int t = words[r1];
            words[r1] = words[r2];
            words[r2] = t;
        }
    }

    /**
     * Start the applet.
     */
    public void start() {
    }

    /**
     * Stop the applet.  
     */
    public void stop() {
    }


    /* Added by K.A. Smith 10/25/95 */
    public String getAppletInfo() {
       return "Author: Patrick Chan\nVersion: 1.5";
    }


    /* List of words in various languages */
    String englishWords[] = {
        "house", "book", "java", "lips", "airplane", "UnitedStates",
        "apple", "clock", "phone", "star", "flower", "television",
        "car", "dog", "bug", "bird"};
    String cantoneseWords[] = {
        "fohng uk", "syu", "ga fe", "seuhn", "fei gei", "meih gwok",
        "pihng gwo", "jung", "dihn wa", "sing", "fa", "dihn sih",
        "che", "gau", "chuhng", "jeuk"};
    String dutchWords[] = {
        "huis", "boek", "koffie", "lippen", "vliegtuig", "Verenigde Staten",
        "appel", "klok", "telefoon", "ster", "bloem", "televisie",
        "auto", "hond", "insekt", "vogel"};
    String frenchWords[] = {
        "maison", "livre", "cafe", "lèvres", "avion", "Etats Unis",
        "pomme", "horloge", "telephone", "astre", "fleur", "television",
        "voiture", "chien", "insecte", "oiseaux"};
    String germanWords[] = {
        "Haus", "Buch", "Kaffee", "Lippen", "Flugzeug", "Vereinigte Staaten",
        "Apfel", "Uhr", "Telephon", "Stern", "Blume", "Fernsehgeraet",
        "Auto", "Hund", "Kaefer", "Vogel"};
    String geekspeakWords[] = {
        "lab", "FM (of RTFM)", "Peet's", "programming languages", "wings", "www.usa.com",
        "food", "MHz", "cellular", "splat (aka asterisk)", "inflorescence", "tube",
        "wheels", "386", "feature", "finger"};
    String hebrewWords[] = {
        "ba'it", "sefer", "java", "s'fata'im", "matos", "ar'tzot ha'brit",
        "tapu'ach", "sha'on", "tele'phon", "kochav", "pe'rach", "televizia",
        "m'chonit", "kelev", "cha'rak", "tsipor"};
    String hindiWords[] = {
        "ghar", "kitab", "coffee", "honth", "hawai jahaj", "America",
        "seb", "ghadi", "doorbhash", "sitara", "phool", "television",
        "gaadi", "kutta", "keeda", "chidiya"};
    String italianWords[] = {
        "casa", "libro", "cafè", "labbra", "aeroplano", "Stati Uniti",
        "mela", "orologio", "telefono", "stella", "fiore", "televisione",
        "macchina", "cane", "insetto", "uccello"};
    String japaneseWords[] = {
        "ie", "hon", "kouhii", "kuchibiru", "hikouki", "Amerika",
        "ringo", "tokei", "denwa", "hoshi", "hana", "terebi",
        "kuruma", "inu", "mushi", "tori"};
    String mandarinWords[] = {
        "fang wu", "shu", "ka fei", "chun", "fei ji", "mei guo",
        "ping guo", "zhong", "dian hua", "xing", "hua", "dian shi",
        "che", "gou", "chong", "niao"};
    String portugueseWords[] = {	
	"casa", "livro", "cafe", "lapios", "aviao", "Estados Unidos",
	"maca", "relogio", "telefone", "estrela", "flor", "televisao",
	"carro", "cachorro", "bicho", "passarinho"};
    String pigLatinWords[] = {
        "ouse-hay", "ook-bay", "ava-jay", "ips-lay", "airplane-gay", "United-gay Ates-Stay",
        "apple-gay", "ock-clay", "one-phay", "ar-stay", "ower-flay", "elevision-tay",
        "ar-cay", "og-day", "ug-bay", "ird-bay"};
    String spanishWords[] = {
        "casa", "libro", "cafe", "labios", "avion", "Estados Unidos",
        "manzana", "reloj", "telefono", "estrella", "flor", "television",
        "coche", "perro", "insecto", "pajaro"};
    String swissGermanWords[] = {
        "Huus", "Buech", "Cafi", "Lippe", "Flugzueg", "Vereinigti Staate",
        "Oepfel", "Uhr", "Telephon", "Stern", "Blueme", "Fernsehapparat",
        "Auto", "Hund", "Chaefer", "Vogel"};
    String esperantoWords[] = {
        "domo", "libro", "kafo", "lipoj", "aviadilo", "Usono",
        "pomo", "horlogxo", "telefono", "stelo", "floro", "televidilo",
        "auxto", "hundo", "insekto", "birdo"};
    String czechWords[] = {
        "dum", "kniha", "kava", "ret", "letadlo", "Spojene Staty",
        "jablko", "hodina", "telefon", "hvezda", "kvetina", "televizor",
        "auto", "pes", "hmyz", "ptak"};


    /* List of languages. */
    String languages[] = {
        "English",
        "Cantonese",
        "Czech",
        "Dutch",
        "Esperanto",
        "French",
        "Geekspeak",
	"German",
	"Hebrew",
	"Hindi",
        "Italian",
        "Japanese",
        "Mandarin",
        "PigLatin",
        "Portuguese",
        "Spanish",
        "SwissGerman"};

    String dictionary[][] = {
        englishWords,
        cantoneseWords,
        czechWords,
        dutchWords,
        esperantoWords,
        frenchWords,
        geekspeakWords,
	germanWords,
	hebrewWords,
	hindiWords,
        italianWords,
        japaneseWords,
        mandarinWords,
        pigLatinWords,
	portugueseWords,
        spanishWords,
        swissGermanWords};
}
