import java.lang.*;
import java.io.*;



 /*
   fractmorse.java

   This class provides methods that enables
   main.java to encrypt and decrypt text using 
   fractionated morse. 
 */



public class fractmorse
{
 private String plaintext;
 private String ciphertext;
 private String morsecode;
 private String keyword;
 private String [] fractionatedmorsecode;
 private String [] morseconversions;
 private String [] keywordconversions;
 private String [] statickeyconversions;

 /*
   fractmorse - constructor which initializes the plaintext
   string and keyword string.  Also initializes the conversion
   tables for converting from plaintext letters to morse code
   representations and from 3-symbol blocks to ciphertext letters.
   The morseconversions table and the statickeyconversions table
   are static.  But the keywordconversions table is permutation
   on the statickeyconversions table depending on the keyword.
 */

 public fractmorse(String plain, String cipher, String key)
  {
   keyword = new String(key);
   plaintext = new String(plain);
   ciphertext = new String(cipher);
   morseconversions = new String[] {"01","1000","1010","100","0","0010",
                                    "110","0000","00","0111","101","0100",
                                    "11","10","111","0110","1101","010",
                                    "000","1","001","0001","011","1001",
                                    "1011","1100"};
   statickeyconversions = new String[] {"000","001","002","010","011","012",
                                    "020","021","022","100","101","102",
                                    "110","111","112","120","121","122",
                                    "200","201","202","210","211","212",
                                    "220","221"};

   keywordconversions = new String[26];
   for(int i = 0; i < 26; i++)
     keywordconversions[i] = new String();
  }

 /*
   PrintMorseCodeConversions - prints the morse code table to the screen 
 */
 public void PrintMorseCodeConversions()
  {
   char c = 'a';
   for(int i = 0; i < 26; i++)
     {
      System.out.print(c + " = " + morseconversions[i] + " ");
      c++;
     }
  }

 /*
   PrintKeywordTableConversions - prints the keyword conversions table
                                  to the screen
 */

 public void PrintKeywordTableConversions()
  {
   char c = 'a';
   for(int i = 0; i < 26; i++)
     {
      System.out.print(c + " = " + keywordconversions[i] + " ");
      c++;
     }
  }
   
 /*
   PrintPlaintext - prints the plaintext string
 */

 public String getPlaintext()
  {
   return plaintext;
  }

 public String getCiphertext()
  {
   return ciphertext;
  }

 public void PrintPlaintext()
  {
   System.out.println(plaintext);
  }
 
 public void PrintCiphertext()
  {
   System.out.println(ciphertext);
  }

 /*
   PrintMorseCode - prints the morsecode string associated
                    with the plaintext string
 */

 public void PrintMorseCode()
  {
   for(int i = 0; i < morsecode.length(); i++)
    System.out.print(morsecode.charAt(i));
  }


 /*
   PrintFractionatedMorseCode - prints the fractionated morsecode string
                    associated with its morsecode string
 */

  public void PrintFractionatedMorseCode()
   {
    for(int i = 0; i < fractionatedmorsecode.length; i++)
     System.out.print(fractionatedmorsecode[i] + " ");
   }



 /*
   CharToInt - converts the lowercase character to its
               integer representation, a = 0,...,z = 25
 */

 public int CharToInt(char c)
  {
   switch(c)
    {
     case('a') : return 0; case('b') : return 1; case('c') : return 2;
     case('d') : return 3; case('e') : return 4; case('f') : return 5;
     case('g') : return 6; case('h') : return 7; case('i') : return 8;
     case('j') : return 9; case('k') : return 10; case('l') : return 11;
     case('m') : return 12; case('n') : return 13; case('o') : return 14;
     case('p') : return 15; case('q') : return 16; case('r') : return 17;
     case('s') : return 18; case('t') : return 19; case('u') : return 20;
     case('v') : return 21; case('w') : return 22; case('x') : return 23;
     case('y') : return 24; case('z') : return 25;
    }
   return -1;
  }
 /*
   IntToChar - converts the integer (0...25) to its
               corresponding character
 */

 public char IntToChar(int num)
  {
   switch(num)
    {
     case(0) : return 'a'; case(1) : return 'b'; case(2) : return 'c';
     case(3) : return 'd'; case(4) : return 'e'; case(5) : return 'f';
     case(6) : return 'g'; case(7) : return 'h'; case(8) : return 'i';
     case(9) : return 'j'; case(10) : return 'k'; case(11) : return 'l';
     case(12) : return 'm'; case(13) : return 'n'; case(14) : return 'o';
     case(15) : return 'p'; case(16) : return 'q'; case(17) : return 'r';
     case(18) : return 's'; case(19) : return 't'; case(20) : return 'u';
     case(21) : return 'v'; case(22) : return 'w'; case(23) : return 'x';
     case(24) : return 'y'; case(25) : return 'z';
    }
   return '!';     // error
  }

 /*
   HasChar - returns true if the string str contains the char c
 */

 public boolean HasChar(String str, char c)
  {
   for(int i = 0; i < str.length(); i++)
    if(str.charAt(i) == c) return true;
   return false;
  }


 //-----------------------------------------------------------------
 // the following methods carry out encryption
 //-----------------------------------------------------------------


 /*
  PlaintextMorse - converts the plaintext string into its morse code
  representation and stores it in the string morsecode
 */

 public void PlaintextToMorse()
  {
   StringBuffer temp = new StringBuffer();
   for(int i = 0; i < plaintext.length(); i++)
    {
     if(Character.isLetter(plaintext.charAt(i)))
      temp = temp.append(morseconversions[CharToInt(plaintext.charAt(i))]+"2");
     else
      temp = temp.append("2");
    }
   morsecode = new String(temp);
  }

 /*
  MorseCodeToFractionatedMorse - converts the plaintext's morsecode
  to a fractionated morse form by partitioning the morsecode into
  an array of 3-symbol StringBuffers
 */

 public void MorseCodeToFractionatedMorse()
  {
   // numfractmorsechars - # of 3-symbol segments in morsecode
   int offset = 0, numfractmorsechars = (morsecode.length() / 3) + 1;

   // leftoverchars - # of char's at the end which can't complete a segment
   //                 on their own          
   int leftoverchars = morsecode.length() % numfractmorsechars;
   int end = morsecode.length();
   char [] morsechararray = morsecode.toCharArray();

   StringBuffer [] temp = new StringBuffer[numfractmorsechars];
   for(int i = 0; i < numfractmorsechars; i++)
    temp[i] = new StringBuffer();

   fractionatedmorsecode = new String[numfractmorsechars];

   for(int i = 0; i < numfractmorsechars; i++)
    {
     if(i < (numfractmorsechars - 1) )   
      {
       temp[i] = temp[i].append(morsechararray,offset,3);
       offset += 3;
      }
     else    // handling the last segment
      {
       if(leftoverchars == 0)
        temp[i] = new StringBuffer(morsecode.charAt(end-3) +
                      morsecode.charAt(end-2) + morsecode.charAt(end-1) );
       else if (leftoverchars == 1)
        temp[i] = new StringBuffer(morsecode.charAt(end-2) +
                      morsecode.charAt(end-1) + "2");
       else 
        temp[i] = new StringBuffer(morsecode.charAt(end-1) + "2" + "2");
      }

     fractionatedmorsecode[i] = new String(temp[i]);
    } // end for

  } // end method MorseCodeToFractionatedMorse


 /*
   AssignKeyWordTableConversions - assigns the first n 3-symbol
     segments in the keyword conversion table to the n keyword letters.
     the remaining 26 - n segments are assigned to the remainder
     of the alphabet not included in the keyword.
 */

  public void AssignKeywordTableConversions()
   {
    char c = 'a';

    for(int i = 0; i < keyword.length(); i++)
     keywordconversions[CharToInt(keyword.charAt(i))] =
        new String(statickeyconversions[i]);

    for(int i = keyword.length(); i < 26; i++)
     {
      while(HasChar(keyword,c)) c++;
      keywordconversions[CharToInt(c)] = new String(statickeyconversions[i]);
      c++;
     }
  }

 /*
   FractionatedMorseToCiphertext - substitutes a character
      derived from the 3-symbol segment keyword conversions table,
      keywordconversions, and the result is the ciphertext.
 */

 public void FractionatedMorseToCiphertext()
  {
   StringBuffer temp = new StringBuffer();

   for(int i = 0; i < fractionatedmorsecode.length; i++)
    for(int j = 0; j < keywordconversions.length; j++)
      {
       if(fractionatedmorsecode[i].equals(keywordconversions[j]))
        temp = temp.append(IntToChar(j));
      }
   ciphertext = new String(temp);
  }



 //-----------------------------------------------------------------
 // the following methods carry out decryption
 //-----------------------------------------------------------------

 /*
   CiphertextToFractionatedMorse - converts the ciphertext back
        to its fractionated morse representation
 */

 public void CiphertextToFractionatedMorse()
  {
   StringBuffer [] temp = new StringBuffer[ciphertext.length()];
   fractionatedmorsecode = new String[ciphertext.length()];
   for(int i = 0; i < ciphertext.length(); i++)
     temp[i] = new StringBuffer();
     
   for(int i = 0; i < ciphertext.length(); i++)
    {
     temp[i] =
        temp[i].append(keywordconversions[CharToInt(ciphertext.charAt(i))]);
     fractionatedmorsecode[i] = new String(temp[i]);
    }
  }  

 /*
   FractionatedMorseToMorseCode - converts fractionated morse
    representation back to morse code by concatenating all the
    3-segment chunks into one big string.
  */

 public void FractionatedMorseToMorseCode()
  {
   StringBuffer temp = new StringBuffer();

   for(int i = 0; i < fractionatedmorsecode.length; i++)
    temp = temp.append(fractionatedmorsecode[i]);
   morsecode = new String(temp);
  }

 /*
   MorseCodeChar - takes a StringBuffer of a morse code
      representation and returns the corresponding letter that matches
      the representation
 */

 public char MorseCodeChar(StringBuffer morse)
  {
   String str = new String(morse);
   if(str.equals(" ")) return ' ';
   else
    {
     for(int i = 0; i < 26; i++)
      if(str.equals(morseconversions[i])) return IntToChar(i);
    }
   return '!';    // error
  }


 /*
   MorseCodeToPlaintext - converts morsecode back to plaintext.
    2's serve to separate letters so each morsecode sequence can
    be parsed and converted back to a letter.
  */

  public void MorseCodeToPlaintext()
   {
    StringBuffer [] temp = new StringBuffer[ciphertext.length()];
    for(int i = 0; i < ciphertext.length(); i++)
      temp[i] = new StringBuffer();
    StringBuffer plain = new StringBuffer();
    int j = 0;

    for(int i = 0; i < ciphertext.length() - 1; i++)
     {
     
        if(morsecode.charAt(j) == '2')
         {  
          temp[i] = temp[i].append(' ');
          if(j < morsecode.length() - 1) j++;
          plain = plain.append(" ");
         }
        else
         {
          while(morsecode.charAt(j) == '1' || morsecode.charAt(j) == '0')
           {
            temp[i] = temp[i].append(morsecode.charAt(j));
            if(j >= morsecode.length() - 1) break;
            else j++;
           }
          if(j < morsecode.length() - 1) j++;
          if(j < morsecode.length() )
             plain = plain.append(MorseCodeChar(temp[i]));
         }
      plaintext = new String(plain);
    }
 } // end method MorseCodeToPlaintext 


 public static void main(String [] argv)
  {
   
  }

}  // end class fractmorse

