/*
 *
 * USAGE: AsciiHebrew -[ca|c2|hl|hv] <pvm line of text>
 *
 * <pvm line of text> is a Partially Vowel Marked ASCII Hebrew line of text
 * enclosed in quotation marks, and not containing the alert (bell) character
 * (ASCII code 7).
 * The program sends its output to the screen.
 *
 * OPTIONS:
 * -c[a|2] ... The output is in strictly consonantal free-form ASCII Hebrew.
 *  -ca ... Alephs are represented by apostrophes.
 *  -c2 ... Alephs are represented by the digit 2.
 * -h[l|v] ... The output is in standard Hebrew script.
 *  -hl ... The output is in logical order.
 *  -hv ... The output is in visual order.
 *
 */
public class AsciiHebrew
{
   //STATIC
   // PRIVATE
   //  TYPES
   private static enum Aleph {APOSTROPHE, TWO};
   private static enum Position {END, MIDDLE};
   private static enum State {C, END, ERROR, START, V, VV}
   //  CONSTANTS
   private static final String ERR_INPUT = "ERROR! Illegal input.";
   private static final String ERR_USAGE =
      "ERROR! Usage: java ASCIIHebrew -[ca|c2|hl|hv] -[f|k] <pvm word>";
   private static final char NULL_LETTER = ' ';
   private static final int STATUS_FAILURE = 1;
   private static final int STATUS_SUCCESS = 0;
   //  FUNCTIONS
   private static String diphthongToConsonantCluster
   (
      char v1, char v2, Position p
   )
   {
      StringBuffer rv = new StringBuffer();

      if ( v1==v2 )
      {
         rv.append('h');
      }
      else if ( p==Position.MIDDLE || v2!='a')
      {
         rv.append(middleVowelToConsonant(v2));
      }
      else
      {
         switch (v1)
         {
            case 'i':
               rv.append('j');
               break;
            case 'o': case 'u':
               rv.append('w');
               break;
         }
         rv.append('\'');
      }

      return rv.toString();
   }
   private static char endVowelToConsonant(char v)
   {
      char rv = NULL_LETTER;

      switch (v)
      {
         case 'a': case 'e':
            rv = 'h';
            break;
         case 'i':
            rv = 'j';
            break;
         case 'o': case 'u':
            rv = 'w';
            break;
      }

      return rv;
   }
   private static boolean isConsonant(char c)
   {
      boolean rv = false;
      switch (c)
      {
         case '\'': case '2': case 'b': case 'v': case 'g':
         case '4': case 'd': case '6': case 'h': case 'w':
         case 'z': case '7': case 'y': case 'j': case 'k':
         case 'x': case 'l': case 'm': case 'n': case 's':
         case '0': case 'p': case 'f': case '5': case 'q':
         case 'r': case '1': case 'c': case 't': case '8':
            rv = true;
            break;
      }
      return rv;
   }
   private static boolean isVowel(char c)
   {
      boolean rv = false;
      switch (c)
      {
         case 'a': case 'e': case 'i': case 'o': case 'u':
            rv = true;
            break;
      }
      return rv;
   }
   private static char middleVowelToConsonant(char v)
   {
      char rv = NULL_LETTER;

      switch (v)
      {
         case 'a':
            rv = '\'';
            break;
         case 'e': case 'i':
            rv = 'j';
            break;
         case 'o': case 'u':
            rv = 'w';
            break;
      }

      return rv;
   }
   private static char vowelToConsonant(char v, Position p)
   {
      char rv = NULL_LETTER;

      switch (p)
      {
         case MIDDLE:
            rv = middleVowelToConsonant(v);
            break;
         case END:
            rv = endVowelToConsonant(v);
            break;
      }

      return rv;
   }
   // PUBLIC
   //  MAIN
   public static void main(String[] args)
   {
      if
      (
         args==null || args.length!=2 ||
         !(
            args[0].equals("-ca") || args[0].equals("-c2") ||
            args[0].equals("-hl") || args[0].equals("-hv")
         )
      )
      {
         System.err.println(ERR_USAGE);
         System.exit(STATUS_FAILURE);
      }
      else
      {
         Aleph aleph = args[0].equals("-ca") ? Aleph.APOSTROPHE : Aleph.TWO;
         String sc = PVMToSC(args[1], aleph);
         if ( sc==null )
         {
            System.err.println(ERR_INPUT);
            System.exit(STATUS_FAILURE);
         }
         else
         {
            StringBuffer output = new StringBuffer();
            if ( args[0].charAt(1)=='c' )
            {
               output.append(sc);
            }
            else
            {
               output.append(consonantalASCIIHebrewToStandardHebrew(sc));
               if ( args[0].charAt(2)=='v' )
               {
                  output.reverse();
               }
            }
            System.out.println(output);
            System.exit(STATUS_SUCCESS);
         }
      }
   }
   //  FUNCTIONS
   public static String consonantalASCIIHebrewToStandardHebrew(String s)
   {
      StringBuffer rv = new StringBuffer(s.length());

      for (int i=0; i<s.length(); i++)
      {
         rv.append(consonantalASCIIHebrewToStandardHebrew(s.charAt(i)));
      }

      return rv.toString();
   }
   public static char consonantalASCIIHebrewToStandardHebrew(char c)
   {
      char rv = NULL_LETTER;

      switch (c)
      {
         case '\'': case '2':
            rv = 'א';
            break;
         case 'b': case 'B': case 'v': case 'V':
            rv = 'ב';
            break;
         case 'g': case 'G': case '4':
            rv = 'ג';
            break;
         case 'd': case 'D': case '6':
            rv = 'ד';
            break;
         case 'h': case 'H':
            rv = 'ה';
            break;
         case 'w': case 'W':
            rv = 'ו';
            break;
         case 'z': case 'Z':
            rv = 'ז';
            break;
         case '7':
            rv = 'ח';
            break;
         case 'y': case 'Y':
            rv = 'ט';
            break;
         case 'j': case 'J':
            rv = 'י';
            break;
         case 'k': case 'x':
            rv = 'כ';
            break;
         case 'K': case 'X':
            rv = 'ך';
            break;
         case 'l': case 'L':
            rv = 'ל';
            break;
         case 'm':
            rv = 'מ';
            break;
         case 'M':
            rv = 'ם';
            break;
         case 'n':
            rv = 'נ';
            break;
         case 'N':
            rv = 'ן';
            break;
         case 's': case 'S':
            rv = 'ס';
            break;
         case '0':
            rv = 'ע';
            break;
         case 'p': case 'f':
            rv = 'פ';
            break;
         case 'P':
            rv = 'פ';
            break;
         case 'F':
            rv = 'ף';
            break;
         case '5':
            rv = 'צ';
            break;
         case '\u0007':
            rv = 'ץ';
            break;
         case 'q': case 'Q':
            rv = 'ק';
            break;
         case 'r': case 'R':
            rv = 'ר';
            break;
         case '1': case 'c': case 'C':
            rv = 'ש';
            break;
         case 't': case 'T': case '8':
            rv = 'ת';
            break;
         default:
            rv = c;
            break;
      }

      return rv;
   }
   public static String PVMToSC(String s, Aleph a)
   {
      String rv = null;

      if ( s!=null)
      {
         s = s.toLowerCase();
         AsciiHebrew converter = new AsciiHebrew();
         for (char c : s.toCharArray())
         {
            converter.setInput(c);
            converter.process();
            if ( converter.isError() )
            {
               break;
            }
         }
         converter.setEndOfInput();
         converter.process();
         if ( !converter.isError() )
         {
            rv = converter.getOutput();
            switch (a)
            {
               case APOSTROPHE:
                  rv = rv.replaceAll("2", "\'");
                  break;
               case TWO:
                  rv = rv.replaceAll("\\\'", "2");
                  break;
            }
         }
      }

      return rv;
   }

   //INSTANCE
   // PRIVATE
   //  FIELDS
   private char _input;
   private boolean _isEndOfInput;
   private StringBuffer _output;
   private State _s;
   private char _tempConsonant;
   private char _tempVowel;
   private char _tempVowel2;
   //  CONSTRUCTORS
   private AsciiHebrew()
   {
      init();
   }
   //  METHODS
   //   ACCESSORS
   private String getOutput()
   {
      String rv = _output.toString();
      return rv;
   }
   //  PREDICATES
   private boolean isError()
   {
      boolean rv = _s==State.ERROR;
      return rv;
   }
   //  MUTATORS
   private void setEndOfInput()
   {
      _isEndOfInput = true;
   }
   private void setInput(char input)
   {
      _input = input;
   }
   //   GENERAL
   private void init()
   {
      _input = NULL_LETTER;
      _isEndOfInput = false;
      _output =
         _output==null ?
         new StringBuffer() : _output.delete(0, _output.length());
      _s = State.START;
      _tempConsonant = NULL_LETTER;
      _tempVowel = NULL_LETTER;
      _tempVowel2 = NULL_LETTER;
   }
   private void process()
   {
      switch (_s)
      {
         case C:
            processC();
            break;
         case END:
            processEND();
            break;
         case ERROR:
            processERROR();
            break;
         case START:
            processSTART();
            break;
         case V:
            processV();
            break;
         case VV:
            processVV();
            break;
      }
   }
   private void processC()
   {
      if ( _isEndOfInput )
      {
         _output.append
         (
            _tempConsonant=='5' ?
            '\u0007' : Character.toUpperCase(_tempConsonant)
         );
         _s = State.END;
      }
      else if ( isConsonant(_input) )
      {
         _output.append(_tempConsonant);
         _tempConsonant = _input;
         _s = State.C;
      }
      else if ( isVowel(_input) )
      {
         _output.append(_tempConsonant);
         _tempVowel = _input;
         _s = State.V;
         
      }
      else
      {
         _output.append
         (
            _tempConsonant=='5' ?
            '\u0007' : Character.toUpperCase(_tempConsonant)
         );
         _output.append(_input);
         _s = State.START;
      }
   }
   private void processEND()
   {
      return;
   }
   private void processERROR()
   {
      return;
   }
   private void processSTART()
   {
      if ( _isEndOfInput )
      {
         _s = State.END;
      }
      else if ( isVowel(_input) )
      {
         _s = State.ERROR;
      }
      else if ( isConsonant(_input) )
      {
         _tempConsonant = _input;
         _s = State.C;
      }
      else
      {
         _output.append(_input);
         _s = State.START;
      }
   }
   private void processV()
   {
      if ( _isEndOfInput )
      {
         _output.append(vowelToConsonant(_tempVowel, Position.END));
         _s = State.END;
      }
      else if ( isConsonant(_input) )
      {
         _output.append(vowelToConsonant(_tempVowel, Position.MIDDLE));
         _tempConsonant = _input;
         _s = State.C;
      }
      else if ( isVowel(_input) )
      {
         _tempVowel2 = _input;
         _s = State.VV;
      }
      else
      {
         _output.append(vowelToConsonant(_tempVowel, Position.END));
         _output.append(_input);
         _s = State.START;
      }
   }
   private void processVV()
   {
      if ( _isEndOfInput )
      {
         _output.append
         (
            diphthongToConsonantCluster(_tempVowel, _tempVowel2, Position.END)
         );
         _s = State.END;
      }
      else if ( isConsonant(_input) )
      {
         _output.append
         (
            diphthongToConsonantCluster
            (
               _tempVowel, _tempVowel2, Position.MIDDLE
            )
         );
         _tempConsonant = _input;
         _s = State.C;
      }
      else
      {
         _output.append
         (
            diphthongToConsonantCluster(_tempVowel, _tempVowel2, Position.END)
         );
         _output.append(_input);
         _s = State.START;
      }
   }
}

