<?php
/**
 * DokuWiki Plugin jsongendoc (Action Component)
 *
 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
 * @author  Janez Paternoster <janez.paternoster@siol.net>
 */

// must be run within Dokuwiki
if (!defined('DOKU_INC')) {
    die();
}

//Resolve to absolute page ID
use dokuwiki\File\PageResolver;

class action_plugin_jsongendoc extends DokuWiki_Action_Plugin
{

    /**
     * Registers a callback function for a given event
     *
     * @param Doku_Event_Handler $controller DokuWiki's event controller object
     *
     * @return void
     */
    public function register(Doku_Event_Handler $controller) {
        $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handle_ajax_call_unknown');
    }


    /**
     * Handle event AJAX_CALL_UNKNOWN for json_plugin_save_inline and
     * json_plugin_archive call
     *
     * @param Doku_Event $event  event object by reference
     * @param mixed      $param  [the parameters passed as fifth argument to register_hook() when this
     *                           handler was registered]
     *
     * @return Ajax response as json
     */
    public function handle_ajax_call_unknown(Doku_Event $event, $param) {
        if ($event->data !== 'jsongendoc_plugin') {
            return;
        }

        //no other ajax call handlers needed
        $event->stopPropagation();
        $event->preventDefault();

        //access additional request variables
        global $INPUT;
        $new_page_id = cleanID($INPUT->str('name'));
        $data = json_decode($INPUT->str('data'), true);
        $parent_id = strval($data['parent_id']);
        $mime = strval($data['mime']);
        $docname_meta = strval($data['docname_meta']);
        $namespace = strval($data['namespace']);
        $template_id = cleanID($data['template']);
        $json = $data['json'];
        $new_page_exist = $template_exist = false;
        $resolver = new PageResolver($namespace.':page'); // ':page' will be stripped

        //resolve tempalate_id
        if($template_id) {
            $template_id = $resolver->resolveId($template_id);
            $template_exist = page_exists($template_id);
            $template_link = '<a href="'.wl($template_id).'">'.$template_id.'</a>';
        }
        //resolve page_id, if page will be stored locally (mime undefined)
        if(!$mime && $new_page_id) {
            $new_page_id = $resolver->resolveId($new_page_id);
            $new_page_exist = page_exists($new_page_id);
            $new_page_link = '<a href="'.wl($new_page_id).'">'.$new_page_id.'</a>';
            $new_page_id_for_template = $new_page_id;
        }
        else {
            //document will be downloaded to the client machine
            $new_page_link = $new_page_id;
            $new_page_id_for_template = $template_id;
        }

        //verify parameters, existances and rights
        $err = '';
        if(!isset($json)) {
            $err = $this->getLang('err_json');
        }
        else if($data['template'] && $template_exist === false) {
            $err = sprintf($this->getLang('err_template_not_exist'), $template_link);
        }
        else if($template_id && auth_quickaclcheck($template_id) < AUTH_READ) {
            $err = sprintf($this->getLang('permision_denied_template'), $template_id);
        }
        else if(!$mime) {
            if(!$new_page_id) {
                $err = $this->getLang('err_no_page_name');
            }
            else if($new_page_exist === true) {
                $err = sprintf($this->getLang('err_page_exists'), $new_page_link);
            }
            else if(auth_quickaclcheck($new_page_id) < AUTH_CREATE) {
                $err = sprintf($this->getLang('permision_denied_write'), $new_page_link);
            }
        }

        //load template text from user or default namespace template
        //and replace standard replacement patterns
        //https://www.dokuwiki.org/namespace_templates
        if($err === '') {
            if($template_exist === true) {
                //load user template
                $parsePageTemplate_arg = array('id'=>$new_page_id_for_template, 'tpl'=>rawWiki($template_id));
                //perform common page template replacements
                $new_page_text = parsePageTemplate($parsePageTemplate_arg);
            }
            else {
                //load default template
                $template_id = 'Namespace Template'; //for info
                $new_page_text = pageTemplate($new_page_id_for_template);
                $template_link = $this->external_link('https://www.dokuwiki.org/namespace_templates', 'Namespace Template', 'urlextern');
            }
            if(!$new_page_text) {
                $err = sprintf($this->getLang('err_empty_template'), $template_link, $new_page_link);
            }
        }

        //statistics
        $cnt = array(
            'sections shown'    => 0,
            'sections removed'  => 0,
            'scalars extracted' => 0,
            'json extracted'    => 0,
            'null values'       => 0
        );

        //helper functions from JSON plugin
        $json_o = $this->loadHelper('json');

        //display or remove sections @%$-start (filter)% ... %$end%@
        if($err === '') {
            $new_page_text = preg_replace_callback(
                '/@(\%\$-start\s*\(([^[\]{}%]*?)\)%)(.*?)%\$end\%@/s',
                function($matches) use($json_o, $json, &$cnt, &$err) {
                    if($err === '') {
                        list($match, $pattern, $filter, $contents) = array_pad($matches, 4, '');

                        $filter = $json_o->parse_filter($filter);
                        if(count($filter) > 0) {
                            if($json_o->filter($json, $filter)) {
                                $cnt['sections shown'] ++;
                                return $contents;
                            }
                            else {
                                $cnt['sections removed'] ++;
                                return '';
                            }
                        }
                        else {
                            $err = printf($this->getLang('err_tpl_section'), $template_link, $pattern);
                        }
                    }
                    return '';
                },
                $new_page_text
            );
        }

        //replace patterns @%$ ... %@
        if($err === '') {
            $new_page_text = preg_replace_callback(
                '/@%\$(.*?)%@/s',
                function($matches) use($json_o, $json, &$cnt) {
                    list(, $path) = $matches;

                    $var = $json;
                    $tokens = $json_o->parse_tokens($path);

                    //get variable on specified path of the json data
                    foreach($tokens as $tok) {
                        if(is_array($var) && isset($var[$tok])) {
                            $var = $var[$tok];
                        }
                        else {
                            $var = null;
                            break;
                        }
                    }

                    //return extraced data
                    if(is_bool($var)) {
                        $cnt['scalars extracted'] ++;
                        return $var ? 'true' : 'false';
                    }
                    else if(is_scalar($var)) {
                        $cnt['scalars extracted'] ++;
                        return strval($var);
                    }
                    else if(is_array($var)) {
                        $cnt['json extracted'] ++;
                        return json_encode($var);
                    }
                    else {
                        $cnt['null values'] ++;
                        return 'null';
                    }
                },
                $new_page_text
            );
        }

        //create new file or send error
        if($err === '') {
            $response = array(
                'response' => 'OK',
                'mime' => '',
                'msg' => sprintf($this->getLang('file_created_msg'), $new_page_link, $template_link)
                        .'<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'
                        .sprintf($this->getLang('file_created_stat'),
                            $cnt['sections shown'], $cnt['sections removed'],
                            $cnt['scalars extracted'], $cnt['json extracted'], $cnt['null values']
                        )
                        .'<br/>'
            );

            if(!$mime) {
                //save document as new Dokuwiki page
                saveWikiText($new_page_id, $new_page_text, sprintf($this->getLang('file_created_summary'), $template_id));
            }
            else {
                //send data, so they can be downloaded
                $response['mime'] = $mime;
                $response['document'] = $new_page_text;
            }

            //store autoincrement into metadata of the caller
            if(strlen($docname_meta) > 0 && preg_match('/^(.+?)(\d+)$/', $new_page_id, $matches)) {
                list(, $base, $number) = $matches;
                $base .= str_repeat('0', strlen($number));
                if($docname_meta === $base && strlen($parent_id) > 0) {
                    p_set_metadata($parent_id, array('jsongendoc_'.$docname_meta => $number + 1));
                }
            }
        }
        else {
            $response = array(
                'response' => 'error',
                'msg' => '<span class="json-error">'.$this->getLang('err').'</span>: '.$err.'<br/>'
            );
        }

        //send response
        header('Content-Type: application/json');
        echo json_encode($response);
    }
}
