<?php
/**
 * GSManager
 *
 * This is a mighty and platform independent software for administrating game servers of various kinds.
 * If you need help with installing or using this software, please visit our website at: www.gsmanager.de
 * If you have licensing enquiries e.g. related to commercial use, please contact us at: sales@gsmanager.de
 *
 * @copyright Greenfield Concept UG (haftungsbeschränkt)
 * @license GSManager EULA <https://www.gsmanager.de/eula.php>
 * @version 1.2.1
**/

namespace GSM\Plugins\Censor;

use GSM\Daemon\Core\Utils;

/**
 * Censor Plugin
 *
 * This plugin adds filters for badnames and badwordes.
 *
 * @author Mirko911 <mirko911@gsmanager.de>
 */
class Censor extends Utils {
    /**
     * Bad word list
     *
     * Contains a list of plain bad words and regex filters for bad words
     *
     * @var string[][] Array parts are <strong>normal</strong> and <strong>regexp</strong>
     */
    private $bad_word_list = array();
    /**
     * Bad name list
     *
     * Contains a list of plain bad words and regex filters for bad words
     *
     * @var string[][] Array parts are <strong>normal</strong> and <strong>regexp</strong>
     */
    private $bad_name_list = array();

    /**
     * Inits the plugin
     *
     * This function initiates the plugin. This means that it register commands
     * default values, and events. It's important that every plugin has this function
     * Otherwise the plugin exists but can't be used
     */
    public function initPlugin() {
        parent::initPlugin();

        $this->config->setDefault('censor', 'badword', true);
        $this->config->setDefault('censor', 'badwords', array());
        $this->config->setDefault('censor', 'badname', false);
        $this->config->setDefault('censor', 'badnames', array());
        $this->config->setDefault('censor', 'uppercasename', false);
        $this->config->setDefault('censor', 'uppercasenamescore', 5);
        $this->config->setDefault('censor', 'uppercaseword', false);
        $this->config->setDefault('censor', 'uppercasewordscore', 5);
    }

    /**
     * Function to enable this plugin
     */
    public function enable() {
        parent::enable();

        $this->events->register("playerSay", [$this, "playerSay"]);
        $this->events->register("playerJoined", [$this, "playerJoined"]);
        $this->events->register("playerNameChange", [$this, "playerNameChange"]);
        $this->events->register("parseConfig", [$this, "parseConfig"]);

        $this->parseConfig();
    }

    /**
     * Function to disable this plugin
     */
    public function disable() {
        parent::disable();

        $this->events->unregister("playerSay", [$this, "playerSay"]);
        $this->events->unregister("playerJoined", [$this, "playerJoined"]);
        $this->events->unregister("playerNameChange", [$this, "playerNameChange"]);
        $this->events->unregister("parseConfig", [$this, "parseConfig"]);
    }

    /**
     * This function will be executed, when a player says something.
     *
     * This functions check if a player used a bad word.
     * You can define normal world filters and regex filters.
     * It's also possible to punish people for uppercase Words
     *
     * @param string $guid       Playerguid
     * @param string $message    Message
     * @param bool   $executed   Interpreted as Command?
     * @return false on fail
     */
    public function playerSay($guid, $message, $executed) {

        /* Return if this was a command */
        if ($executed) {
            return;
        }

        /* Remove color codes from message to check plain text message */
        $message = $this->mod->removecolor($message);

        $badword_found = false;
        $uppercase_found = false;

        $multi = 1;
        /* match with badword list */
        if ($this->config->get("censor", "badword")) {

            foreach ($this->bad_word_list["normal"] as $value) {
                if (mb_stripos($message, $value[0]) !== false) {
                    $badword_found = true;
                    $badword = $value[0];
                    $multi = $value[1];
                    break;
                }
            }

            /* Match regex checks */
            if (!$badword_found) {
                foreach ($this->bad_word_list["regexp"] as $value) {
                    if (preg_match("@" . $value[0] . "@im", $message, $subpatterns)) {
                        $badword_found = true;
                        $badword = $subpatterns[0];
                        $multi = $value[1];
                        break;
                    }
                }
            }
        }


        /* Match uppercase Words */
        if ($this->config->get("censor", "uppercaseword") && !$badword_found && preg_match('|^[^a-z0-9]+$|', $message)) {
            $uppercase_found = true;
            $multi = $this->config->get("censor", "uppercasewordscore");
        }

        if ($badword_found) {
            $reason = $this->language->get("censor.badword", array("<WORD>"), array($badword));
            $this->punishment->doPunishment($guid, $multi, $reason, 'censor.immunity.badword');
        } elseif ($uppercase_found) {
            $reason = $this->language->get("censor.uppercaseword");
            $this->punishment->doPunishment($guid, $multi, $reason, 'censor.immunity.uppercaseword');
        }
    }

    /**
     * This function will be executed, when a player joins.
     *
     * This functions check if a player used a bad name.
     * You can define normal world filters and regex filters.
     * It's also possible to punish people for uppercase Names
     *
     * @param string $guid Players Guid
     * @return false on fail
     */
    public function playerJoined($guid) {

        /* Remove color codes from message to check plain text message */
        $name = $this->mod->removecolor($this->players[$guid]->getName());

        $badname_found = false;
        $uppercase_found = false;
        $badname = false;

        /* match with badword list */
        if ($this->config->get("censor", "badname")) {

            foreach ($this->bad_name_list["normal"] as $value) {
                if (mb_stripos($name, $value[0]) !== false) {
                    $badname_found = true;
                    $badname = $value[0];
                    $multi = $value[1];
                    break;
                }
            }
            /* Match regex checks */
            if (!$badname_found) {
                foreach ($this->bad_name_list["regexp"] as $value) {
                    if (preg_match("@" . $value[0] . "@im", $name, $subpatterns)) {
                        $badname_found = true;
                        $badname = $subpatterns[0];
                        $multi = $value[1];
                        break;
                    }
                }
            }
        }

        /* Match uppercase Names */
        if ($this->config->get("censor", "uppercasename") && !$badname_found && preg_match('|^[^a-z0-9]+$|', $name)) {
            $uppercase_found = true;
            $multi = $this->config->get('censor', 'uppercasenamescore');
        }

        if ($badname_found) {
            $reason = $this->language->get("censor.badname", array("<WORD>"), array($badname));
            $this->punishment->doPunishment($guid, $multi, $reason, 'censor.immunity.badname');
        } elseif ($uppercase_found) {
            $reason = $this->language->get("censor.uppercasename");
            $this->punishment->doPunishment($guid, $multi, $reason, 'censor.immunity.uppercasename');
        }
    }

    /**
     * This function is called, when a player changes his name ingame
     *
     * This function passed the guid of the player to the playerJoined function to check
     * if the new name is listed as bad word.
     * <strong>Notice: this is only for quake games</strong>
     *
     * @param $guid
     * @param $oldname
     * @param $name
     */
    public function playerNameChange($guid, $oldname, $name) {
        $this->playerJoined($guid);
    }

    /**
     * Execute the event readConfig.
     *
     * Reads in the badword and the badname list
     *
     */
    public function parseConfig() {
        $list = $this->config->get("censor", "badwords");

        $normal = array();
        $regexp = array();
        foreach ($list as $badword => $multi) {
            /* Remove whitespaces */
            $badword = trim($badword);

            /* Decide to move filter to regex filter or to normal filter */
            if (mb_stripos($badword, 'regexp:') === 0) {
                $regexp[] = array(mb_substr($badword, 7), $multi);
            } else {
                $normal[] = array($badword, $multi);
            }
        }
        $this->bad_word_list = array("normal" => $normal, "regexp" => $regexp);

        $list = $this->config->get("censor", "badnames");

        $normal = array();
        $regexp = array();
        foreach ($list as $badname => $multi) {

            /* Remove whitespaces */
            $badname = trim($badname);

            /* Decide to move filter to regex filter or to normal filter */
            if (mb_stripos($badname, 'regexp:') === 0) {
                $regexp[] = array(mb_substr($badname, 7), $multi);
            } else {
                $normal[] = array($badname, $multi);
            }
        }
        $this->bad_name_list = array("normal" => $normal, "regexp" => $regexp);
    }
}
