<?php
/*
 * ----------------------------------------------------------------------
 *
 *                          Borlabs Cookie
 *                      developed by Borlabs
 *
 * ----------------------------------------------------------------------
 *
 * Copyright 2018 Borlabs - Benjamin A. Bornschein. All rights reserved.
 * This file may not be redistributed in whole or significant part.
 * Content of this file is protected by international copyright laws.
 *
 * ----------------- Borlabs Cookie IS NOT FREE SOFTWARE -----------------
 *
 * @copyright Borlabs - Benjamin A. Bornschein, https://borlabs.io
 * @author Benjamin A. Bornschein, Borlabs ben@borlabs.io
 *
 */

namespace BorlabsCookie\Cookie\Backend;

use BorlabsCookie\Cookie\Config;
use BorlabsCookie\Cookie\Multilanguage;
use BorlabsCookie\Cookie\Frontend\BlockedContentTypes\Fallback;

class BlockedContentTypes
{
    private static $instance;

    private $imagePath;
    private $defaultBlockedContentTypes = [
        'facebook'=>'Facebook',
        'default'=>'Fallback', // Default
        'googlemaps'=>'GoogleMaps',
        'instagram'=>'Instagram',
        'twitter'=>'Twitter',
        'vimeo'=>'Vimeo',
        'youtube'=>'YouTube',
    ];

    public static function getInstance()
    {
        if (null === self::$instance) {
            self::$instance = new self;
        }

        return self::$instance;
    }

    private function __clone()
    {
    }

    private function __wakeup()
    {
    }

    protected function __construct()
    {
        $this->imagePath = plugins_url('images', realpath(__DIR__.'/../../'));
    }

    /**
     * displayTabBlockedContentTypes function.
     *
     * @access public
     * @return void
     */
    public function displayTabBlockedContentTypes()
    {
        global $wpdb;

        // Ensure that all BCT are present
        $this->checkAndRestoreDefaults();

        $id = 0;

        // Save exception list
        if (!empty($_POST['formSendExceptionList']) && check_admin_referer('borlabs_cookie_blocked_content_type')) {

            $this->saveExceptionList($_POST);

            Backend::getInstance()->addMessage(_x('Saved successfully.', 'Status message', 'borlabs-cookie'), 'success');
        }

        // Validate
        if (!empty($_POST['formSend']) && check_admin_referer('borlabs_cookie_edit_blocked_content_type')) {

            $errorStatus = $this->validate($_POST);

            // Save
            if ($errorStatus === false) {

                $id = $this->save($_POST);

                Backend::getInstance()->addMessage(_x('Saved successfully.', 'Status message', 'borlabs-cookie'), 'success');
            }
        }

        // Reset default BCT
        if (!empty($_POST['resetDefaultBCT']) && check_admin_referer('borlabs_cookie_reset_default_blocked_content_types')) {

            $this->resetDefaults();

            Backend::getInstance()->addMessage(_x('Default Blocked Content Types successfully reset.', 'Status message', 'borlabs-cookie'), 'success');
        }

        // Change Status BCT
        if (!empty($_GET['changeStatusId']) && isset($_GET['changeStatus']) && !empty($_GET['_wpnonce'])) {
            if (wp_verify_nonce($_GET['_wpnonce'], 'changeStatus_'.intval($_GET['changeStatusId']))) {

                // Change Status BCT
                $this->changeStatus($_GET['changeStatusId'], $_GET['changeStatus']);

                Backend::getInstance()->addMessage(_x('Changed status successfully.', 'Status message', 'borlabs-cookie'), 'success');
            } else {
                Backend::getInstance()->addMessage(_x('Could not change status of Blocked Content Type.', 'Status message', 'borlabs-cookie'), 'error');
            }
        }

        // Delete BCT
        if (!empty($_GET['deleteId']) && !empty($_GET['_wpnonce'])) {
            if (wp_verify_nonce($_GET['_wpnonce'], 'delete_'.intval($_GET['deleteId']))) {

                // Delete BCT
                $this->delete($_GET['deleteId']);

                Backend::getInstance()->addMessage(_x('Deleted successfully.', 'Status message', 'borlabs-cookie'), 'success');
            } else {
                Backend::getInstance()->addMessage(_x('Could not delete Blocked Content Type.', 'Status message', 'borlabs-cookie'), 'error');
            }
        }

        // Add or edit BCT
        if (!empty($_GET['editId']) || !empty($_GET['new']) || !empty($_POST['id'])) {

            if (empty($id)) {
                if (!empty($_GET['editId'])) {
                    $id = $_GET['editId'];
                } elseif (!empty($_POST['id'])) {
                    $id = $_POST['id'];
                }
            }

            $this->displayEditMask($id, $_POST);
        } else {
            $this->displayOverview();
        }
    }

    /**
     * displayOverview function.
     *
     * @access public
     * @return void
     */
    public function displayOverview()
    {
        global $wpdb;

        $tableName = $wpdb->prefix.'borlabs_cookie_blocked_content_types';

        // Get all blocked content types for the current language
        $blockedContentTypes = $wpdb->get_results('
            SELECT
                `id`,
                `type_id`,
                `name`,
                `hosts`,
                `status`,
                `undeletable`
            FROM
                `'.$tableName.'`
            WHERE
                `language`="'.esc_sql(Multilanguage::getInstance()->getCurrentLanguageCode()).'"
            ORDER BY
                `name` ASC
        ');

        if (!empty($blockedContentTypes)) {
            foreach ($blockedContentTypes as $key => $bctData) {
                $blockedContentTypes[$key]->hosts = esc_html(implode(', ', unserialize($bctData->hosts)));
                $blockedContentTypes[$key]->undeletable = intval($bctData->undeletable);
            }
        }

        $textareaBCTHostExceptions = esc_textarea(!empty(Config::getInstance()->get('bctHostExceptions')) ? implode("\n", Config::getInstance()->get('bctHostExceptions')) : '');

        include Backend::getInstance()->templatePath.'/settings-blocked-content-types.html';
    }

    /**
     * displayEditMask function.
     *
     * @access public
     * @param int $id (default: 0)
     * @param mixed $formData (default: [])
     * @return void
     */
    public function displayEditMask($id = 0, $formData = [])
    {
        $bctData = new \stdClass();

        // Load settings
        if (!empty($id)) {
            $bctData = $this->get($id);

            // Check if the language was switched during editing
            if ($bctData->language !== Multilanguage::getInstance()->getCurrentLanguageCode()) {

                // Try to get the id for the switched language
                $previousTypeId = $bctData->type_id;
                $bctData = $this->getByTypeId($bctData->type_id);

                // If not found
                if (empty($bctData->id)) {
                    Backend::getInstance()->addMessage(_x('The selected Blocked Content Type is not available in the current language.', 'Status message', 'borlabs-cookie'), 'notice');

                    $bctData = new \stdClass;
                    $bctData->type_id = $previousTypeId;
                }
            }
        }

        // Re-insert data
        $reinsertData = [
            'type_id'=>'',
            'name'=>'',
            'hosts'=>'',
            'preview_html'=>'',
            'global_js'=>'',
            'init_js'=>'',
            'status'=>'',
            'settings'=>[],
        ];

        $reinsertData['type_id']        = !empty($formData['typeId']) ? stripslashes($formData['typeId']) : '';
        $reinsertData['name']           = !empty($formData['name']) ? stripslashes($formData['name']) : '';
        $reinsertData['hosts']          = !empty($formData['hosts']) ? stripslashes($formData['hosts']) : '';
        $reinsertData['preview_html']   = !empty($formData['previewHTML']) ? stripslashes($formData['previewHTML']) : '';
        $reinsertData['global_js']      = !empty($formData['globalJS']) ? stripslashes($formData['globalJS']) : '';
        $reinsertData['init_js']        = !empty($formData['initJS']) ? stripslashes($formData['initJS']) : '';
        $reinsertData['status']         = isset($formData['status']) ? $formData['status'] : null;
        $reinsertData['settings']       = isset($formData['settings']) ? $formData['settings'] : [];

        // The id can not be changed after the BCT was created!
        $inputId             = intval(!empty($bctData->id) ? $bctData->id : 0);
        $checkboxStatus      = (!empty($bctData->status) && !isset($reinsertData['status'])) || isset($reinsertData['status'])   ? ' checked' : '';
        $inputTypeId         = esc_html(!empty($bctData->type_id)                                                    ? $bctData->type_id             : $reinsertData['type_id']);
        $inputName           = esc_html(!empty($bctData->name) && empty($reinsertData['name'])                       ? $bctData->name                : $reinsertData['name']);
        $textareaHosts       = esc_textarea(!empty($bctData->hosts) && empty($reinsertData['hosts'])                 ? implode("\n", $bctData->hosts): $reinsertData['hosts']);

        if (!empty($bctData->preview_html) && empty($reinsertData['preview_html'])) {
            $textareaPreviewHTML = esc_textarea($bctData->preview_html);
        } else {
            if (!empty($reinsertData['preview_html'])) {
                $textareaPreviewHTML = esc_textarea($reinsertData['preview_html']);
            } else {
                $textareaPreviewHTML = esc_textarea(Fallback::getInstance()->getDefault()['previewHTML']);
            }
        }

        $textareaGlobalJS    = esc_textarea(!empty($bctData->global_js) && empty($reinsertData['global_js'])         ? $bctData->global_js           : $reinsertData['global_js']);
        $textareaInitJS      = esc_textarea(!empty($bctData->init_js) && empty($reinsertData['init_js'])             ? $bctData->init_js             : $reinsertData['init_js']);
        $checkboxSettingsUnblockAll  = (!empty($bctData->settings['unblockAll']) && !isset($reinsertData['settings']['unblockAll'])) || isset($reinsertData['settings']['unblockAll'])   ? ' checked' : '';

        include Backend::getInstance()->templatePath.'/settings-blocked-content-type-edit.html';
    }

    /**
     * saveExceptionList function.
     *
     * @access public
     * @param mixed $formData
     * @return void
     */
    public function saveExceptionList ($formData)
    {
        $updatedConfig = Config::getInstance()->get();

        // Clean hosts
        $hosts = explode("\n", $formData['bctHostExceptions']);

        foreach ($hosts as $key => $host) {
            $host = trim($host);

            if (!empty($host)) {
                $hosts[$key] = stripslashes($host);
            } else {
                unset($hosts[$key]);
            }
        }

        $updatedConfig['bctHostExceptions']  = $hosts;

        // Save config
        Config::getInstance()->saveConfig($updatedConfig);
    }

    /**
     * validate function.
     *
     * @access public
     * @param mixed $formData
     * @return void
     */
    public function validate($formData)
    {
        $errorStatus = false;

        // Check typeId if a new BCT is about to be added
        if (empty($formData['id'])) {
            if (empty($formData['typeId']) || preg_match('/^[a-z]{3,}$/', $formData['typeId']) === 0) {

                $errorStatus = true;
                Backend::getInstance()->addMessage(_x('Please fill out the field <strong>ID</strong>. The id has to be minimum 3 letters and only contains letters from a-z.', 'Status message', 'borlabs-cookie'), 'error');

            } elseif ($this->checkIdExists($formData['typeId'])) {

                // Check if typeId already exists
                $errorStatus = true;
                Backend::getInstance()->addMessage(_x('The <strong>ID</strong> already exists.', 'Status message', 'borlabs-cookie'), 'error');

            } elseif (in_array($formData['typeId'], ['all', 'cookie', 'thirdparty', 'firstparty'])) {

                // Check if typeId already exists
                $errorStatus = true;
                Backend::getInstance()->addMessage(_x('Please change the name of the <strong>ID</strong>. Your selected name for <strong>ID</strong> is reserved and can not be used.', 'Status message', 'borlabs-cookie'), 'error');

            }
        }

        if (empty($formData['name'])) {
            $errorStatus = true;

            Backend::getInstance()->addMessage(_x('Please fill out the field <strong>Name</strong>.', 'Status message', 'borlabs-cookie'), 'error');
        }

        $errorStatus = apply_filters('borlabsCookie/bct/validate', $errorStatus, $formData);

        return $errorStatus;
    }

    /**
     * save function.
     *
     * @access public
     * @param mixed $formData
     * @return void
     */
    public function save($formData)
    {
        $formData = apply_filters('borlabsCookie/bct/save', $formData);

        // Clean hosts
        $hosts = explode("\n", $formData['hosts']);

        foreach ($hosts as $key => $host) {
            $host = trim($host);

            if (!empty($host)) {
                $hosts[$key] = stripslashes($host);
            } else {
                unset($hosts[$key]);
            }
        }

        $settings = [];

        if (!empty($formData['settings']) && is_array($formData['settings'])) {
            $settings = $formData['settings'];
        }

        // Check if previewHTML is empty
        if (empty($formData['previewHTML'])) {

            // If it is a default Blocked Content Type we load its default previewHTML
            if (!empty($formData['id'])) {
                $bctData = $this->get($formData['id']);

                if (!empty($bctData->type_id) && !empty($this->defaultBlockedContentTypes[$bctData->type_id])) {
                    $className = '\BorlabsCookie\Cookie\Frontend\BlockedContentTypes\\'.$this->defaultBlockedContentTypes[$bctData->type_id];
                    $defaultBCTData = $className::getInstance()->getDefault();

                    $formData['previewHTML'] = $defaultBCTData['previewHTML'];
                }
            }

            if (empty($formData['previewHTML'])) {
                $formData['previewHTML'] = Fallback::getInstance()->getDefault()['previewHTML'];
            }
        }

        $id = 0;

        if (!empty($formData['id'])) {
            // Edit
            $id = $this->modify(
                $formData['id'],
                $formData['name'],
                $hosts,
                $formData['previewHTML'],
                $formData['globalJS'],
                $formData['initJS'],
                $settings,
                (isset($formData['status']) ? true : false)
            );
        } else {
            // Add
            $id = $this->add(
                $formData['typeId'],
                $formData['name'],
                '',
                $hosts,
                $formData['previewHTML'],
                $formData['globalJS'],
                $formData['initJS'],
                $settings,
                (isset($formData['status']) ? true : false)
            );
        }

        return $id;
    }

    /**
     * changeStatus function.
     *
     * @access public
     * @param mixed $id
     * @param mixed $status
     * @return void
     */
    public function changeStatus($id, $status)
    {
        global $wpdb;

        $tableName = $wpdb->prefix.'borlabs_cookie_blocked_content_types';

        $wpdb->query('
            UPDATE
                `'.$tableName.'`
            SET
                `status`="'.intval($status).'"
            WHERE
                `id`="'.intval($id).'"
        ');

        return true;
    }

    /**
     * delete function.
     *
     * @access public
     * @param mixed $id
     * @return void
     */
    public function delete($id)
    {
        global $wpdb;

        $tableName = $wpdb->prefix.'borlabs_cookie_blocked_content_types';

        $wpdb->query('
            DELETE FROM
                `'.$tableName.'`
            WHERE
                `id`="'.intval($id).'"
        ');

        return true;
    }

    /**
     * addBlockedContentType function.
     *
     * @access public
     * @param string $typeId
     * @param string $name
     * @param string $description
     * @param array $hosts
     * @param string $previewHTML
     * @param string $globalJS
     * @param string $initJS
     * @param array $settings (default: [])
     * @param bool $status (default: false)
     * @param bool $undeleteable (default: false)
     * @param mixed $language
     * @return void
     */
    public function add($typeId, $name, $description = '', $hosts, $previewHTML, $globalJS = '', $initJS = '', $settings = [], $status = false, $undeleteable = false, $language = null)
    {
        global $wpdb;

        if (empty($language)) {
            $language = Multilanguage::getInstance()->getCurrentLanguageCode();
        }

        if ($this->checkIdExists($typeId, $language) === false) {

            $tableName = $wpdb->prefix.'borlabs_cookie_blocked_content_types';

            $wpdb->query('
                INSERT INTO
                    `'.$tableName.'`
                    (
                        `type_id`,
                        `language`,
                        `name`,
                        `description`,
                        `hosts`,
                        `preview_html`,
                        `global_js`,
                        `init_js`,
                        `settings`,
                        `status`,
                        `undeletable`
                    )
                VALUES
                    (
                        "'.esc_sql($typeId).'",
                        "'.esc_sql($language).'",
                        "'.esc_sql(stripslashes($name)).'",
                        "'.esc_sql(stripslashes($description)).'",
                        "'.esc_sql(serialize($hosts)).'",
                        "'.esc_sql(stripslashes($previewHTML)).'",
                        "'.esc_sql(stripslashes($globalJS)).'",
                        "'.esc_sql(stripslashes($initJS)).'",
                        "'.esc_sql(serialize($settings)).'",
                        "'.($status ? true : false).'",
                        "'.($undeleteable ? true : false).'"
                    )
            ');

            if (!empty($wpdb->insert_id)) {
                return $wpdb->insert_id;
            }
        }

        return false;
    }

    /**
     * modify function.
     *
     * @access public
     * @param mixed $id
     * @param mixed $name
     * @param mixed $hosts
     * @param mixed $previewHTML
     * @param string $globalJS (default: '')
     * @param string $initJS (default: '')
     * @param mixed $settings (default: [])
     * @param mixed $status (default: false)
     * @return void
     */
    public function modify($id, $name, $hosts, $previewHTML, $globalJS = '', $initJS = '', $settings = [], $status = false)
    {
        global $wpdb;

        $tableName = $wpdb->prefix.'borlabs_cookie_blocked_content_types';

        $wpdb->query('
            UPDATE
                `'.$tableName.'`
            SET
                `name`="'.esc_sql(stripslashes($name)).'",
                `hosts`="'.esc_sql(serialize($hosts)).'",
                `preview_html`="'.esc_sql(stripslashes($previewHTML)).'",
                `global_js`="'.esc_sql(stripslashes($globalJS)).'",
                `init_js`="'.esc_sql(stripslashes($initJS)).'",
                `settings`="'.esc_sql(serialize($settings)).'",
                `status`="'.($status ? true : false).'"
            WHERE
                `id`="'.intval($id).'"
        ');

        return $id;
    }

    /**
     * get function.
     *
     * @access public
     * @param mixed $id
     * @return void
     */
    public function get($id)
    {
        global $wpdb;

        $data = false;

        $tableName = $wpdb->prefix.'borlabs_cookie_blocked_content_types';

        $bctData = $wpdb->get_results('
            SELECT
                `id`,
                `type_id`,
                `language`,
                `name`,
                `description`,
                `hosts`,
                `preview_html`,
                `global_js`,
                `init_js`,
                `settings`,
                `status`
            FROM
                `'.$tableName.'`
            WHERE
                `id`="'.esc_sql($id).'"
        ');

        if (!empty($bctData[0]->id)) {
            $data = $bctData[0];

            $data->hosts = unserialize($data->hosts);
            $data->settings = unserialize($data->settings);

            $data->description = wp_kses(
                $data->description,
                [
                    'a'=>[],
                    'br'=>[],
                    'div'=>[],
                    'em'=>[],
                    'pre'=>[],
                    'span'=>[],
                    'strong'=>[],
                ],
                [
                    'https'
                ]
            );
        }

        return $data;
    }

    /**
     * getByTypeId function.
     *
     * @access public
     * @param mixed $typeId
     * @return void
     */
    public function getByTypeId($typeId)
    {
        global $wpdb;

        $data = false;

        $tableName = $wpdb->prefix.'borlabs_cookie_blocked_content_types';

        $language = Multilanguage::getInstance()->getCurrentLanguageCode();

        // Get all blocked content types for the current language
        $blockedContentType = $wpdb->get_results('
            SELECT
                `id`
            FROM
                `'.$tableName.'`
            WHERE
                `language`="'.esc_sql($language).'"
                AND
                `type_id`="'.esc_sql($typeId).'"
        ');

        if (!empty($blockedContentType[0]->id)) {
            $data = $this->get($blockedContentType[0]->id);
        }

        return $data;
    }

    /**
     * checkIdExists function.
     * Checks if the typeId for the current language exists
     *
     * @access public
     * @param mixed $typeId
     * @param mixed $language
     * @return void
     */
    public function checkIdExists($typeId, $language = null)
    {
        global $wpdb;

        if (empty($language)) {
            $language = Multilanguage::getInstance()->getCurrentLanguageCode();
        }

        $tableName = $wpdb->prefix.'borlabs_cookie_blocked_content_types';

        $checkId = $wpdb->get_results('
            SELECT
                `type_id`
            FROM
                `'.$tableName.'`
            WHERE
                `type_id`="'.esc_sql($typeId).'"
                AND
                `language`="'.esc_sql($language).'"
        ');

        if (!empty($checkId[0]->type_id)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * checkAndRestoreDefaults function.
     *
     * @access public
     * @param mixed $language (default: null)
     * @return void
     */
    public function checkAndRestoreDefaults($language = null)
    {
        global $wpdb;

        $tableName = $wpdb->prefix.'borlabs_cookie_blocked_content_types';

        if (empty($language)) {
            $language = Multilanguage::getInstance()->getCurrentLanguageCode();
        }

        // Get all blocked content types for the current language
        $blockedContentTypes = $wpdb->get_results('
            SELECT
                `type_id`
            FROM
                `'.$tableName.'`
            WHERE
                `language`="'.esc_sql($language).'"
        ');

        $bctTypeIds = [];

        foreach ($blockedContentTypes as $bctData) {
            $bctTypeIds[$bctData->type_id] = $bctData->type_id;
        }

        // Load defaults
        foreach ($this->defaultBlockedContentTypes as $class) {
            $className = '\BorlabsCookie\Cookie\Frontend\BlockedContentTypes\\'.$class;
            $defaultBCTData = $className::getInstance()->getDefault();

            if (empty($bctTypeIds[$defaultBCTData['typeId']])) {

                // The default bct is missing and will be added again
                $this->add(
                    $defaultBCTData['typeId'],
                    $defaultBCTData['name'],
                    $defaultBCTData['description'],
                    $defaultBCTData['hosts'],
                    $defaultBCTData['previewHTML'],
                    $defaultBCTData['globalJS'],
                    $defaultBCTData['initJS'],
                    $defaultBCTData['settings'],
                    $defaultBCTData['status'],
                    $defaultBCTData['undeleteable'],
                    $language
                );
            }
        }
    }

    /**
     * resetDefaults function.
     *
     * @access public
     * @return void
     */
    public function resetDefaults()
    {
        global $wpdb;

        $tableName = $wpdb->prefix.'borlabs_cookie_blocked_content_types';

        $language = Multilanguage::getInstance()->getCurrentLanguageCode();

        foreach ($this->defaultBlockedContentTypes as $typeId => $class) {
            $blockedContentTypes = $wpdb->query('
                DELETE FROM
                    `'.$tableName.'`
                WHERE
                    `language`="'.esc_sql($language).'"
                    AND
                    `type_id`="'.esc_sql($typeId).'"
            ');
        }

        $this->checkAndRestoreDefaults();
    }
}
