<?php


namespace App\Model;


use App\Model\Entities\Localization\Translation;
use App\Model\Entities\Localization\GuiTranslation;
use App\Model\Entities\Localization\Language;
use App\Model\Exceptions\Translator\InvalidPluralsExpressionException;
use App\Model\Exceptions\Translator\TranslationAlreadyExistsException;
use App\Model\Facades\TranslatorFacade;
use App\Model\Interfaces\ITranslation;
use Nette\Localization\ITranslator;

class Translator implements ITranslator
{
    /** @var TranslatorFacade */
    protected $translatorFacade;

    /** @var  Language */
    protected $language;

    /** @var GuiTranslation[] */
    protected $guiTranslations;

    /** @var  Language */
    protected $defaultLanguage;

    /** @var  GuiTranslation[] */
    protected $defaultGuiTranslations;

    /**
     * Translator constructor.
     * @param TranslatorFacade $translatorFacade
     */
    public function __construct(TranslatorFacade $translatorFacade)
    {
        $this->translatorFacade = $translatorFacade;
    }

    public function setupLocale($locale)
    {
        $this->language = $this->translatorFacade->getLanguageReader()->findLanguage($locale);

        $this->guiTranslations = $this->translatorFacade->getTranslationReader()->getGuiTranslationsAssoc($this->language);
    }

    /**
     * @param string $message
     * @param int $count
     * @return string
     */
    public function translate($message, $count = NULL)
    {
        if ($count !== null) {
            $message = str_replace($count, '%d', $message);
        }

        $message = trim($message);
        $lastChar = substr($message, -1);
        if (in_array($lastChar, [',', '.', ':', '"'])) {
            $message = rtrim($message, ',.:"');
        } else {
            $lastChar = '';
        }

        if (strtoupper($message) === $message) {
            $translation = strtoupper($this->getGuiTranslation($message, $count)) . $lastChar;
        } elseif (ucwords($message) === $message) {
            $translation = ucwords($this->getGuiTranslation($message, $count)) . $lastChar;
        } elseif (ucfirst($message) === $message) {
            $translation = ucfirst($this->getGuiTranslation($message, $count)) . $lastChar;
        } else {
            $translation = $this->getGuiTranslation($message, $count) . $lastChar;
        }

        if ($count !== null) {
            return str_replace(['%d', '%D'], $count, $translation);
        }

        return $translation;
    }

    public function getSupportedLocales() : array
    {
        return $this->translatorFacade->getLanguageReader()->getAvailableLanguagesPairs();
    }

    public function getLanguage() : Language
    {
        return $this->language;
    }

    protected function getGuiTranslation($message, $count = null)
    {
        $key = strtolower($message);

        // if translation exists process it
        if (isset($this->guiTranslations[$key])) {
            $translation = $this->guiTranslations[$key];
            if ($translation->getState() !== Translation::PENDING) {
                return $translation->getTranslation();
            }
        }

        // try to retrieve translation for default language
        if ($this->language->getId() !== $this->getDefaultLanguage()->getId()) {
            if (isset($this->getDefaultGuiTranslations()[$key])) {
                $translation = $this->defaultGuiTranslations[$key];
                if ($translation->getState() !== Translation::PENDING) {
                    return $translation->getTranslation();
                }
            }
        }

        // no translation found
        if (!isset($translation)) {
            $this->translationRequest($message);
        }

        return $message;
    }

    protected function translationRequest($message)
    {
        $languages = $this->translatorFacade->getLanguageReader()->getLanguages();
        foreach ($languages as $language) {
            try {
                $this->translatorFacade->getTranslationWriter()->createGuiTranslation($message, $language);       // record not found, create new one for all languages
            } catch (TranslationAlreadyExistsException $e) {
                // translation already exists => do nothing;
            }
        }
    }

    protected function translatePlurals($n, ITranslation $translation) : string
    {
        //$gettext = $this->language->getPlurals();
        $gettext = "(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2";   // test expression

        if (!preg_match('/^[0-9n!=><()?:%&|\s]+$/', $gettext)) {
            throw new InvalidPluralsExpressionException();
        }

        $expresion = 'return ' . str_replace('n', '$n', $this->fixMultipleTernaryOperators($gettext)) . ';';
        $result = eval($expresion);

        //$translations = $translation->getTranslation();
        $translations = array('řízek', 'řízky', 'řízků');   // test data

        return $translations[$result];
    }

    protected function fixMultipleTernaryOperators($expression) : string
    {
        $parts = explode(':', $expression, 2);
        if (count($parts) === 2) {
            return "{$parts[0]} : ({$this->fixMultipleTernaryOperators($parts[1])} )";
        }

        return $expression;
    }

    /**
     * @return Language
     */
    protected function getDefaultLanguage() : Language
    {
        if ($this->defaultLanguage === null) {
            $this->defaultLanguage = $this->translatorFacade->getLanguageReader()->getDefaultLanguage();
        }

        return $this->defaultLanguage;
    }

    /**
     * @return GuiTranslation[]
     */
    protected function getDefaultGuiTranslations() : array
    {
        if ($this->defaultGuiTranslations === null) {
            $this->defaultGuiTranslations = $this->translatorFacade->getTranslationReader()->getGuiTranslationsAssoc($this->getDefaultLanguage());
        }

        return $this->defaultGuiTranslations;
    }

}