/* -*- Mode: c++ -*-
 * @(#) CryptoGram.java 1.0 6/10/97 Ralph Morelli
 *
 * Classes: CryptoGram  
 * Author: Ralph Morelli, Trinity College (ralph.morelli@trincoll.edu)
 * 
 * Copyright (c) 2000 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 provided that this copyright
 * notice appears in all copies. 
 */

/**
 * A class to support making and breaking of simple
 * substitution cryptograms. 
 * @version  1.0 6/10/97
 * @author Ralph Morelli
 */
 
public class CryptoGram {
    public static final  char NULL_CHAR = '*';
  
    private char theKey[] = new char[26];	      // key for 'a' to 'z'
  
    private String cipherText;		// remains unchanged 
    private String plainText = "DIRECTIONS: Type substitutions into the above keys.\n " +
                         "To undo a substitution, just type '*'.\n" +
                         "Use the mouse or arrow keys to navigate the keypad.\n" +
                          "For a hint, type the <SHIFT> key and the letter you want to know.";
    private String hintString;
    
    public int gCurX = 0;    // Current mouse locations in terms of block number
    public int gCurY = 0;

    public CryptoGram ( )  {	      // Default Constructor
        cipherText = new String("") ;			
        initKey();
    }

    /**
     * Construct the initial cryptogram from a String.
     * @param s the String
     */
     public CryptoGram (String s)  {	
         hintString = new String (s.substring(0,26).toLowerCase());
         cipherText = new String(s.substring(26, s.length()));
         initKey();
     }
		
    /**
     * initKey() initializes the key to the null character ('*')
     */
     private void initKey() {
        for (int k = 0; k < 26; k++)
           theKey[k] = NULL_CHAR;
     }
  
    /**
     * setKeyChar() sets the current key character to its parameter.
     *  This method is invoked whenever the user proposes a substitution.
     *  For example, substitite 'a' for 'f'. The updated substitution key
     *  is then used to retranslate the cryptogram.
     * @param key a char in the range 'a' to 'z'
     */
     public void setKeyChar(char key) {
         int m = gCurX + 13 * gCurY;	// Find the current key on the keypad
         theKey[m] = (char)key;	        // and store the lower case version of letter  
         plainText = translate();       // Then retranslate the cryptogram.
     }
		  
    /**
     * keyIsValid() maintains the consistency of the key. It prevents the
     *  same letter from being substituted more than once.
     * @param key an integer array storing the key
     * @return true if the key is consistent and false otherwise
     */
    public boolean keyIsValid (char key) {	// Works just for 'a' to 'z' 
        if (key >= 'a' && key <= 'z') {         // If key is a valid letter
            int k;
            for (k = 0; k < 26 && theKey[k] != key; k++) ; // and not a duplicate
                if (k < 26)
                    return(false);
                else
                    return(true);			
        } else if (key == '*')
            return true;
        else
            return false;
    } // keyIsValid()
  
    /**
     * Returns the mapping from a key on the keypad to a given character in theKey.
     * @param ch the character to be mapped
     * @return the character's mapping.
     */
     public char getCellContents(int x, int y) { // x is col y is the row
         int m = x + 13 * y;		// Use x, y to compute what cell this is
         return(theKey[m]);		// and return its key
     }	

    public boolean inActiveBlock(int x, int y) {
        return x == gCurX && y == gCurY;
    } // InActiveBlock

    public void setActiveBlock(int x, int y) {
        gCurX = x;		// reset current mouse locations
        gCurY = y;
    } //SetActiveBlock
   
    /**
     * Gets the current plaintext for this cryptogram.
     * The key is first applied to generate an up-to-date
     * plaintext.
     * @return  the updated plaintext.
     */
    public String getPlainText( ) {
        translate();
        return plainText;	
    }

    /**
     * Gets the current ciphertext for this cryptogram.
     * @return  the current cipherText.
     */
    public String getCipherText( ) {
        return cipherText;	
    }

    /**
     * translate() uses the key to generate the plaintext from the cryptogram
     */
    public String translate() {
        StringBuffer sb = new StringBuffer();
        for (int k = 0; k < cipherText.length(); k++) 
            sb.append( encode(cipherText.charAt(k)) );	
        return sb.toString();
    } // translate()
	
    /**
     * encode() substitutes the input char to another char using the key.
     * @param inChar the char to be encoded
     */
    char encode(char inChar){
        char outChar;
        if (inChar == '*') 
            return inChar;         // Asterisk is special case
        if (inChar >= 'A' && inChar <= 'Z')  {
            inChar = (char)((int)inChar + 32);		// convert to lowercase
            outChar = theKey[(int)inChar - (int)'a'];	// get its substitute
	  
            if (outChar == NULL_CHAR) // return null if there's no key value
                return outChar;
            else 
                return (char) ((int)'A' + (int)outChar - (int)'a');	// else return the substitute
         } else if (inChar >= 'a' && inChar <= 'z')
             return theKey[(int)inChar - (int)'a'];
        return inChar;	
    } // encode()
    
    /**
     * getHint() returns the requested character from the key
     * @param s -- the requested character
     * @return a char from the key
     */

    public char getHint(char ch) {
        if (ch >= 'A' && ch <= 'Z')	// convert to lower case if necessary
            ch = (char) ((int) ch + 32);					
        if (ch >= 'a' && ch <= 'z') {   // if it's a valid letter
            char hint = 'A';
            int index = 0;
            while (ch != hintString.charAt(index) && index <= 25) {
                index++;
            }
            return (char) ((int) hint + index);
        }
        return ('?');
   } // getHint()  
}  //  CryptoGram


