import java.math.BigInteger;

public class OperaAlphabet
{
  static String[] LATIN_26 = { 
      "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", 
      "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" 
  };
  static String[] LATIN_27 = { 
      " ", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", 
      "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" 
  };
  
  static String[][] alphabets = { LATIN_26, LATIN_27, reverse(LATIN_26) };
                                
  static String[] descriptions = { "Latin 26", "Latin 27", "Latin reversed" };
  
  private static char[] MAX_RADIX_DIGITS = { 
      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
      'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 
      'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' 
  };
  
  static String BR = "\n";                    // for <pre> webpage output
  
  private int alphabetsIndex;
  private String[] alphabet;
  
  // Constructors for String[] alphabet and its String name
  public OperaAlphabet(String[] strArr, String name)
  {
    alphabet = strArr;                        // temp initialization
    alphabetsIndex = addToAlphabets(name);    // index in alphabets
    alphabet = alphabets[alphabetsIndex];     // official initialization
  }
  
  public OperaAlphabet(String[] alphabet)
  {   this(alphabet, "Custom");   }           // "Custom alphabet"
  
  public OperaAlphabet(String alphabet, String name)
  {   this(toStringArray(alphabet), name);   }
  
  public OperaAlphabet(String alphabet)
  {   this(alphabet, "Custom");   }
  
  public OperaAlphabet(int index)    // first check with alphabetExists(index)
  {   this(alphabets[index], descriptions[index]);   }
  
  
  // The purpose of this class is to translate large numbers in an alphabet
  public String translate(BigInteger number, int maxLineLength)
  {
    int radix = alphabet.length;
    String bigStr = number.toString(radix);             // big number as a string
    int bigLen = bigStr.length(); 
    StringBuilder bigTrans = new StringBuilder(bigLen);    // to build the output
    
    boolean checkLineEnd = (maxLineLength > 0);
    
    for(int i = 0, lineLen = 0; i < bigLen; i++)
    {
      int digit = Character.digit(bigStr.charAt(i), radix);   // next digit
      String letter = alphabet[digit];                        // translation
      
      if(checkLineEnd)
      {
        if(lineLen+letter.length() > maxLineLength)
        {
          bigTrans.append(BR);     // insert BR at line end
          lineLen = 0;             // reset line chars counter
        }
        lineLen += letter.length();        // count next chars
      }
      bigTrans.append(letter);    // append next letters
    }
    return bigTrans.toString();
  }
  
  public String translate(BigInteger number)
  {   return translate(number, 0);   }

  
  // return e.g. Latin alphabet
  public String getDescription()
  {
    return descriptions[alphabetsIndex] + " alphabet";
  }
  
  // return e.g. " ABCDEFG'HIJ'KLMNOPQRSTUVWXYZ"
  // or if HTML: " ABCDEFG'HIJ'KL<br />MNOPQRSTUVWXYZ"
  public String toString(int maxWebLineLength)
  {
    String str = "\"";
    boolean HTML = (maxWebLineLength > 0);
    int lineLen = 1;                       // begun with "
    
    for(int i = 0; i < alphabet.length; i++)
    {
      String s = alphabet[i];

      if(s.length() > 1)           // if it is a word..
        s = "'" + s + "'";         // ..put it between apostrophes
      
      if(HTML) 
      {
        if(lineLen+s.length() > maxWebLineLength)
        {
          str += BR;          // first break
          lineLen = 0;
        }
        lineLen += s.length();
      }
      str += s;
    }
    str += "\"";
    
    String desc = getDescription() + ":";
    
    if(HTML && (str.length() + desc.length() > maxWebLineLength))
      return desc + BR + str;

    return desc + " " + str;
  }
  
  public String toString()
  {   return toString(0);   }
  
  // return e.g. 26
  public int length()
  {
    return alphabet.length;
  }
  
  // Checks if index can be an index of alphabets[]
  public static boolean alphabetExists(int index)
  {
    return (index >= 0 && index < alphabets.length);
  }

  
  // Add the new alphabet to the existing alphabets (if not there yet), return it's index
  protected int addToAlphabets(String name)
  {
    // Check if alphabet already exists in the class variable alphabets
    checkloop:
    for(int i = 0; i < alphabets.length; i++)
      if(alphabets[i].length == alphabet.length)     // same length so..
      {
        for(int j = 0; j < alphabets[i].length; j++)  // ..go through to..
          if(alphabets[i][j] != alphabet[j])          // ..match every letter
            continue checkloop;                       // no match! try next alphabet
        return i;                                     // total match! by alphabets[i]
      }
    
    // No match, then add copy alphabets and add new alphabet at end index
    int endIndex = alphabets.length;
    String[][] tempAlpha = new String[endIndex+1][];
    String[] tempDesc = new String[endIndex+1];
    
    for(int i = 0; i < endIndex; i++) {
      tempAlpha[i] = alphabets[i];
      tempDesc[i] = descriptions[i];
    }
    tempAlpha[endIndex] = alphabet;
    tempDesc[endIndex] = name;
    
    alphabets = tempAlpha;             // reassign arrays
    descriptions = tempDesc;
    
    return endIndex;
  }
  
  // Create alphabets String[] from a String.. 
  // ..with possible multi-letters given between apostrophes
  protected static String[] toStringArray(String str)
  {
    int len = str.length();
    String[] tempArr = new String[len];     // maximum is len chars

    for(int i = 0, j = 0; i < len; i++, j++)
    {
      String ch = str.substring(i, i+1);    // next char
      if(ch.equals("'"))                    // found an apostrophe
      {
        int nextApos = str.indexOf("'", i+2);
        if(nextApos == -1)      // no second apostrophe
          tempArr[j] = ch;      // just take the ' as a letter
        else {
          tempArr[j] = str.substring(i+1, nextApos);   // get the multi-letter
          i = nextApos;
        }
      }
      else                      // just the char         
        tempArr[j] = ch;
    }
    
    // Find first empty array entry 
    int i = 0;
    for( ; i < tempArr.length; i++)
      if(tempArr[i] == null)        // empty entry
        break;
    
    // Create full array
    String[] strArr = new String[i];
    for(i = 0; i < strArr.length; i++)
      strArr[i] = tempArr[i];
    // And return it
    return strArr;
  }
  
  // Return the reverse of any String array
  public static String[] reverse(String[] letters)
  {
    String[] reversed = new String[letters.length];
    for(int i = 0, j = letters.length-1; i < reversed.length; i++, j--)
      reversed[i] = letters[j];
    return reversed;
  }
}
