//library inflection2.singular;

import 'dart:convert';

import 'uncountable_nouns.dart';
import 'irregular_plural_nouns.dart';
import 'util.dart';

class SingularEncoder extends Converter<String, String> {
  final List<List> _inflectionRules = [];

  SingularEncoder() {
    irregularPluralNouns.forEach((singular, plural) {
      addIrregularInflectionRule(singular, plural);
    });

    [
      [r's$', (Match m) => ''],
      [r'(ss)$', (Match m) => m[1]],
      [r'(n)ews$', (Match m) => '${m[1]}ews'], // TODO: uncountable?
      [r'([ti])a$', (Match m) => '${m[1]}um'],
      [
        r'((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$',
        (Match m) => '${m[1]}sis'
      ],
      [r'(^analy)(sis|ses)$', (Match m) => '${m[1]}sis'], // TODO: not needed?
      [r'([^f])ves$', (Match m) => '${m[1]}fe'],
      [r'(hive|tive)s$', (Match m) => m[1]],
      [r'([lr])ves$', (Match m) => '${m[1]}f'],
      [r'([^aeiouy]|qu)ies$', (Match m) => '${m[1]}y'],
      [r'(s)eries$', (Match m) => '${m[1]}eries'], // TODO: uncountable
      [r'(m)ovies$', (Match m) => '${m[1]}ovie'],
      [r'(x|ch|ss|sh)es$', (Match m) => m[1]],
      [r'^(m|l)ice$', (Match m) => '${m[1]}ouse'],
      [r'(bus)(es)?$', (Match m) => m[1]],
      [r'(shoe)s$', (Match m) => m[1]],
      [r'(cris|test)(is|es)$', (Match m) => '${m[1]}is'],
      [r'^(a)x[ie]s$', (Match m) => '${m[1]}xis'],
      [r'(octop|vir)(us|i)$', (Match m) => '${m[1]}us'],
      [r'(alias|status)(es)?$', (Match m) => m[1]],
      [r'^(ox)en', (Match m) => m[1]],
      [r'(vert|ind)ices$', (Match m) => '${m[1]}ex'],
      [r'(matr)ices$', (Match m) => '${m[1]}ix'],
      [r'(quiz)zes$', (Match m) => m[1]],
      [r'(database)s$', (Match m) => m[1]]
    ]
        .reversed
        .forEach((rule) => addInflectionRule(rule.first as String, rule.last));
  }

  void addInflectionRule(String plural, dynamic singular) {
    _inflectionRules.add([new RegExp(plural, caseSensitive: false), singular]);
  }

  void addIrregularInflectionRule(String singular, String plural) {
    final s0 = singular.substring(0, 1);
    final srest = singular.substring(1);
    final p0 = plural.substring(0, 1);
    final prest = plural.substring(1);

    if (s0.toUpperCase() == p0.toUpperCase()) {
      addInflectionRule('(${s0})${srest}\$', (Match m) => '${m[1]}${srest}');
      addInflectionRule('(${p0})${prest}\$', (Match m) => '${m[1]}${srest}');
    } else {
      addInflectionRule('${s0.toUpperCase()}(?i)${srest}\$',
          (Match m) => '${s0.toUpperCase()}${srest}');
      addInflectionRule('${s0.toLowerCase()}(?i)${srest}\$',
          (Match m) => '${s0.toUpperCase()}${srest}');
      addInflectionRule('${p0.toUpperCase()}(?i)${prest}\$',
          (Match m) => '${s0.toUpperCase()}${srest}');
      addInflectionRule('${p0.toLowerCase()}(?i)${prest}\$',
          (Match m) => '${s0.toLowerCase()}${srest}');
    }
  }

  @override
  String convert(String word) {
    if (!word.isEmpty) {
      if (uncountableNouns.contains(word.toLowerCase())) {
        return word;
      } else {
        for (var r in _inflectionRules) {
          var pattern = r.first as RegExp;
          if (pattern.hasMatch(word)) {
            return word.replaceAllMapped(pattern, r.last as MatchToString);
          }
        }
      }
    }

    return word;
  }
}

final Converter<String, String> SINGULAR = new SingularEncoder();
