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

public class WordMatch extends Applet 
        implements ActionListener, ItemListener {

    // Number of displayed cards and words.
    int numCards = 4;

    // Currently selected language.
    int curLanguage;

    // card[i] refers to the i'th word.
    int cards[];

    // words[i] refers to the i'th card.
    int words[];

    // Currently selected card.
    int curCard;

    // The components.
    Box soundBox;
    List languageList = new List();
    Label statusBar = new Label("Ready");
    Button scoreButton;
    Box[] cardBoxes;
    Box[] wordBoxes;

    // If true, the user has successfully matched all the words.
    boolean done;
    
    // links[i] == j -> cards[i] is linked to words[j].
    int links[];

    // Images of pictures.  There is one image for each different word.
    Image images[];

    // The dimensions of the images.  They must all be the same size.
    int imageW, imageH;

    int numWords;
    String[] languages;
    String[][] dictionary;

    // Width of language list.
    int languageListW = 120;

    // Used to layout the components.
    int gap = 10;

    /**
     * Initialize the applet. Resize and load images.
     */
    public void init() {
        imageW = Integer.parseInt(getParameter("image-w"));
        imageH = Integer.parseInt(getParameter("image-h"));

        // Count number of languages
        for (int i=1; ; i++) {
            if (getParameter("language"+i) == null) {
                languages = new String[i-1];
                dictionary = new String[i-1][];
                break;
            }
        }

        // Count total number of words, based on the first language.
        for (int i=0; i<languages.length; i++) {
	    StringTokenizer st = new StringTokenizer(
                getParameter("language"+(i+1)), "|");

            dictionary[i] = new String[st.countTokens()-1];
            languages[i] = st.nextToken();
            languageList.addItem(languages[i]);
            
	    for (int j=0; j<dictionary[i].length; j++) {
                dictionary[i][j] = st.nextToken();
	    }
        }
        numWords = dictionary[0].length;

        // Create data structures.
        cards = new int[numCards];
        cardBoxes = new Box[numCards];
        words = new int[numCards];
        wordBoxes = new Box[numCards];
        links = new int[numCards];
        images = new Image[numWords];
        curLanguage = 0;

        // Load the images.
        for (int i=0; i<numWords; i++) {
	    images[i] = getImage(getCodeBase(), "rsrc/"
		+ dictionary[0][i] + ".gif");
        }

        // Layout the components.  Use absoluting positioning.
        setLayout(null);

        // Setup the score button.
        scoreButton = new Button("Score");
        add(scoreButton);
        Dimension d = scoreButton.getPreferredSize();
        scoreButton.setBounds(0, getSize().height-d.height, 
            languageListW, d.height);
        scoreButton.addActionListener(this);

        // Setup the language list.
        add(languageList);
        languageList.setBounds(0, 0, 
            languageListW, getSize().height-d.height);
        languageList.addItemListener(this);
        languageList.select(curLanguage);

	// Setup the status bar.
        add(statusBar);
        statusBar.setBounds(languageListW+2*gap, getSize().height-d.height, 
            getSize().width-languageListW-2*gap, d.height);

        // Setup sound box.
        soundBox = new Box(getImage(getCodeBase(), "rsrc/sound.gif"));
        add(soundBox);
        soundBox.addItemListener(this);
        soundBox.setBounds(0, 0, 32, 32);

	Font f = new Font("SansSerif", Font.PLAIN, 16);
        for (int i=0; i<numCards; i++) {
            // Setup the picture cards.
            cardBoxes[i] = new Box(images[cards[i]]);
            add(cardBoxes[i]);
            cardBoxes[i].setBounds(languageListW+2*gap, 
                i*(imageH+gap), imageW, imageH);
            cardBoxes[i].addItemListener(this);
            cardBoxes[i].select(i == 0);

            // Setup up the word boxes.
            wordBoxes[i] = new Box(dictionary[curLanguage][words[i]], f);
            add(wordBoxes[i]);
            wordBoxes[i].setBounds(languageListW+4*gap+2*imageW, 
                i*(imageH+gap), getSize().width, imageH);
            wordBoxes[i].addItemListener(this);
        }

        moveSoundBox();
        newRound();
    }

    /**
     * Paint the screen.
     */
    public void paint(Graphics g) {
        int x, y;

        // Paint links.
        g.setColor(Color.black);
        for (int i=0; i<numCards; i++) {
            if (links[i] >= 0) {
                Point pt1 = cardBoxes[i].getLocation();
                Point pt2 = wordBoxes[links[i]].getLocation();

                pt1.translate(imageW+gap, imageH/2);
                pt2.translate(-gap, imageH/2);

                g.drawLine(pt1.x, pt1.y, pt2.x, pt2.y);
            }
	}
    }

    public void itemStateChanged(ItemEvent evt) {
        if (!done) {
            statusBar.setText(null);
        }
        if (evt.getSource() == soundBox) {
	    "rsrc/" + dictionary[0][cards[curCard]] 
		+ "." + languages[curLanguage] + ".au");
	    play(getCodeBase(), "rsrc/" + dictionary[0][cards[curCard]] 
		+ "." + languages[curLanguage] + ".au");
        }

        // Was the event fired from one of the picture cards?
        for (int i=0; i<cardBoxes.length; i++) {
            if (cardBoxes[i] == evt.getSource()) {
	        // In card box.
                cardBoxes[curCard].select(false);
                cardBoxes[i].select(true);
                curCard = i;

                moveSoundBox();
		repaint();
                break;
	    }
        } 

        // Was the event fired from one of the words?
        for (int i=0; i<wordBoxes.length; i++) {
            if (wordBoxes[i] == evt.getSource()) {
		// Break an old link if necessary.
		for (int j=0; j<numCards; j++) {
		    if (links[j] == i) {
			links[j] = -1;
		    }
		}
		links[curCard] = i;
		repaint();
            }
        } 

        // Was the event fired from the language list?
        if (evt.getSource() == languageList) {
            curLanguage = languageList.getSelectedIndex();
            for (int i=0; i<wordBoxes.length; i++) {
                wordBoxes[i].setText(dictionary[curLanguage][words[i]]);
            }
        }
    }

    void moveSoundBox() {
	// Move the sound box.
	Point pt = cardBoxes[curCard].getLocation();
	soundBox.setLocation(
	    pt.x+imageW-soundBox.getSize().width/2, 
	    pt.y+imageH-soundBox.getSize().height/2);
    }

    public void actionPerformed(ActionEvent evt) {
        if (evt.getSource() == scoreButton) {
            if (done) {
	        scoreButton.setLabel("Score");
                newRound();
                repaint();
            }
            int count = 0;
            for (int i=0; i<numCards; i++) {
                if (links[i] == -1) {
                    statusBar.setText("You have not yet matched all the words.");
                    return;
                }
                if (cards[i] == words[links[i]]) {
                    count++;
                }
            }
            if (count == numCards) {            
		statusBar.setText("Congratulations, they're all right!");
                done = true;
                scoreButton.setLabel("New Game");
            } else if (count == 1) {
		statusBar.setText("There is only 1 correct match.");
            } else if (count == 0) {
		statusBar.setText("There are no correct matches.");
            } else {
		statusBar.setText("There are " + count + " correct matches.");
            }
        }
    }

    /**
     * Starts a new round.
     */
    public void newRound() {
        boolean[] picked = new boolean[numWords];

        statusBar.setText(null);
        done = false;

        // Pick new cards.
        for (int i=0; i<numCards; i++) {
	    while (true) {
                int r = (int)Math.floor(Math.random() * numWords);

                if (!picked[r]) {
	            cards[i] = r;
	            words[i] = r;
	            links[i] = -1;
	            picked[r] = true;
                    break;
                }
            }
	}

        // Scramble the pictures
        for (int i=0; i<100; i++) {
	    int r = (int)Math.floor(Math.random() * numCards);
            int t = cards[r];

            cards[r] = cards[i%numCards];
            cards[i%numCards] = t;
        }


	// Initialize the new pictures and words.
	for (int i=0; i<numCards; i++) {
	    cardBoxes[i].setImage(images[cards[i]]);
	    wordBoxes[i].setText(dictionary[curLanguage][words[i]]);
	}
    }


    // Added by K.A. Smith 10/25/95 
    public String getAppletInfo() {
       return "Author: Patrick Chan\nVersion: 2.0, June 1 1997";
    }
}

class Box extends Canvas implements ItemSelectable {
    Font font;
    Image image;
    String string;
    boolean selected;

    Box(Image image) {
        this.image = image;
        addMouseListener(new MouseEventHandler());
    }

    Box(String string, Font f) {
        this.string = string;
        font = f;
        addMouseListener(new MouseEventHandler());
    }

    public void select(boolean s) {
        selected = s;
        repaint();
    }

    public void setText(String s) {
        string = s;
        repaint();
    }

    public void setImage(Image i) {
        image = i;
        repaint();
    }

    public Object[] getSelectedObjects() {
	Object[] result;
        if (selected) {
            result = new Object[1];
            result[0] = this;
        } else {
            result = new Object[0];
        }
        return result;
    }

    public void paint(Graphics g) {
        int w = getSize().width;
        int h = getSize().height;

        // If it's an image, paint it.
        if (image != null) {
            g.drawImage(image, 0, 0, this);

            if (selected) {
                g.setColor(Color.red);
                g.drawRect(0, 0, w-1, h-1);
            }
        }
        // If it's a string, paint it.
        if (string != null) {
            g.setFont(font);
            FontMetrics fontM = g.getFontMetrics();

	    g.setColor(Color.black);
	    g.drawString(string, 0, (h-fontM.getHeight())/2+fontM.getAscent());
        }
    }

    class MouseEventHandler extends MouseAdapter {
        public void mousePressed(MouseEvent evt) {
	    ItemEvent e = new ItemEvent(Box.this, 
	        ItemEvent.ITEM_STATE_CHANGED, string, ItemEvent.SELECTED);
    
	    if (ItemListener != null) {
	        ItemListener.itemStateChanged(e);
	    }
        }
    }

    ItemListener ItemListener;
    public synchronized void addItemListener(ItemListener l) {
	ItemListener = AWTEventMulticaster.add(ItemListener, l);
    }

    public synchronized void removeItemListener(ItemListener l) {
	ItemListener = AWTEventMulticaster.remove(ItemListener, l);
    }
}
