<?php

/**
 * Emoticons.
 *
 * Plugin to replace emoticons in plain text message body with real emoji.
 * Also it enables emoticons in HTML compose editor. Both features are optional.
 *
 * @license GNU GPLv3+
 * @author Thomas Bruederli
 * @author Aleksander Machniak
 *
 * @website https://roundcube.net
 */
class emoticons extends rcube_plugin
{
    public $task = 'mail|settings|utils';

    /**
     * Plugin initialization.
     */
    #[\Override]
    public function init()
    {
        $rcube = rcmail::get_instance();

        $this->add_hook('message_part_after', [$this, 'message_part_after']);
        $this->add_hook('html_editor', [$this, 'html_editor']);

        if ($rcube->task == 'settings') {
            $this->add_hook('preferences_list', [$this, 'preferences_list']);
            $this->add_hook('preferences_save', [$this, 'preferences_save']);
        }
    }

    /**
     * 'message_part_after' hook handler to replace common
     * plain text emoticons with emoji
     */
    public function message_part_after($args)
    {
        if ($args['type'] == 'plain') {
            $this->load_config();

            $rcube = rcmail::get_instance();
            if (!$rcube->config->get('emoticons_display', false)) {
                return $args;
            }

            $args['body'] = self::text2icons($args['body']);
        }

        return $args;
    }

    /**
     * 'html_editor' hook handler, where we enable emoticons in TinyMCE
     */
    public function html_editor($args)
    {
        $rcube = rcmail::get_instance();

        $this->load_config();

        if ($rcube->config->get('emoticons_compose', true)) {
            $args['extra_plugins'][] = 'emoticons';
            $args['extra_buttons'][] = 'emoticons';
        }

        return $args;
    }

    /**
     * 'preferences_list' hook handler
     */
    public function preferences_list($args)
    {
        $rcube = rcmail::get_instance();
        $dont_override = $rcube->config->get('dont_override', []);

        if ($args['section'] == 'mailview' && !in_array('emoticons_display', $dont_override)) {
            $this->load_config();
            $this->add_texts('localization');

            $field_id = 'emoticons_display';
            $checkbox = new html_checkbox(['name' => '_' . $field_id, 'id' => $field_id, 'value' => 1]);

            $args['blocks']['main']['options']['emoticons_display'] = [
                'title' => html::label($field_id, $this->gettext('emoticonsdisplay')),
                'content' => $checkbox->show(intval($rcube->config->get('emoticons_display', false))),
            ];
        } elseif ($args['section'] == 'compose' && !in_array('emoticons_compose', $dont_override)) {
            $this->load_config();
            $this->add_texts('localization');

            $field_id = 'emoticons_compose';
            $checkbox = new html_checkbox(['name' => '_' . $field_id, 'id' => $field_id, 'value' => 1]);

            $args['blocks']['main']['options']['emoticons_compose'] = [
                'title' => html::label($field_id, $this->gettext('emoticonscompose')),
                'content' => $checkbox->show(intval($rcube->config->get('emoticons_compose', true))),
            ];
        }

        return $args;
    }

    /**
     * 'preferences_save' hook handler
     */
    public function preferences_save($args)
    {
        if ($args['section'] == 'mailview') {
            $args['prefs']['emoticons_display'] = (bool) rcube_utils::get_input_value('_emoticons_display', rcube_utils::INPUT_POST);
        } elseif ($args['section'] == 'compose') {
            $args['prefs']['emoticons_compose'] = (bool) rcube_utils::get_input_value('_emoticons_compose', rcube_utils::INPUT_POST);
        }

        return $args;
    }

    /**
     * Replace common plain text emoticons with emoji
     *
     * @param string $text Text
     *
     * @return string Converted text
     */
    protected static function text2icons($text)
    {
        // This is a lookbehind assertion which will exclude html entities
        // E.g. situation when ";)" in "&quot;)" shouldn't be replaced by the icon
        // It's so long because of assertion format restrictions
        $entity = '(?<!&'
            . '[a-zA-Z0-9]{2}|#[0-9]{2}|'
            . '[a-zA-Z0-9]{3}|#[0-9]{3}|'
            . '[a-zA-Z0-9]{4}|#[0-9]{4}|'
            . '[a-zA-Z0-9]{5}|'
            . '[a-zA-Z0-9]{6}|'
            . '[a-zA-Z0-9]{7}'
            . ')';

        // map of emoticon replacements
        $map = [
            '/(?<!mailto):-?D/' => self::ico_tag('1f603', ':D'), // laugh
            '/:-?\(/' => self::ico_tag('1f626', ':('), // frown
            '/' . $entity . ';-?\)/' => self::ico_tag('1f609', ';)'), // wink
            '/8-?\)/' => self::ico_tag('1f60e', '8)'), // cool
            '/(?<!mailto):-?O/i' => self::ico_tag('1f62e', ':O'), // surprised
            '/(?<!mailto):-?P/i' => self::ico_tag('1f61b', ':P'), // tongue out
            '/(?<!mailto):-?@/i' => self::ico_tag('1f631', ':-@'), // yell
            '/O:-?\)/i' => self::ico_tag('1f607', 'O:-)'), // innocent
            '/(?<!O):-?\)/' => self::ico_tag('1f60a', ':-)'), // smile
            '/(?<!mailto):-?\$/' => self::ico_tag('1f633', ':-$'), // embarrassed
            '/(?<!mailto):-?\*/i' => self::ico_tag('1f48b', ':-*'), // kiss
            '/(?<!mailto):-?S/i' => self::ico_tag('1f615', ':-S'), // undecided
        ];

        return preg_replace(array_keys($map), array_values($map), $text);
    }

    protected static function ico_tag($ico, $title)
    {
        return html::span(['title' => $title], "&#x{$ico};");
    }
}
