package org.unicode.cldr.util;

import java.util.Locale;

import com.ibm.icu.text.DecimalFormat;
import com.ibm.icu.text.DecimalFormatSymbols;
import com.ibm.icu.text.NumberFormat;
import com.ibm.icu.util.ULocale;

public class XChoiceFormat {
  /**
   * Enumeration representing different types of plurals used in various
   * languages. More may be added over time. The names are chosen to be short.
   */
  public enum Condition {
    /** nullar: used for zero items */
    N0,
    /** singular: used for 1 item */
    N1,
    /** dual: used for 2 items */
    N2,
    /** trial: used for 2 items */
    N3,
    /** paucal: used for 2-4 items */
    N234,
    /** slavic singular: used for 1, 21, 31, ... items */
    S1,
    /** slavic dual: used for 2, 22, 32, ... items */
    S2,
    /** slavic paucal: used for 2, 3, 4, 22, 23, ... items */
    S234,
    /** other: used for anything not mentioned above, such as plurals */
    N;

    public boolean matches(long num) {
      switch (this) {
        case N0:
          return num == 0;
        case N1:
          return num == 1;
        case N2:
          return num == 2;
        case N3:
          return num == 3;
        case N234:
          return num >= 3 && num <= 4;
        case S1:
        case S2:
        case S234:
          long digit = num % 10;
          if (((num / 10) % 10) == 1) {
            return false;
          }
          switch (this) {
            case S1: {
              return digit == 1;
            }
            case S2: {
              return digit == 2;
            }
            default: {
              return digit >= 2 && digit <= 4;
            }
          }
      }
      return true; // CN
    }
  };

  private Condition[] conditions;

  private Object[] results;

  ULocale locale = ULocale.getDefault();

  /**
   * "There {countOfBooks,C0:are no files|C1:is one file|Cn:are #0 files} in
   * {directory}."
   * 
   * @param pattern
   */
  public void applyPattern(String pattern) {
    String[] items = pattern.split("[|]");
    conditions = new Condition[items.length];
    results = new Object[items.length];
    // conditions = new int
    int i = 0;
    for (String item : items) {
      int semiPosition = item.indexOf(':');
      if (semiPosition < 0) {
        throw new IllegalArgumentException("syntax error: no semicolon");
      }
      String tag = item.substring(0, semiPosition);
      conditions[i] = Condition.valueOf(tag.toUpperCase(Locale.ENGLISH));
      String numberPattern = item.substring(semiPosition + 1);
      results[i] = numberPattern.contains("#") 
      ? new DecimalFormat(numberPattern, new DecimalFormatSymbols(locale)) 
          : numberPattern;
      ++i;
    }
    if (conditions[i - 1] != Condition.N) {
      throw new IllegalArgumentException("final condition must be CN");
    }
  }

  public String format(long num) {
    for (int i = 0; i < conditions.length; ++i) {
      if (conditions[i].matches(num)) {
        if (results[i] instanceof String) {
          return (String) results[i];
        } else {
          return ((NumberFormat) results[i]).format(num);
        }
      }
    }
    throw new IllegalArgumentException("final condition must be CN");
  }

  public static void main(String[] args) {
    Object[] tests = { 
        new ULocale("en"), "There {N0:are no files|N1:is 1 file|N:are #,### files} in the directory.", 
        new ULocale("fr"), "There {N0:is aucun file|N1:is 1 file|N:are #,### files} in the directory.", 
        new ULocale("sl"), "There {N0:are no mest|S1:is #,### mesto|S2:are #,### mesti|S234:are #,### mesta|N:are #,### mest} in the directory.",
        new ULocale("ru"), "There {N0:are no zavody|S1:is #,### zavod|S234:are #,### zavoda|N:are #,### zavodov} in the directory.", };

    int[] testNumbers = { 0, 1, 2, 3, 5, 10, 11, 12, 15, 20, 21, 22, 25, 123450, 123451, 123452, 123456};
    XChoiceFormat foo = new XChoiceFormat();
    for (Object test : tests) {
      if (test instanceof ULocale) {
        ULocale locale = (ULocale) test;
        System.out.println("Language:\t" + locale.getDisplayName());
        foo.setLocale(locale);
        continue;
      }
      System.out.println("Test pattern:\t\"" + test + "\"");
      String[] parts = ((String)test).split("[{}]");
      foo.applyPattern(parts[1]);
      for (int i : testNumbers) {
        System.out.println("\tSample:\t" + parts[0] + foo.format(i) + parts[2]);
      }
    }
  }

  /*
   * zeroHours oneHour twoHours threeToFourItems oneItemRussian
   * twoToFourItemsRussian otherHours
   * 
   * Do the same for zeroMinutes, zeroSeconds, etc.
   * 
   * For a given locale, for a given field type (hour, minute, second), the code
   * will fetch each of the corresponding strings, and do the following
   * pseudocode, given a number x. // special checks for certain slavic
   * languages int digit = x % 10; int tens = (x / 10) % 10; if (oneItemRussian !=
   * null && tens != 1 && digit == 1) return oneItemRussian; if
   * (twoToFourItemsRussian != null && tens != 1 && 2 <= digit && digit <= 4)
   * return twoToFourItemsRussian; // above might need fixing, this is off the
   * top of my head // more regular cases if (zeroItems != null && x == 0)
   * return zeroItems; if (oneItem != null && x == 1) return oneItem; if
   * (twoItems != null && x == 2) return twoItems; if (threeToFourItems != null &&
   * x == 2) return threeToFourItems; return otherItems; // must always be
   * populated // above can be optimized, again off the top of my head
   * 
   */

  private void setLocale(ULocale locale2) {
    locale = locale2;
  }
}