/* -*- c++ -*-
 * %W% %E%
 *
 * Applet: Cryptogram (Java 1.2) 
 * Author: Ralph Morelli, Trinity College (ralph.morelli@mail.trincoll.edu)
 * Description:  Displays a weekly cryptogram and provides a simple GUI
 *  for solving it.
 * 
 * Copyright (c) 1996 Ralph Morelli. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL purposes and
 * without fee is hereby granted. 
 */

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.lang.InterruptedException;
import java.net.*;
import java.io.*;
import java.util.*; // StringTokenizer, Calendar classes

public class CryptoGramApplet extends Applet implements MouseListener, 
                                                        KeyListener {
                                           // REFERENCE CONSTANTS FOR PAINTING
    final static int kTopCrypto = 45, kLeftCrypto = 20;
    final static int kBlockWidth = 30, kBlockHeight = 30, kBlocksWide = 13, kBlocksHigh = 2;
    final static int kTopKey = kTopCrypto + 10 + kBlockHeight*kBlocksHigh+8;
    final static int kLeftKey 	= 20;
    final static int kLeftPlain = 20;
    final static int kTopPlain 	= kTopKey + 10 + kBlockHeight*kBlocksHigh+8;
    
    final static int kAppWidth 	= 40 + (kBlockWidth * kBlocksWide); // 430
    final static int kAppHeight = 3 * (kBlockHeight * kBlocksHigh + 30); // 270
 
    final static int kButtonWidth = 100, kButtonHeight = 25, kPadding 	= 20;
    
    final static Font bigFont = new Font("TimesRoman",0,18); 
    final static Font regularFont = new Font("TimesRoman", 0, 12);
    final static int kRegFontSize = 12, kLineLength = 56, kMaxLines = 4, kMaxKeys = 26, kMaxChars  = 2048;

    final static String cgrams = "cgrams/"; // where cryptograms are stored

    final static Color appletbGround = new Color(100,30,255); // dark blue
    final static Color textbGround = Color.yellow;
    final static Color titleColor = Color.yellow;
    final static Color textColor = Color.blue;
    final static Color buttonbGround = Color.yellow;
    final static Color buttonFocusColor = Color.white;
    final static Color labelColor = Color.red;

    private CryptoGram cryptoGram;
    private Point mouse;
    private String hintPrompt = "For a hint, type <SHIFT> and the letter you want to know.";
    private String hintString = "";
    private String weekString;

    public String getAppletInfo() {
	    return "Cryptogram of the Week by Ralph Morelli";
    }

    /*** Parameter Info  */
    public String[][] getParameterInfo() {
    	String[][] info = {{"cryptosource","url","text file containing the cryptogram"},};
	return info;
    }

    /**
     * init() initializes the applet. Most of the applet is painted.
     */
    public void init() {	
        Calendar calendar = Calendar.getInstance();       // Find what week it is
        weekString = "week" + calendar.get(Calendar.WEEK_OF_YEAR);

        cryptoGram = new CryptoGram(new String(downloadCryptogram()));
        setLayout(new FlowLayout (FlowLayout.LEFT));
        addMouseListener(this);                           // Set up listeners
        addKeyListener(this);
    } // init()
    
    /**
     * downloadCryptogram() gets the cryptogram from the server by
     *  using a URL to create an input stream to the applet's home base.
     * @return a String giving the contents of cryptogram file
     */
    private String downloadCryptogram() {
        String cryptogram = null;
                                                // Build URL for cryptogram
        String urlString = getDocumentBase() + "";
        urlString = urlString.substring(0,urlString.lastIndexOf('/'));
        urlString = urlString  + "/" + cgrams + weekString + ".txt"; 

        try {			               // Set up the URL
            URL cryptoSource = new URL (urlString); 
            InputStream is = cryptoSource.openStream();	// Get URL's input stream
            byte cryptoBytes[] = new byte[kMaxChars];
            int count = is.read(cryptoBytes);
            is.close();
            cryptogram = new String(cryptoBytes);
        } catch (IOException e) {
             e.printStackTrace();
	}
        return cryptogram;
    }
        
    /**
     * paintString() paints the cryptogram or the plaingtext string in a rectangular region
     *  on the applet.
     * @param cString the cryptogram
     * @param left, top -- reference points for the painting
     * @param linelen -- the length of each line
     */
    public void paintString(Graphics g, String cString,int left,int top,int linelen) {    
        String tmpString = "";
        g.setFont(regularFont);
        g.setColor(textbGround);			// Draw rectangular area
        g.draw3DRect(left-4,top-4,kBlockWidth*kBlocksWide+8,kBlockHeight*kBlocksHigh+8,true);
        g.setColor(textbGround);		        // Fill rectangular area
        g.fillRect(left-4+1,top-4+1,kBlockWidth*kBlocksWide+8-1,kBlockHeight*kBlocksHigh+8-1);      
        g.setColor(textColor);
                
        StringTokenizer st = new StringTokenizer(cString, "\n\r\f"); // Linefeed tokens
        StringBuffer buf = new StringBuffer();
        int k = 1;                          
        while (st.hasMoreTokens()) {
            g.drawString(st.nextToken(), left + 2, top + (kRegFontSize +1) * k++);
        }        
    } // paintString()
    
    /**
     * paint() draws the applet's GUI
     */
    public void paint(Graphics g) {
        g.draw3DRect(0,0,kAppWidth,kAppHeight,true);    // Paint background
        g.setColor(appletbGround);						// Fill outer rectangle
        g.fillRect(0+1,0+1,kAppWidth-1,kAppHeight-1);
       
        g.setColor(titleColor);							 //  Title
        g.setFont(bigFont);
        g.drawString("Cryptogram of the Week (" + weekString + ")", 110 ,20 );				
        g.setFont(regularFont);
        g.drawString(hintPrompt, 95, 35);
        g.drawString(hintString, 10, 15);
      
        String cryptoString = cryptoGram.getCipherText();											
        paintString(g, cryptoString, kLeftCrypto,kTopCrypto, kLineLength); // Cryptogram 

        g.setColor(textbGround);							// Keyboard
        g.draw3DRect(kLeftKey-4,kTopKey-4,kBlockWidth*kBlocksWide+8,kBlockHeight*kBlocksHigh+8,true);
        g.setColor(appletbGround);			
        g.fillRect(kLeftKey-4+1,kTopKey-4+1,kBlockWidth*kBlocksWide+8-1,kBlockHeight*kBlocksHigh+8-1);
    
        for (int j = 0 ; j < kBlocksHigh ; j++) {		// draw 26 letter keys
		    for (int i = 0 ; i < kBlocksWide ; i++) {
	  		    paintCell(g,i,j);
		    } // for i
        } // for j   	
        String plainText = cryptoGram.getPlainText();											
        paintString(g, plainText, kLeftPlain, kTopPlain, kLineLength); // Plaintext message
    } // paint

    /**
     * paintCell() paints an individual cell on the painted keyboard.
     * @param x, y are the horizontal and vertical coordinates of the cell
     *   in a 2 x 13 grid.
     */
    private void paintCell(Graphics g, int x, int y) {  // x is col, y is row 
        g.setFont(regularFont);
        int tempLeft = kLeftKey + (x * kBlockWidth);	// Compute coordinates
        int tempTop = kTopKey + (y * kBlockHeight);
      
        if (cryptoGram.inActiveBlock(x,y))		// Set Cell's color
		    g.setColor(buttonFocusColor);
        else
		    g.setColor(buttonbGround);
			
        g.fillRect(tempLeft+1, tempTop+1, kBlockWidth-1, kBlockHeight-1);	// Paint cell
        g.setColor(appletbGround);		
        g.draw3DRect(tempLeft,tempTop,kBlockWidth, kBlockHeight,true);
      
        Character chobj = new Character(getCellLabel(x,y));			// Draw cell's label
        g.setColor(labelColor);
        g.drawString(chobj.toString(), tempLeft + 2, tempTop + 11);	
      
        g.setColor(textColor);						       // Draw cell's letter
        chobj = new Character(cryptoGram.getCellContents(x,y));
        g.drawString(chobj.toString(), tempLeft + 12, tempTop + 20);				
    } // paintCell()

   /** getCellLabel() computes the cell's label based on its position in
    *  the 2 x 13 grid of cells.
    * @param x and y are the horizontal and vertical coordinates of the cell  
    */
    private char getCellLabel(int x, int y) {
        int m = x + 13 * y;				// Use x, y to compute the cell number
        return((char) ((int)'A' + m) );	// and return the corresponding letter	
    } // getCellLabel()

    public void mousePressed (MouseEvent e) { }    // Unimplemented method of MouseListener Interface
    public void mouseReleased (MouseEvent e) { }
    public void mouseEntered (MouseEvent e) { }
    public void mouseExited (MouseEvent e) { }
 
   /**
    * mouseCicked() is from MouseListener interface. It moves the
    *  focus to one of the keys on the painted keyboard.
    */
    public void mouseClicked (MouseEvent e) {
        System.out.println("Mouse clicked");
        Point mouse = e.getPoint();
        requestFocus();

        if (mouse.x < kLeftKey)			// if the click was not in the key area		
		    return;
        if (mouse.y < kTopKey)
		    return;
      
        int j = mouse.y - kTopKey;		// get row and column indexes of the block
        j /= kBlockHeight;
        int i = mouse.x - kLeftKey;
        i /= kBlockWidth;
      
        if (i >= 0 && i < kBlocksWide && j >= 0 && j < kBlocksHigh) {
		    if (!cryptoGram.inActiveBlock(i,j)) { 
	  		    int old_i = cryptoGram.gCurX, old_j = cryptoGram.gCurY;
	  		    cryptoGram.setActiveBlock(i, j);
	  		    paintCell(getGraphics(), i,j);
	  		    paintCell(getGraphics(), old_i,old_j);
		    } 
		    return;
        }
    } // mouseClicked()
    
   /**
    * keyPressed() is from the KeyListener Interface and is used
    *  to handle control keys such as arrows and shift.
    */
    public void keyPressed(KeyEvent e) {
        if (e.isShiftDown())
            displayHint(e.getKeyChar());   // Shift means hint
        else
            moveFocus(e);     // Otherwise arrow keys
    }
    public void keyReleased(KeyEvent e) { }

   /**
    * keyTypted() is from the KeyListener Interface and is used
    *  to handle an alphabetic key, whose value is painted on 
    *  the current key cell in the painted keyboard.
    */
    public void keyTyped(KeyEvent e) {
        char key = e.getKeyChar();
        if (e.isShiftDown() && key != CryptoGram.NULL_CHAR) 
            displayHint(key);
        else 
            displayLetter(key);
    } // keyDown

   /** displayHint() sets the hintString to the secret value of
    *   of its char parameter. The hintString is painted in the applet's
    *   upper left corner.
    * @param -- a char giving a letter between 'A' and 'Z'
    */
    public void displayHint(char key) {
       Graphics g = getGraphics();
       g.setColor(appletbGround);
       g.drawString(hintString, 10, 15);
       hintString = new String("hint " + key +  " = " + cryptoGram.getHint(key));
       g.setColor(titleColor);
       g.drawString(hintString, 10, 15);
    }

   /** displayLetter() displays its parameter as the label of the current
    *   key on the applet's painted keyboard.
    * @param -- a char giving a letter between 'A' and 'Z' or 'a' and 'z'
    */
    private void displayLetter(char key) {
        if (key >= 'A' && key <= 'Z')
            key = (char) (key + 32);				// convert to lower case
        if (cryptoGram.keyIsValid(key)) {
//            System.out.println("valid key = " + key);
            cryptoGram.setKeyChar(key);	
            paintCell(getGraphics(), cryptoGram.gCurX, cryptoGram.gCurY);
            paintString(getGraphics(), cryptoGram.getPlainText(), kLeftPlain, kTopPlain, kLineLength);
            return;
         } //if valid
    } // displayLetter()
    
    /**
     * moveFocus() moves the keyboard focus up, down, left or right based on which
     *   arrow key was pressed. The event's keyCode is used to determine which arrow
     *   key was pressed.
     * @param the KeyEvent from which the keyCode is extracted
     */
    private void moveFocus (KeyEvent e) {	// x is column, y is row
       int keyCode = e.getKeyCode();
       int oldx = cryptoGram.gCurX, oldy = cryptoGram.gCurY;
       int x = oldx, y = oldy;
       int m = x + 13 * y;						// cells are numbered 0..25
    // 	System.out.println("col " + x + " row " + y + " cell " + m );
       switch (keyCode) {
           case e.VK_UP: {
               if (m >= 13 && m <= 25) { y--; } break;
           }
           case e.VK_DOWN: {
               if (m >= 0 && m <= 12) { y++; } break;
           }
           case e.VK_LEFT: {
               if ( (m > 0 && m <= 12) || (m > 13 && m <= 25) ) { x--; }
               if (m == 0) { x = 12; y = 1;}
               if (m == 13) { x = 12; y = 0; }
               break;
           }
           case e.VK_RIGHT: {
               if ( (m >= 0 && m < 12) || (m >= 13 && m < 25) ) { x++; }
               if (m == 12) { x = 0; y = 1; }  
               if (m == 25) { x = 0; y = 0; }
               break;
           }
        } // switch
        cryptoGram.setActiveBlock(x,y);
        paintCell(getGraphics(), x,y);
        paintCell(getGraphics(), oldx,oldy);
   } // moveFocus()
 } // CryptoGramApplet 

