package zemberek.morphology.analysis.tr;

/**
 * This class is used for finding the last word of a number.
 * such as 123 returns "üç"(3) whereas it returns "yüz"(100) for 12300
 * It uses a simple state machine that processes the input backwards.
 */
public class TurkishNumeralEndingMachine {

  State ROOT = new State(StateId.ROOT);
  private State[] states1 = {
      new State(StateId.SIFIR),
      new State(StateId.BIR),
      new State(StateId.IKI),
      new State(StateId.UC),
      new State(StateId.DORT),
      new State(StateId.BES),
      new State(StateId.ALTI),
      new State(StateId.YEDI),
      new State(StateId.SEKIZ),
      new State(StateId.DOKUZ)};

  private State[] states10 = {
      null,
      new State(StateId.ON),
      new State(StateId.YIRMI),
      new State(StateId.OTUZ),
      new State(StateId.KIRK),
      new State(StateId.ELLI),
      new State(StateId.ALTMIS),
      new State(StateId.YETMIS),
      new State(StateId.SEKSEN),
      new State(StateId.DOKSAN)};

  private State SIFIR = new State(StateId.SIFIR);

  private State YUZ = new State(StateId.YUZ);
  private State BIN_1 = new State(StateId.BIN);
  private State BIN_2 = new State(StateId.BIN);
  private State BIN_3 = new State(StateId.BIN);
  private State MILYON_1 = new State(StateId.MILYON);
  private State MILYON_2 = new State(StateId.MILYON);
  private State MILYON_3 = new State(StateId.MILYON);
  private State MILYAR_1 = new State(StateId.MILYAR);
  private State MILYAR_2 = new State(StateId.MILYAR);
  private State MILYAR_3 = new State(StateId.MILYAR);
  State[] zeroStates = {SIFIR, YUZ, BIN_1, BIN_2, BIN_3, MILYON_1, MILYON_2, MILYON_3, MILYAR_1,
      MILYAR_2, MILYAR_3};

  public TurkishNumeralEndingMachine() {
    build();
  }

  private void build() {
    SIFIR.zeroState = false;
    for (State largeState : zeroStates) {
      largeState.zeroState = true;
    }
    for (int i = 1; i < states1.length; i++) {
      State oneState = states1[i];
      ROOT.add(i, oneState);
    }
    for (int i = 1; i < states10.length; i++) {
      State tenState = states10[i];
      SIFIR.add(i, tenState);
    }
    ROOT.add(0, SIFIR);
    SIFIR.add(0, YUZ);
    YUZ.add(0, BIN_1);
    BIN_1.add(0, BIN_2);
    BIN_2.add(0, BIN_3);
    BIN_3.add(0, MILYON_1);
    MILYON_1.add(0, MILYON_2);
    MILYON_2.add(0, MILYON_3);
    MILYON_3.add(0, MILYAR_1);
    MILYAR_1.add(0, MILYAR_2);
    MILYAR_2.add(0, MILYAR_3);
  }

  /**
   * Finds the last Turkish number word of an alphanumeric String's pronunciation.
   * Examples:
   * 123 -> "üç"(3)
   * 12300 -> "yüz"(100)
   * a20 -> "yirmi"(20)
   * 00 -> "sıfır"(0)
   * abc -> ""
   * 1abc -> ""
   *
   * @param numStr input suppose to have digits in it. It may contain alphanumeric values.
   * @return last Turkish word pronunciation of the imput number.
   */
  public String find(String numStr) {
    State current = ROOT;
    for (int i = numStr.length() - 1; i >= 0; i--) {
      int k = numStr.charAt(i) - '0';
      if (k < 0 || k > 9) {
        if (current.zeroState) {
          return StateId.SIFIR.lemma;
        } else {
          break;
        }
      }
      if (k > 0 && current.zeroState) {
        if (current == SIFIR) {
          return current.transitions[k].id.lemma;
        }
        break;
      }
      current = current.transitions[k];
      if (current == null) {
        return StateId.ERROR.lemma;
      }
      if (!current.zeroState) { // we are done
        break;
      }
    }
    return current.id.lemma;
  }

  private enum StateId {
    ROOT(""), ERROR(""),
    SIFIR("sıfır"),
    BIR("bir"),
    IKI("iki"),
    UC("üç"),
    DORT("dört"),
    BES("beş"),
    ALTI("altı"),
    YEDI("yedi"),
    SEKIZ("sekiz"),
    DOKUZ("dokuz"),
    ON("on"),
    YIRMI("yirmi"),
    OTUZ("otuz"),
    KIRK("kırk"),
    ELLI("elli"),
    ALTMIS("altmış"),
    YETMIS("yetmiş"),
    SEKSEN("seksen"),
    DOKSAN("doksan"),
    YUZ("yüz"),
    BIN("bin"),
    MILYON("milyon"),
    MILYAR("milyar");

    String lemma;

    StateId(String lemma) {
      this.lemma = lemma;
    }
  }

  class State {

    StateId id;
    State[] transitions;
    boolean zeroState;

    State(StateId id) {
      this.id = id;
      transitions = new State[10];
    }

    void add(int i, State state) {
      transitions[i] = state;
    }
  }
}
