<?php

namespace Drupal\basket\Commands;

use Drush\Commands\DrushCommands;
use Drupal\Component\Gettext\PoStreamWriter;
use Drupal\Component\Gettext\PoItem;
use Drupal\Component\Gettext\PoReaderInterface;
use \Drupal\Component\Gettext\PoHeader;
use Drupal\Component\Serialization\Yaml;

class BasketCommands extends DrushCommands{
    /**
     * Basket translate update po files (basket:po $module_name)
     *
     * @param string $moduleName
     *
     * @command basket:po
     * @usage basket:po                     Basket translate update po files
     */
    public function basketPO($moduleName = '---'){
        if (\Drupal::moduleHandler()->moduleExists($moduleName)){
            $moduleInfo = \Drupal::service('extension.list.module')->getExtensionInfo($moduleName);
            if(empty($moduleInfo['project'])) { 
                \Drupal::logger('basket:po')->error($moduleName.'.info.yml not "project" info');
            }
            if(empty($moduleInfo['version'])){
                \Drupal::logger('basket:po')->error($moduleName.'.info.yml not "version" info');
            }
            if(!empty($moduleInfo['project']) && !empty($moduleInfo['version'])){
                $dir = realpath(drupal_get_path('module', $moduleName)).'/translations/';
                if(is_dir($dir)){
                    \Drupal::service('file_system')->deleteRecursive($dir);
                }
                @\Drupal::service('file_system')->mkdir($dir, NULL, TRUE);
                foreach (\Drupal::languageManager()->getLanguages() as $langcode => $language){
                    if($langcode == 'en') continue;
                    if(is_dir($dir)){
                        $reader = new BasketPoDatabaseReader();
                        $reader->setLangcode($langcode);
                        $reader->setOptions([
                            'not_translated'    => FALSE,
                            'translated'        => TRUE,
                            'context'           => $moduleName
                        ]);
                        $item = $reader->readItem();
                        if(!empty($item)){
                            $uri = $dir.$moduleInfo['project'].'-'.$moduleInfo['version'].'.'.$langcode.'.po';
                            // Header
                            $header  = new PoHeader($langcode);
                            $header->setProjectName($moduleInfo['project']);
                            $header->setLanguageName($langcode);
                            $header->setFromString('Plural-Forms: '.$this->getPluralForm($langcode));

                            $writer = new PoStreamWriter();
                            $writer->setHeader($header);
                            $writer->setURI($uri);
                            $writer->open();
                            $writer->writeItem($item);
                            $writer->writeItems($reader);
                            $writer->close();
                        }
                        \Drupal::logger('basket:po')->notice('Finish "'.$langcode.'"');
                    }
                }
            }
        } else {
            \Drupal::logger('basket:po')->error('Module not found "'.$moduleName.'"');
        }
    }

    /**
     * @see http://docs.translatehouse.org/projects/localization-guide/en/latest/l10n/pluralforms.html?id=l10n/pluralforms
     */
    protected function getPluralForm($langcode){
        switch($langcode){
            case 'en':
                return 'nplurals=2; plural=(n != 1);';
            default:
                return 'nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);';
        }
    }
    /**
     * Basket translate update po files (basket:po_update $module_name)
     *
     * @param string $moduleName
     *
     * @command basket:po_update
     * @usage basket:po_update                     Basket translate update po files
     */
    public function basketPOUpdate($moduleName = '---'){
        if (!\Drupal::moduleHandler()->moduleExists('locale')){
            \Drupal::logger('basket:po_update')->error('Module not found "locale"');
        } else if (\Drupal::moduleHandler()->moduleExists($moduleName)){
            $moduleInfo = \Drupal::service('extension.list.module')->getExtensionInfo($moduleName);
            if(empty($moduleInfo['project'])) { 
                \Drupal::logger('basket:po')->error($moduleName.'.info.yml not "project" info');
            }
            if(empty($moduleInfo['version'])){
                \Drupal::logger('basket:po')->error($moduleName.'.info.yml not "version" info');
            }
            if(!empty($moduleInfo['project']) && !empty($moduleInfo['version'])){
                $dir = drupal_get_path('module', $moduleName).'/translations/';
                if(is_dir($dir)){
                    \Drupal::moduleHandler()->loadInclude('locale', 'translation.inc');
                     \Drupal::moduleHandler()->loadInclude('locale', 'bulk.inc');
                    foreach (\Drupal::languageManager()->getLanguages() as $langcode => $language){
                        $options = array_merge(_locale_translation_default_update_options(), [
                            'langcode'          => $langcode
                        ]);
                        $filename = $moduleInfo['project'].'-'.$moduleInfo['version'].'.'.$langcode.'.po';
                        $uri = $dir.$filename;
                        if(file_exists($uri)){
                            $file = \Drupal\file\Entity\File::create([
                              'uid'         => 1,
                              'filename'    => $filename,
                              'uri'         => $uri,
                              'status'      => 1,
                            ]);
                            $file = locale_translate_file_attach_properties($file, $options);
                            $batch = locale_translate_batch_build([$file->uri => $file], $options);
                            batch_set($batch);
                        }
                    }
                    drush_backend_batch_process();
                }
            }
        } else {
            \Drupal::logger('basket:po_update')->error('Module not found "'.$moduleName.'"');
        }
    }
    /**
     * Basket translate update po files (basket:po_update $module_name)
     *
     * @param string $moduleName
     *
     * @command basket:readme
     * @usage basket:readme                 Basket generate readme.md
     */
    public function genedateReadme($moduleName = '- - -'){
        if (!\Drupal::moduleHandler()->moduleExists($moduleName)) return FALSE;
        $items = [];
        // ---
        $hooksFile = drupal_get_path('module', $moduleName).'/config/basket_install/HOOKs.yml';
        if(file_exists($hooksFile)){
            $ymldata = Yaml::decode(file_get_contents($hooksFile));
            foreach ($ymldata as $groupKey => $groupInfo) {
                $items[] = '# '.trim($groupInfo['title']);
                $items[] = '';
                foreach ($groupInfo['lists'] as $hookKey => $hookInfo) {
                    $items[] = '#### '.trim($hookInfo['title']);
                    $items[] = '````';
                        $items[] = '/**';
                        $items[] = ' * Implements hook_'.$hookKey.'_alter().';
                        $items[] = ' * '.implode(PHP_EOL.' * ', !empty($hookInfo['descriptions']) ? $hookInfo['descriptions'] : []);
                        $items[] = ' */';
                        $items[] = 'function HOOK_'.$hookKey.'_alter('.$hookInfo['params'].'){';
                        $items[] = '';
                        $items[] = '}';
                    $items[] = '````';

                    $items[] = '';
                }
                $items[] = '';
                $items[] = '';
                $items[] = '';
            }
        }
        // ---
        file_put_contents(drupal_get_path('module', $moduleName).'/README.md', trim(implode(PHP_EOL, $items)));
    }
}


/**
 * Gettext PO reader working with the locale module database.
 */
class BasketPoDatabaseReader implements PoReaderInterface {
    private $options;
    private $langcode;
    private $result;
    public function __construct() {
        $this->setOptions([]);
    }
    public function getLangcode() {
        return $this->langcode;
    }
    public function setLangcode($langcode) {
        $this->langcode = $langcode;
    }
    public function getOptions() {
        return $this->options;
    }
    public function setOptions(array $options) {
        $options += [
            'customized' => FALSE,
            'not_customized' => FALSE,
            'not_translated' => FALSE,
        ];
        $this->options = $options;
    }
    public function getHeader() {
        return new PoHeader($this->getLangcode());
    }
    public function setHeader(PoHeader $header) {
        throw new \Exception('You cannot set the PO header in a reader.');
    }
    private function loadStrings() {
        $langcode = $this->langcode;
        $options = $this->options;
        $conditions = [];

        if (array_sum($options) == 0) {
            $langcode = NULL;
            $options['not_translated'] = TRUE;
        }
        if (!empty($langcode)) {
            $conditions['language'] = $langcode;
            if ($options['customized']) {
                if (!$options['not_customized']) {
                    $conditions['customized'] = LOCALE_CUSTOMIZED;
                }
            } else {
                if ($options['not_customized']) {
                    $conditions['customized'] = LOCALE_NOT_CUSTOMIZED;
                } else {
                    $conditions['translated'] = FALSE;
                }
            }
            if (!$options['not_translated']) {
                $conditions['translated'] = TRUE;
            }
            $conditions['context'] = $this->options['context'];
            return \Drupal::service('locale.storage')->getTranslations($conditions);
        } else {
            return \Drupal::service('locale.storage')->getStrings($conditions);
        }
    }
    private function readString() {
        if (!isset($this->result)) {
            $this->result = $this->loadStrings();
        }
        return array_shift($this->result);
    }
    public function readItem() {
        if ($string = $this->readString()) {
            $values = (array) $string;
            $po_item = new PoItem();
            $po_item->setFromArray($values);
            return $po_item;
        }
    }
}