<?php
require_once(dirname(dirname(__DIR__)) . '/vendor/autoload.php');

/**
 * MODx Polylang Class
 *
 * @package polylang
 */
class Polylang
{
    /** @var modX $modx */
    public $modx;
    /** @var PolylangTools $tools */
    protected $tools;
    /** @var PolylangGeoLocatorInterface $geoLocator */
    protected $geoLocator;
    /** @var PolylangTranslator $translator */
    protected $translator;
    /** @var PolylangCrawlerDetector $crawlerDetector */
    protected $crawlerDetector;
    /** @var string $namespace */
    protected $namespace = 'polylang';
    /** @var string */
    public $version = '1.3.14-pl';


    /**
     * Polylang constructor.
     * @param modX $modx
     * @param array $config
     */
    function __construct(modX &$modx, array $config = array())
    {
        $this->modx =& $modx;
        $this->modx->lexicon->load('polylang:default', 'polylang:site');
        $corePath = $modx->getOption('polylang.core_path', $config, $modx->getOption('core_path', null, MODX_CORE_PATH) . 'components/polylang/');
        $assetsUrl = $modx->getOption('polylang.assets_url', $config, $modx->getOption('assets_url') . 'components/polylang/');
        $assetsPath = $modx->getOption('polylang.assets_path', $config, $modx->getOption('assets_path', null, MODX_ASSETS_PATH) . 'components/polylang/');
        $this->config = array_merge(array(
            'chunksPath' => $corePath . 'elements/chunks/',
            'controllersPath' => $corePath . 'controllers/',
            'corePath' => $corePath,
            'assetsUrl' => $assetsUrl,
            'assetsPath' => $assetsPath,
            'modelPath' => $corePath . 'model/',
            'processorsPath' => $corePath . 'processors/',
            'jsUrl' => $assetsUrl . 'js/',
            'cssUrl' => $assetsUrl . 'css/',
            'templatesPath' => $corePath . 'elements/templates/',
            'connector_url' => $assetsUrl . 'connector.php',
            'actionUrl' => $assetsUrl . 'action.php',
            'handlersPath' => $corePath . 'handlers/',
            'jsonResponse' => true,
            'prepareResponse' => false,
            'cacheTime' => $this->modx->getOption('polylang_cache_time', null, 1800, true),
            'toolsHandler' => $this->modx->getOption('polylang_tools_handler_class', null, 'PolylangTools', true),
            'geoLocatorHandler' => $this->modx->getOption('polylang_geo_locator_handler_class', null, 'PolylangGeoLocator', true),
            'crawlerDetectorHandler' => $this->modx->getOption('polylang_crawlerdetector_handler_class',null, 'PolylangUserAgentCrawlerDetector', true),
            'dbHelperHandler' => $this->modx->getOption('polylang_dbhelper_handler_class', null, 'PolylangDbHelper', true),
        ), $config);
        $this->modx->addPackage('polylang', $this->config['modelPath']);
    }

    /**
     * @return string
     */
    public function getNamespace()
    {
        return $this->namespace;
    }

    /**
     * Shorthand for the call of processor
     *
     * @access public
     *
     * @param string $action Path to processor
     * @param array $data Data to be transmitted to the processor
     *
     * @return mixed The result of the processor
     */
    public function runProcessor($action, $data = array())
    {
        if (empty($action)) {
            return $this->modx->error->failure();
        }
        if ($this->modx->context->get('key') !== 'mgr') {
            $action = 'web/' . $action;
        }
        $this->modx->error->reset();
        $processorsPath = !empty($this->config['processorsPath'])
            ? $this->config['processorsPath']
            : MODX_CORE_PATH . 'components/polylang/processors/';

        $response = $this->modx->runProcessor($action, $data, array(
            'processors_path' => $processorsPath,
        ));

        return $this->config['prepareResponse'] ? $this->prepareResponse($response) : $response;

    }

    /**
     * This method returns prepared response
     *
     * @param mixed $response
     *
     * @return array|string $response
     */
    public function prepareResponse($response)
    {
        if ($response instanceof modProcessorResponse) {
            $output = $response->getResponse();
        } else {
            $message = $response;
            if (empty($message)) {
                $message = $this->modx->lexicon('err_unknown');
            }
            $output = $this->failure($message);
        }
        if ($this->config['jsonResponse'] and is_array($output)) {
            $output = $this->modx->toJSON($output);
        } else if (!$this->config['jsonResponse'] and !is_array($output)) {
            $output = $this->modx->fromJSON($output);
        }
        return $output;
    }


    /**
     * @param string $message
     * @param array $data
     * @param array $placeholders
     *
     * @return array|string
     */
    public function failure($message = '', $data = array(), $placeholders = array())
    {
        $response = array(
            'success' => false,
            'message' => $message,
            'data' => $data,
        );

        return $this->config['jsonResponse'] ? $this->modx->toJSON($response) : $response;
    }

    /**
     * @param string $message
     * @param array $data
     * @param array $placeholders
     *
     * @return array|string
     */
    public function success($message = '', $data = array(), $placeholders = array())
    {
        $response = array(
            'success' => true,
            'message' => $message,
            'data' => $data,
        );

        return $this->config['jsonResponse'] ? $this->modx->toJSON($response) : $response;
    }

    /**
     * @param array $config
     *
     * @return PolylangTools
     */
    public function getTools(array $config = array())
    {
        if (!is_object($this->tools) || !($this->tools instanceof PolylangTools)) {
            $toolsClass = $this->modx->loadClass('tools.' . $this->config['toolsHandler'], $this->config['handlersPath'], true, true);
            if ($toolsClass) {
                $config = array_merge($this->config, $config);
                $this->tools = new $toolsClass($this, $config);
            }
        }
        return $this->tools;
    }

    /**
     * @param array $config
     *
     * @return PolylangGeoLocatorInterface
     */
    public function getGeoLocator(array $config = array())
    {
        if (!class_exists('PolylangGeoLocatorHandler')) {
            require_once dirname(__FILE__, 3) . '/handlers/geo/polylanggeolocatorhandler.class.php';
        }

        if (!is_object($this->geoLocator) || !($this->geoLocator instanceof PolylangGeoLocatorInterface)) {
            $geoLocatorClass = $this->modx->loadClass('geo.' . $this->config['geoLocatorHandler'], $this->config['handlersPath'], true, true);
            if ($geoLocatorClass) {
                $config = array_merge($this->config, $config);
                $this->geoLocator = new $geoLocatorClass($this, $config);
            }
        }
        return $this->geoLocator;
    }

    /**
     * @param array $config
     *
     * @return PolylangTranslatorHandler
     */
    public function getTranslator(array $config = array())
    {
        if (!class_exists('PolylangTranslatorHandler')) {
            require_once dirname(__FILE__, 3) . '/handlers/translator/polylangtranslatorhandler.class.php';
        }

        if (!is_object($this->translator) || !($this->translator instanceof PolylangTranslatorInterface)) {
            $config = array_merge($this->config, $config);
            $translatorHandler = $this->modx->getOption('polylang_class_translator', $config, 'PolylangTranslatorGoogle', true);
            $translatorClass = $this->modx->loadClass('translator.' . $translatorHandler, $this->config['handlersPath'], true, true);
            if ($translatorClass) {
                $this->translator = new $translatorClass($this, $config);
            }
        }
        return $this->translator;
    }

    /**
     * @param array $config
     *
     * @return null|PolylangCrawlerDetector
     */
    public function getCrawlerDetector(array $config = array())
    {
        if (!($this->crawlerDetector instanceof PolylangCrawlerDetector)) {
            $crawlerDetectorClass = $this->modx->loadClass('crawler.' . $this->config['crawlerDetectorHandler'], $this->config['handlersPath'], true, true);
            if ($crawlerDetectorClass) {
                $config = array_merge($this->config, $config);
                $this->crawlerDetector = new $crawlerDetectorClass($this, $config);
            }
        }
        return $this->crawlerDetector ?: null;
    }

    /**
     * @param int $template
     *
     * @return bool
     */
    public function isWorkingTemplates($template)
    {
        $templates = $this->getTools()->explodeAndClean($this->modx->getOption('polylang_working_templates', null, ''));
        return empty($templates) || in_array($template, $templates);
    }

    /**
     * @param int $rid
     *
     * @param modManagerController $controller
     */
    public function loadControllerJsCss($rid, modManagerController &$controller)
    {
        $controller->addLexiconTopic('polylang:default');
        $defaultLanguage = $this->modx->getOption('cultureKey');
        $localizationSettings = $this->modx->getOption('polylang_localization_settings');
        $localizationSettings = $this->getTools()->fromJSON($localizationSettings, array());
        $config = array_merge($this->config, array(
            'rid' => $rid,
            'totalLanguages' => count($this->getTools()->getLanguageKeys()) - 1,
            'contentLanguages' => count($this->getTools()->getResourceLanguageKeys($rid, false, false)),
            'showTranslateBtn' => $this->modx->getOption('polylang_show_translate_btn'),
            'disallowTranslationCompletedField' => $this->modx->getOption('polylang_disallow_translation_completed_field'),
            'defaultLanguage' => $this->modx->getOption('polylang_default_language', null, $defaultLanguage, true),
            'useResourceEditorStatus' => $this->modx->getOption('polylang_use_resource_editor_status', null, 1, true),
            'editorHeight' => $this->modx->getOption('polylang_editor_height', null, 300, true),
            'localization' => $localizationSettings,
        ));
        $controller->addHtml("<script type='text/javascript'>  Polylang.config = {$this->modx->toJSON($config)}</script>");
        $controller->addJavascript($this->config['jsUrl'] . 'mgr/polylang.js');
        $controller->addJavascript($this->config['jsUrl'] . 'mgr/misc/combo.js');
        $controller->addJavascript($this->config['jsUrl'] . 'mgr/misc/strftime-min-1.3.js');
        $controller->addJavascript($this->config['jsUrl'] . 'mgr/misc/default.grid.js');
        $controller->addJavascript($this->config['jsUrl'] . 'mgr/misc/default.window.js');
        $controller->addJavascript($this->config['jsUrl'] . 'mgr/polylangcontent/polylangcontent.grid.js');
        $controller->addJavascript($this->config['jsUrl'] . 'mgr/inject/inject.js');

        $controller->addCss($this->config['cssUrl'] . 'mgr/main.css');
        $controller->addCss($this->config['cssUrl'] . 'mgr/bootstrap.buttons.css');
        $controller->addCss($this->config['assetsUrl'] . 'vendor/fontawesome/css/font-awesome.min.css');

    }

    /**
     * @param modManagerController $controller
     */
    public function loadControllerTVJsCss(modManagerController &$controller)
    {
        $controller->addLexiconTopic('polylang:default');
        $controller->addJavascript($this->config['jsUrl'] . 'mgr/inject/inject.tv.js');
    }

    /**
     * @param modManagerController $controller
     */
    public function loadControllerMs2OptionJsCss(modManagerController &$controller)
    {
        $controller->addLexiconTopic('polylang:default');
        $controller->addJavascript($this->config['jsUrl'] . 'mgr/inject/inject.option.js');
    }


    public function extendTvModel()
    {
        $this->extendModel('tv.mysql.inc.php');
    }

    public function extendMs2OptionModel()
    {
        if ($this->getTools()->hasAddition('minishop2')) {
            $this->extendModel('option.mysql.inc.php');
        }
    }

    public function extendPolylangContent()
    {
        $this->extendModel('polylangcontent.map.inc.php');
    }

    public function extendModel($filename)
    {
        $file = $this->config['modelPath'] . 'extend/' . $filename;
        if(file_exists($file)) {
            $include = include_once($file);
            if (is_array($include)) {
                foreach ($include as $class => $map) {
                    if (!isset($this->modx->map[$class])) {
                        $this->modx->loadClass($class);
                    }
                    if (isset($this->modx->map[$class])) {
                        foreach ($map as $key => $values) {
                            $this->modx->map[$class][$key] = array_merge($this->modx->map[$class][$key], $values);
                        }
                    }
                }
            }
        }
    }

    /**
     * @return array
     */
    public function collectRequestParameters(): array
    {
        $params = array();
        $filehandle = fopen('php://input', "r");
        $contentType = isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : '';
        $spPos = strpos($contentType, ';');

        if ($spPos !== false) {
            $contentType = substr($contentType, 0, $spPos);
        }

        switch ($contentType) {
            case 'image/jpeg':
            case 'image/png':
            case 'image/gif':
                $params['filehandle'] = $filehandle;
                break;
            case 'application/xml':
            case 'text/xml':
                $data = stream_get_contents($filehandle);
                fclose($filehandle);
                if (LIBXML_VERSION < 20900) {
                    $disableEntities = libxml_disable_entity_loader(true);
                    $xml = simplexml_load_string($data);
                    libxml_disable_entity_loader($disableEntities);
                } else {
                    $xml = simplexml_load_string($data);
                }
                $params = $this->getTools()->xml2array($xml);
                break;
            case 'application/json':
            case 'text/json':
                $data = stream_get_contents($filehandle);
                fclose($filehandle);
                $params = $this->getTools()->fromJSON($data);
                $params = (!is_array($params)) ? array() : $params;
                break;
            case 'application/x-www-form-urlencoded':
            default:
                $data = stream_get_contents($filehandle);
                fclose($filehandle);
                parse_str($data, $params);
                break;
        }

        return $params;
    }


}