<?php
/**
 * Copyright (c) 2016 Serhii Borodai <clarifying@gmail.com>.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 *
 */

namespace App\Model;

use App\Entity\FAQ\Category;
use App\Entity\FAQ\Item;
use App\Model\FAQ\Categories;
use App\Model\FAQ\Items as FAQItems;
use Gettext\Merge;
use Gettext\Translations as TranslationsCollection;
use Gettext\Translator;


/**
 * Class Translations
 *
 * Used to manipulate gettext translation resources (create, read, update)
 *
 * @package App\Model
 */
class Translations
{
    const PART_COMMON = 'common';
    const PART_BLOG = 'blog';
    const PART_FAQ = 'faq';

    const parts = [self::PART_COMMON, self::PART_BLOG, self::PART_FAQ];

    /**
     * @var Categories
     */
    protected $faqCategoriesModel;
    /**
     * @var FAQItems
     */
    protected $faqItemsModel;
    
    /**
     * @var string
     */
    protected $commonDirs;

    /**
     * @var string
     */
    protected $localePath;

    /**
     * Translations constructor.
     * @param VacanciesItems $vacanciesModel
     * @param Categories $faqCategoriesModel
     * @param FAQItems $faqItemsModel
     * @param array $commonDirs
     * @param string $localePath
     */
    public function __construct(
        
        Categories $faqCategoriesModel,
        FaqItems $faqItemsModel,
        array $commonDirs,
        $localePath = 'data/locale/'
    )
    {
        $this->localePath = rtrim($localePath, '/\\') . '/';

        $this->translator = new Translator();

        
        $this->faqCategoriesModel = $faqCategoriesModel;
        $this->faqItemsModel = $faqItemsModel;

        $this->commonDirs = $commonDirs;
        $this->localePath = $localePath;
    }

    /**
     * Register translator handler
     *
     * @param $lang
     */
    public function register($lang)
    {
        $this->translator->loadTranslations($this->getMergedTranslationParts($lang));
        $this->translator->register();
    }

    /**
     * Returns list of dirs or single dir if language specified
     *
     * @param string $lang
     * @return array|mixed
     */
    public function getDirs($lang = '*')
    {
        $result = glob($this->localePath . $lang, GLOB_ONLYDIR);
        if ($lang != '*') {
            $result = reset($result);
        }
        return $result;
    }

    /**
     * @param array $locales list of languages to which required to create dir
     */
    public function createDirs($locales)
    {
        foreach ($locales as $localeCode) {
            $dir = realpath($this->localePath . $localeCode);
            if (!$dir) {
                mkdir($this->localePath . $localeCode);
            }
        }

    }

    /**
     * Use this method to load all translation content as one collection
     *
     * @param $lang
     * @return TranslationsCollection
     */
    public function getMergedTranslationParts($lang)
    {
        $result = new TranslationsCollection();
        foreach (self::parts as $part) {
            $tmp = $this->load($part, $lang);
            $result->mergeWith($tmp[$lang]);
        }
        return $result;
    }

    /**
     * Load specific language translations for selected part
     * All if null passed
     * DO NOT USE for client part instead use \App\Model\Translations::getMergedTranslations to load all parts as one collection
     *
     * @param string $part
     * @param string|null $lang
     * @return TranslationsCollection[]
     */
    public function load($part = self::PART_COMMON, $lang = null)
    {
        $result = [];

        if ($lang) {
            $fileName = rtrim($this->getDirs($lang), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $part . '.po';
            if (realpath($fileName)) {
                $result[$lang] = TranslationsCollection::fromPoFile($fileName);
            } else {
                $result[$lang] = TranslationsCollection::fromJsonString('{[]}');
            }
        } else {
            $locales = new Locales();
            //use only enabled languages
            $dirs = array_map([$this, 'getDirs'], $locales->getLanguages());
            foreach ($dirs as $dir) {
                $fileName = realpath(rtrim($dir, '/\\') . '/' . $part . '.po');
                $dirsInPath = explode('/', rtrim($dir, '/\\'));
                $dirLang = end($dirsInPath);
                if ($fileName) {
                    $result[$dirLang] = TranslationsCollection::fromPoFile($fileName);
                } else {
                    $result[$dirLang] = TranslationsCollection::fromJsonString('{[]}');
                }
            }
        }
        return $result;
    }

    /**
     * @param $translations
     * @param $part
     */
    public function save($translations, $part)
    {
        /** @var TranslationsCollection $collection */
        foreach ($translations as $lang => $collection) {
            $collection->setLanguage($lang);
            $collection->toPoFile($this->getDirs($lang) . '/' . $part . '.po');
        }
    }

    /**
     * @return \Gettext\Translations[]
     */
    public function regenerate($part)
    {

        $localesModel = new Locales();

        $this->createDirs($localesModel->getLanguages());

        $result = $this->load($part);

        $result = $this->generate($result, $part);

        $this->save($result, $part);
        return $result;
    }

    /**
     * @param $translationList
     * @return mixed
     */
    private function generateVacancies($translationList)
    {

        return $translationList;
    }

    /**
     * @param $translationList
     * @return mixed
     */
    private function generateFaq($translationList)
    {

        $categories = $this->faqCategoriesModel->findAll([HideableInterface::COLUMN_NAME_HIDDEN => HideableInterface::ANY]);
        $items = $this->faqItemsModel->findAll([HideableInterface::COLUMN_NAME_HIDDEN => HideableInterface::ANY]);

        /** @var \Gettext\Translations $collection */
        foreach ($categories as $category) {
            foreach ($translationList as $lang => $collection) {
                /** @var Category $category */
                $collection->insert(null, $category->getName(), '');
                $collection->insert(null, $category->getDesc(), '');
            }
        }
        /** @var Item $item */
        foreach ($items as $item) {
            foreach ($translationList as $lang => $collection) {
                $collection->insert(null, $item->getName(), '');
                $collection->insert(null, $item->getContent(), '');
            }
        }

        return $translationList;
    }

    /**
     * @param $translationList
     * @return mixed
     */
    private function generateCommon($translationList)
    {
        $iterators = [];
        foreach ($this->commonDirs as $dir) {
            $directoryIterator = new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS);
            $iterators[] = new \RecursiveIteratorIterator($directoryIterator);
        }


        /** @var \Gettext\Translations $collection */
        foreach ($translationList as $lang => $collection) {
            foreach ($iterators as $iterator) {
                /** @var \SplFileInfo $file */
                foreach ($iterator as $file) {
                    if ($file && $file->isFile() && $file->getExtension() == 'phtml') {
                        $collection->addFromPhpCodeFile($file->getPathname(), ['functions' => ['_t' => 'gettext']]);
                    }
                }
            }
        }

        return $translationList;
    }

    /**
     * @param $translationList
     * @return mixed
     */
    private function generateBlog($translationList)
    {
        return $translationList;
    }

    public function clearUnused()
    {

        foreach (self::parts as $part) {
            $translationListFromPO = $this->load($part);
            $translationListGenerated = $this->generate($this->createEmptyTranslationList(), $part);
            /** @var TranslationsCollection $collection */
            foreach ($translationListGenerated as $lang => $collection) {
                $translationListFromPO[$lang]->mergeWith($collection, Merge::REMOVE);
                $translationListFromPO[$lang]->mergeWith($collection, Merge::ADD);
            }
            $this->save($translationListFromPO, $part);
        }

    }

    /**
     * @return array
     */
    private function createEmptyTranslationList()
    {
        $translationListEmpty = [];
        $locales = new Locales();

        foreach ($locales->getLanguages() as $lang) {
            $translationListEmpty[$lang] = TranslationsCollection::fromJsonString('{[]}');
        }

        return $translationListEmpty;
    }

    /**
     * @param $part
     * @return mixed
     */
    private function generate($result, $part)
    {
        switch ($part) {
            case self::PART_COMMON:
                $result = $this->generateCommon($result);
                break;
            case self::PART_FAQ:
                $result = $this->generateFaq($result);
                break;
            case self::PART_BLOG:
                $result = $this->generateBlog($result);
                break;
        }
        return $result;
    }
}