<?php

namespace S2\Rose\Stemmer;

/**
 * @see http://forum.dklab.ru/php/advises/HeuristicWithoutTheDictionaryExtractionOfARootFromRussianWord.html
 */
class PorterStemmerRussian extends AbstractStemmer implements StemmerInterface
{
    const SUPPORTS_REGEX = '#^[а-яА-ЯёЁ\-0-9]*$#Su';

    const VOWEL            = '/аеиоуыэюя/Su';
    const PERFECTIVEGROUND = '/((ив|ивши|ившись|ыв|ывши|ывшись)|((?<=[ая])(в|вши|вшись)))$/Su';
    const REFLEXIVE        = '/(с[яь])$/Su';
    const ADJECTIVE        = '/(ее|ие|ые|ое|ими|ыми|ей|ий|ый|ой|ем|им|ым|ом|его|ого|ему|ому|их|ых|еых|ую|юю|ая|яя|ою|ею)$/Su';
    const PARTICIPLE       = '/((ивш|ывш|ующ)|((?<=[ая])(ем|нн|вш|ющ|щ)))$/Su';
    const VERB             = '/((ила|ыла|ена|ейте|уйте|ите|или|ыли|ей|уй|ил|ыл|им|ым|ен|ило|ыло|ено|ят|ует|уют|ит|ыт|ены|ить|ыть|ишь|ую|ю)|((?<=[ая])(ла|на|ете|йте|ли|й|л|ем|н|ло|но|ет|ют|ны|ть|ешь|нно)))$/Su';
    const NOUN             = '/(а|ев|ов|ие|ье|е|иями|ями|ами|еи|ии|и|ией|ей|ой|ий|й|иям|ям|ием|ем|ам|ом|о|у|ах|иях|ях|ы|ь|у|ию|ью|ю|ия|ья|я)$/Su';
    const RVRE             = '/^(.*?[аеиоуыэюя])(.*)$/Su';
    const DERIVATIONAL     = '/[^аеиоуыэюя][аеиоуыэюя]+[^аеиоуыэюя]+[аеиоуыэюя].*(?<=о)сть?$/Su';

    protected static $irregularWords = [
        'и'       => '',
        'или'     => '',
        'когда'   => '',
        'где'     => '',
        'куда'    => '',
        'если'    => '',
        'тире'    => '',
        'после'   => '',
        'перед'   => '',
        'менее'   => '',
        'более'   => '',
        'уж'      => '',
        'уже'     => '',
        'там'     => '',
        'тут'     => '',
        'туда'    => '',
        'сюда'    => '',
        'оттуда'  => '',
        'отсюда'  => '',
        'здесь'   => '',
        'впрочем' => '',
        'зачем'   => '',
        'ничего'  => '',
        'никогда' => '',
        'иногда'  => '',
        'тогда'   => '',
        'всегда'  => '',
        'сейчас'  => '',
        'теперь'  => '',
        'сегодня' => '',
        'конечно' => '',
        'опять'   => '',
        'хоть'    => '',
        'хотя'    => '',
        'почти'   => '',
        'тоже'    => '',
        'также'   => '',
        'даже'    => '',
        'как'     => '',
        'так'     => '',
        'вот'     => '',
        'нет'     => '',
        'нету'    => 'нет',
        'вдруг'   => '',
        'через'   => '',
        'между'   => '',
        'еще'     => '',
        'ещё'     => 'еще',
        'чуть'    => '',
        'разве'   => '',
        'ведь'    => '',
        'нибудь'  => '',
        'будто'   => '',
        'можно'   => '',
        'нельзя'  => '',
        'хорошо'  => '',
        'только'  => '',
        'почему'  => '',
        'потому'  => '',
        'всего'   => '',
        'чтоб'    => '',
        'чтобы'   => 'чтоб',

        'под'  => '',
        'подо' => 'под',
        'об'   => '',
        'от'   => '',
        'без'  => '',
        'безо' => 'без',
        'над'  => '',
        'надо' => '',
        'из'   => '',

        'что'  => '',
        'чего' => 'что',
        'чему' => 'что',
        'чем'  => 'что',
        'чём'  => 'что',

        'кто'  => '',
        'кого' => 'кто',
        'кому' => 'кто',
        'кем'  => 'кто',
        'ком'  => 'кто',

        'шея'   => '',
        'шее'   => 'шея',
        'шеи'   => 'шея',
        'шеей'  => 'шея',
        'шей'   => 'шея',
        'шеями' => 'шея',
        'шеях'  => 'шея',

        'имя'     => '',
        'имени'   => 'имя',
        'именем'  => 'имя',
        'имена'   => 'имя',
        'именам'  => 'имя',
        'именами' => 'имя',
        'именах'  => 'имя',

        'она' => '',
        'её'  => 'она',
        'ее'  => 'она',
        'ей'  => 'она',
        'ней' => 'она',

        'один'   => '',
        'одного' => 'один',
        'одному' => 'один',
        'одним'  => 'один',
        'одном'  => 'один',

        'одна'  => '',
        'одной' => 'одна',
        'одну'  => 'одна',

        'он'  => '',
        'его' => 'он',
        'ему' => 'он',
        'ним' => 'он',
//		'им'		=> 'он',
        'нем' => 'он',
        'нём' => 'он',

        'я'    => '',
        'меня' => 'я',
        'мне'  => 'я',
        'мной' => 'я',

        'ты'    => '',
        'тебя'  => 'ты',
        'тебе'  => 'ты',
        'тобой' => 'ты',

        'вас' => 'вы',
        'вам' => 'вы',

        'нас' => 'мы',
        'нам' => 'мы',

        'они'  => '',
        'их'   => 'они',
        'им'   => 'они',
        'ими'  => 'они',
        'ними' => 'они',
        'них'  => 'они',

        'ересь'  => 'ерес',
        'ереси'  => 'ерес',
        'ересью' => 'ерес',

        'ищу'   => 'иска',
        'ищешь' => 'иска',
        'ищет'  => 'иска',
        'ищем'  => 'иска',
        'ищете' => 'иска',
        'ищут'  => 'иска',
    ];

    protected $cache = [];

    /**
     * {@inheritdoc}
     */
    public function stemWord($word)
    {
        $word = \mb_strtolower($word);
        $word = \str_replace('ё', 'е', $word);

        if (isset($this->cache[$word])) {
            return $this->cache[$word];
        }

        if (isset(self::$irregularWords[$word])) {
            return self::$irregularWords[$word] !== '' ? self::$irregularWords[$word] : $word;
        }

        if (!\preg_match(self::SUPPORTS_REGEX, $word)) {
            return $this->nextStemmer !== null ? $this->nextStemmer->stemWord($word) : $word;
        }

        $stem = $word;
        do {
            if (!\preg_match(self::RVRE, $word, $p)) {
                break;
            }

            $start = $p[1];
            $RV    = $p[2];
            if (!$RV) {
                break;
            }

            # Step 1
            if (!self::s($RV, self::PERFECTIVEGROUND, '')) {
                self::s($RV, self::REFLEXIVE, '');

                if (self::s($RV, self::ADJECTIVE, '')) {
                    self::s($RV, self::PARTICIPLE, '');
                } else {
                    if (!self::s($RV, self::VERB, '')) {
                        self::s($RV, self::NOUN, '');
                    }
                }
            }

            # Step 2
            self::s($RV, '/и$/Su', '');

            # Step 3
            if (\preg_match(self::DERIVATIONAL, $RV)) {
                self::s($RV, '/ость?$/Su', '');
            }

            # Step 4
            if (!self::s($RV, '/ь$/Su', '')) {
                self::s($RV, '/ейше?/Su', '');
                self::s($RV, '/нн$/Su', 'н');
            }

            $stem = $start . $RV;
        } while (false);

        $this->cache[$word] = $stem;

        return $stem;
    }

    protected static function s(&$s, $re, $to)
    {
        $orig = $s;
        $s    = \preg_replace($re, $to, $s);

        return $orig !== $s;
    }

    /**
     * {@inheritdoc}
     */
    protected function getIrregularWords()
    {
        return self::$irregularWords;
    }
}
