/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.lucene.analysis.cz;

import static org.apache.lucene.analysis.util.StemmerUtil.*;

/**
 * Light Stemmer for Czech.
 *
 * <p>Implements the algorithm described in: <i> Indexing and stemming approaches for the Czech
 * language </i> http://portal.acm.org/citation.cfm?id=1598600
 */
public class CzechStemmer {

  /**
   * Stem an input buffer of Czech text.
   *
   * @param s input buffer
   * @param len length of input buffer
   * @return length of input buffer after normalization
   *     <p><b>NOTE</b>: Input is expected to be in lowercase, but with diacritical marks
   */
  public int stem(char[] s, int len) {
    len = removeCase(s, len);
    len = removePossessives(s, len);
    if (len > 0) {
      len = normalize(s, len);
    }
    return len;
  }

  private int removeCase(char[] s, int len) {
    if (len > 7 && endsWith(s, len, "atech")) return len - 5;

    if (len > 6
        && (endsWith(s, len, "ětem") || endsWith(s, len, "etem") || endsWith(s, len, "atům")))
      return len - 4;

    if (len > 5
        && (endsWith(s, len, "ech")
            || endsWith(s, len, "ich")
            || endsWith(s, len, "ích")
            || endsWith(s, len, "ého")
            || endsWith(s, len, "ěmi")
            || endsWith(s, len, "emi")
            || endsWith(s, len, "ému")
            || endsWith(s, len, "ěte")
            || endsWith(s, len, "ete")
            || endsWith(s, len, "ěti")
            || endsWith(s, len, "eti")
            || endsWith(s, len, "ího")
            || endsWith(s, len, "iho")
            || endsWith(s, len, "ími")
            || endsWith(s, len, "ímu")
            || endsWith(s, len, "imu")
            || endsWith(s, len, "ách")
            || endsWith(s, len, "ata")
            || endsWith(s, len, "aty")
            || endsWith(s, len, "ých")
            || endsWith(s, len, "ama")
            || endsWith(s, len, "ami")
            || endsWith(s, len, "ové")
            || endsWith(s, len, "ovi")
            || endsWith(s, len, "ými"))) return len - 3;

    if (len > 4
        && (endsWith(s, len, "em")
            || endsWith(s, len, "es")
            || endsWith(s, len, "ém")
            || endsWith(s, len, "ím")
            || endsWith(s, len, "ům")
            || endsWith(s, len, "at")
            || endsWith(s, len, "ám")
            || endsWith(s, len, "os")
            || endsWith(s, len, "us")
            || endsWith(s, len, "ým")
            || endsWith(s, len, "mi")
            || endsWith(s, len, "ou"))) return len - 2;

    if (len > 3) {
      switch (s[len - 1]) {
        case 'a':
        case 'e':
        case 'i':
        case 'o':
        case 'u':
        case 'ů':
        case 'y':
        case 'á':
        case 'é':
        case 'í':
        case 'ý':
        case 'ě':
          return len - 1;
      }
    }

    return len;
  }

  private int removePossessives(char[] s, int len) {
    if (len > 5 && (endsWith(s, len, "ov") || endsWith(s, len, "in") || endsWith(s, len, "ův")))
      return len - 2;

    return len;
  }

  private int normalize(char[] s, int len) {
    if (endsWith(s, len, "čt")) { // čt -> ck
      s[len - 2] = 'c';
      s[len - 1] = 'k';
      return len;
    }

    if (endsWith(s, len, "št")) { // št -> sk
      s[len - 2] = 's';
      s[len - 1] = 'k';
      return len;
    }

    switch (s[len - 1]) {
      case 'c': // [cč] -> k
      case 'č':
        s[len - 1] = 'k';
        return len;
      case 'z': // [zž] -> h
      case 'ž':
        s[len - 1] = 'h';
        return len;
    }

    if (len > 1 && s[len - 2] == 'e') {
      s[len - 2] = s[len - 1]; // e* > *
      return len - 1;
    }

    if (len > 2 && s[len - 2] == 'ů') {
      s[len - 2] = 'o'; // *ů* -> *o*
      return len;
    }

    return len;
  }
}
