# This class comes from the Inflect module; it has been
# copied from the unmaintained 'english' ruby gem,
# created by Thomas Sawyer.
#
# Released under the MIT License.
#
#  http://english.rubyforge.org
module Treat::Workers::Inflectors::Declensors::English::Inflect

  @singular_of = {}
  @plural_of = {}

  @singular_rules = []
  @plural_rules = []

  # This class provides the DSL for creating inflections, you can add additional rules.
  # Examples:
  #
  #   word "ox", "oxen"
  #   word "octopus", "octopi"
  #   word "man", "men"
  #
  #   rule "lf", "lves"
  #
  #   word "equipment"
  #
  # Rules are evaluated by size, so rules you add to override specific cases should be longer than the rule
  # it overrides. For instance, if you want "pta" to pluralize to "ptas", even though a general purpose rule
  # for "ta" => "tum" already exists, simply add a new rule for "pta" => "ptas", and it will automatically win
  # since it is longer than the old rule.
  #
  # Also, single-word exceptions win over general words ("ox" pluralizes to "oxen", because it's a single word
  # exception, even though "fox" pluralizes to "foxes")
  class << self
    # Define a general two-way exception.
    #
    # This also defines a general rule, so foo_child will correctly become
    # foo_children.
    #
    # Whole words also work if they are capitalized (Goose => Geese).
    def word(singular, plural=nil)
      plural = singular unless plural
      singular_word(singular, plural)
      plural_word(singular, plural)
      rule(singular, plural)
    end

    # Define a singularization exception.
    def singular_word(singular, plural)
      @singular_of[plural] = singular
      @singular_of[plural.capitalize] = singular.capitalize
    end

    # Define a pluralization exception.
    def plural_word(singular, plural)
      @plural_of[singular] = plural
      @plural_of[singular.capitalize] = plural.capitalize
    end

    # Define a general rule.
    def rule(singular, plural)
      singular_rule(singular, plural)
      plural_rule(singular, plural)
    end

    # Define a singularization rule.
    def singular_rule(singular, plural)
      @singular_rules << [singular, plural]
    end

    # Define a plurualization rule.
    def plural_rule(singular, plural)
      @plural_rules << [singular, plural]
    end

    # Read prepared singularization rules.
    def singularization_rules
      if defined?(@singularization_regex) && @singularization_regex
        return [@singularization_regex, @singularization_hash]
      end
      # No sorting needed: Regexen match on longest string
      @singularization_regex = Regexp.new("(" + @singular_rules.map {|s,p| p}.join("|") + ")$", "i")
      @singularization_hash  = Hash[*@singular_rules.flatten].invert
      [@singularization_regex, @singularization_hash]
    end

    # Read prepared singularization rules.
    #def singularization_rules
    #  return @singularization_rules if @singularization_rules
    #  sorted = @singular_rules.sort_by{ |s, p| "#{p}".size }.reverse
    #  @singularization_rules = sorted.collect do |s, p|
    #    [ /#{p}$/, "#{s}" ]
    #  end
    #end

    # Read prepared pluralization rules.
    def pluralization_rules
      if defined?(@pluralization_regex) && @pluralization_regex
        return [@pluralization_regex, @pluralization_hash]
      end
      @pluralization_regex = Regexp.new("(" + @plural_rules.map {|s,p| s}.join("|") + ")$", "i")
      @pluralization_hash = Hash[*@plural_rules.flatten]
      [@pluralization_regex, @pluralization_hash]
    end

    # Read prepared pluralization rules.
    #def pluralization_rules
    #  return @pluralization_rules if @pluralization_rules
    #  sorted = @plural_rules.sort_by{ |s, p| "#{s}".size }.reverse
    #  @pluralization_rules = sorted.collect do |s, p|
    #    [ /#{s}$/, "#{p}" ]
    #  end
    #end

    #
    def singular_of ; @singular_of ; end

    #
    def plural_of   ; @plural_of   ; end

    # Convert an English word from plurel to singular.
    #
    #   "boys".singular      #=> boy
    #   "tomatoes".singular  #=> tomato
    #
    def singular(word)
      return "" if word == ""
      if result = singular_of[word]
        return result.dup
      end
      result = word.dup

      regex, hash = singularization_rules
      result.sub!(regex) {|m| hash[m]}
      singular_of[word] = result
      return result
      #singularization_rules.each do |(match, replacement)|
      #  break if result.gsub!(match, replacement)
      #end
      #return result
    end

    # Alias for #singular (a Railism).
    #
    alias_method(:singularize, :singular)

    # Convert an English word from singular to plurel.
    #
    #   "boy".plural     #=> boys
    #   "tomato".plural  #=> tomatoes
    #
    def plural(word)
      return "" if word == ""
      if result = plural_of[word]
        return result.dup
      end
      #return self.dup if /s$/ =~ self # ???
      result = word.dup

      regex, hash = pluralization_rules
      result.sub!(regex) {|m| hash[m]}
      plural_of[word] = result
      return result
      #pluralization_rules.each do |(match, replacement)|
      #  break if result.gsub!(match, replacement)
      #end
      #return result
    end

    # Alias for #plural (a Railism).
    alias_method(:pluralize, :plural)

    # Clear all rules.
    def clear(type = :all)
      if type == :singular || type == :all
        @singular_of = {}
        @singular_rules = []
        @singularization_rules, @singularization_regex = nil, nil
      end
      if type == :plural || type == :all
        @singular_of = {}
        @singular_rules = []
        @singularization_rules, @singularization_regex = nil, nil
      end
    end
  end

  # One argument means singular and plural are the same.

  word 'equipment'
  word 'information'
  word 'money'
  word 'species'
  word 'series'
  word 'fish'
  word 'sheep'
  word 'moose'
  word 'hovercraft'
  word 'news'
  word 'rice'
  word 'plurals'

  # Two arguments defines a singular and plural exception.

  word 'Swiss'     , 'Swiss'
  word 'alias'     , 'aliases'
  word 'analysis'  , 'analyses'
  #word 'axis'      , 'axes'
  word 'basis'     , 'bases'
  word 'buffalo'   , 'buffaloes'
  word 'child'     , 'children'
  #word 'cow'       , 'kine'
  word 'crisis'    , 'crises'
  word 'criterion' , 'criteria'
  word 'datum'     , 'data'
  word 'goose'     , 'geese'
  word 'hive'      , 'hives'
  word 'index'     , 'indices'
  word 'life'      , 'lives'
  word 'louse'     , 'lice'
  word 'man'       , 'men'
  word 'matrix'    , 'matrices'
  word 'medium'    , 'media'
  word 'mouse'     , 'mice'
  word 'movie'     , 'movies'
  word 'octopus'   , 'octopi'
  word 'ox'        , 'oxen'
  word 'person'    , 'people'
  word 'potato'    , 'potatoes'
  word 'quiz'      , 'quizzes'
  word 'shoe'      , 'shoes'
  word 'status'    , 'statuses'
  word 'testis'    , 'testes'
  word 'thesis'    , 'theses'
  word 'thief'     , 'thieves'
  word 'tomato'    , 'tomatoes'
  word 'torpedo'   , 'torpedoes'
  word 'vertex'    , 'vertices'
  word 'virus'     , 'viri'
  word 'wife'      , 'wives'

  # One-way singularization exception (convert plural to singular).

  singular_word 'cactus', 'cacti'

  # One-way pluralizaton exception (convert singular to plural).

  plural_word 'axis', 'axes'

  # General rules.

  rule 'rf'     , 'rves'
  rule 'ero'    , 'eroes'
  rule 'ch'     , 'ches'
  rule 'sh'     , 'shes'
  rule 'ss'     , 'sses'
  #rule 'ess'  , 'esses'
  rule 'ta'     , 'tum'
  rule 'ia'     , 'ium'
  rule 'ra'     , 'rum'
  rule 'ay'     , 'ays'
  rule 'ey'     , 'eys'
  rule 'oy'     , 'oys'
  rule 'uy'     , 'uys'
  rule 'y'      , 'ies'
  rule 'x'      , 'xes'
  rule 'lf'     , 'lves'
  rule 'ffe'    , 'ffes'
  rule 'af'     , 'aves'
  rule 'us'     , 'uses'
  rule 'ouse'   , 'ouses'
  rule 'osis'   , 'oses'
  rule 'ox'     , 'oxes'
  rule ''       , 's'

  # One-way singular rules.

  singular_rule 'of' , 'ofs' # proof
  singular_rule 'o'  , 'oes' # hero, heroes
  #singular_rule 'f'  , 'ves'

  # One-way plural rules.

  plural_rule 's'   , 'ses'
  plural_rule 'ive' , 'ives' # don't want to snag wife
  plural_rule 'fe'  , 'ves'  # don't want to snag perspectives

end