<?php declare(strict_types=1);  
namespace MessagesReplacement; # add suffix 'new' for development version 
    if (!defined('PmWiki')) exit();
    const MESSAGESNAME = 'MessagesReplacement';
    $MessagesReplacementNew = ''; # empty string for production version, 'new' for development version
/* MessagesTemplate  
  See https://www.pmwiki.org/wiki/Cookbook/Messages 
  and https://kiwiwiki.nz/pmwiki/pmwiki.php/Cookbook/Messages
  Based on code from https://www.pmwiki.org/wiki/PITS/01368
+
  Copyright 2022-present Petko Yotov, Simon Davis
  This software is written for PmWiki; you can redistribute it and/or modify it under the terms
  of the GNU General Public License as published by the Free Software Foundation; 
  either version 3 of the License, or (at your option) any later version. 
  See pmwiki.php for full details and lack of warranty.
  
  The recipe implements a drop in PmWiki directive to replace the standard (:messages:).
  The global variable $MessagesFmt is written to by PmWiki recipes and extensions with debug and similar information.
  By convention the array has two dimensions
  * the first identifying the source of the message
  * the second the debug message itself.
  
  Differences are:
  * comma or space separated parameters, with wildcards, can be supplied to select messages displayed
  * only messages for specified parameters are displayed
  * parameters are case insensitive
  * directive does not have to start at the beginning of a line
  * placed within a HTML section of class=messages to allow formatting
  * groups of messages are separated by a blank line
+
Revision History <reverse chronological order please>
# 2024-04-27 update for PHP 8 warnings, add details=show||hide parameter
# 2023-12-31 use mb_strtolower, refactor debug message out
# 2022-02-02 Separate groups of messages, enable headings, additional simple displays
# 2022-01-22 Initial version

*/
// Initialise constants
    const NL = "\n";
    const BR = '<br/>' . NL;
# insert into HTML <head> metadata element
    \SDV($HTMLStylesFmt[__NAMESPACE__], NL
    . '.messages {font-family: monospace; font-size:small;}' . NL);
# set debug flag
    \SDV($MessagesReplacementDebug, false); # set default debug setting if not defined in a configuration file
    \SDV($MessagesReplacementHeading, false); # set headings
    \SDV($MessagesReplacementDetails, strval('')); # set details display
    $Messages_debugon = boolval ($MessagesReplacementDebug); # if on writes input and output to $MessagesFmt
    $Messages_heading = boolval ($MessagesReplacementHeading); # if on writes headings level 3 to web page
    $MessagesReplacementDetails = mb_strtolower ($MessagesReplacementDetails);
    $Messages_detail = (('show' === $MessagesReplacementDetails) || ('hide' === $MessagesReplacementDetails));
    if (!function_exists('dmsg')) {
        if (!isset ($MessagesFmt[__NAMESPACE__])) $MessagesFmt[__NAMESPACE__] = []; # initialise
        function dmsg (string $smsgprefix, $smsgdata) { # local instance
            global $MessagesFmt;
            $MessagesFmt[__NAMESPACE__] [] = 
                $smsgprefix . '= ' . (is_array ($smsgdata)
				    ? @implode (BR . $smsgprefix . '= ', \PHSC ($smsgdata))
					: \PHSC ($smsgdata));
        }
    } # end if
# Version date
    $RecipeInfo[MESSAGESNAME]['Version'] = '2024-04-27' . $MessagesReplacementNew; # PmWiki version numbering is done by date
# recipe version page variable
    $FmtPV['$MessagesReplacementVersion'] = "'" . MESSAGESNAME . ' version ' . $RecipeInfo[MESSAGESNAME]['Version'] . "'"; // return version as a custom page variable
#
    if ($Messages_debugon) dmsg('<hr>' . __FILE__, $RecipeInfo[MESSAGESNAME]['Version']);
# declare $Messages for (:if enabled MessagesReplacement:) recipe installation check
    $MessagesReplacement = true; # enabled
#
## Add a PmWiki custom markup 
# (:messages optional,name,list :)
    $markup_pattern = '/\\(:messages(?: (.*?))?:\\)/i';
    # see https://regex101.com/r/HNkxC5/2
    \Markup('messages', 'directives', $markup_pattern, __NAMESPACE__ . '\MessagesReplacement_Parse' );
# i = case insensitive
# uses lazy evaluation, preserves leading and trailing white space
//
    return; # completed messages replacement recipe setup
/*-----------------------------------------------------------------------------------------------------------*/
#
/** Main Messages parser
 *   /param   arguments as documented above
 *   /return  The HTML-formatted information wrapped in a <article> of class "messages".
 */
function MessagesReplacement_Parse (array $m):string {
    global $Messages_debugon, $pagename; # import variables
    global $MessagesFmt; 
    global $Messages_heading, $Messages_detail;
    $retVal = '<:block><section class="messages">' . NL; # start a block-level element, i.e. break out of the paragraphs.
    if (empty ($MessagesFmt)) {
        if ($Messages_debugon) {
            return 'No messages'; # no messages
        } else {
            return ''; # no messages
        }
    }
    $displayDetail = $Messages_detail;
    $args[''] = ['*']; # initialise
    $msgKeys  = '*'; # wildcard to display all messages
    $detailOpts = ''; # initialise
    if (isset($m[1])) { # parameters supplied
		$args = \ParseArgs($m[1]); # contains all text within directive
        if (array_key_exists ('details', $args)) {
            $detailOpts = mb_strtolower ($args ['details']);
    		$displayDetail = ('show' === $detailOpts) || ('hide' === $detailOpts);
        }
        $debugOpt    = array_key_exists ('debug', $args)    ? $args['debug'] === 'true' : false;
		if ($debugOpt) $Messages_debugon = true; # set on
        if (empty ($args[''])) {
            $args[''] = ['*']; # ParseArgs initialises $args
        } 
        $msgKeys = mb_strtolower (implode (',', $args[''])); # parameters case insensitive
    } 
    if ($Messages_debugon) $retVal .= 'Messages_detail: "' . var_export($Messages_detail, true) . '"' . BR;
    if ($Messages_debugon) $retVal .= 'displayDetail: "' . var_export($displayDetail, true) . '"' . BR;
    if ($Messages_debugon) $retVal .= 'm[]: ' .      (is_array ($m)        ? "'" . implode ("' '", $m)        . "'" : '"' . $m . '"') . BR;
    if ($Messages_debugon) $retVal .= "args['']: " . (is_array ($args['']) ? "'" . implode ("' '", $args['']) . "'" : '"' . $args[''] . '"') . BR;
    if ($Messages_debugon) $retVal .= 'msgKeys: ' .  (is_array ($msgKeys)  ? "'" . implode ("' '", $msgKeys)  . "'" : '"' . $msgKeys . '"') . BR;
    if ($Messages_debugon) $retVal .= 'detailOpts: ' . (is_array ($detailOpts) ? "'" . implode ("' '", $detailOpts) . "'" : '"' . $detailOpts . '"') . BR;

    $outArr = []; # store messages by key
    foreach ($MessagesFmt as $key=>$val) {
        # set up a case insensitive key, numeric keys are coalesced
        $mk = $key;
        if (is_integer($key)) $mk = '';
        if (is_string($key)) $mk = mb_strtolower ($mk); # treat keys as case insensitive
        if (!isset($outArr[$mk])) $outArr[$mk] = [];
        # extract all the values for this key to the output array
        foreach((array)$val as $vv) {
            $outArr[$mk][] =  $vv;
        }
    }
    if ($Messages_debugon) $retVal .=  'MF keys: ' . implode (', ', array_keys($outArr)) . BR;
    $out = '';
    $foundkeys = \MatchNames (array_keys($outArr), $msgKeys); # PmWiki function
    if ($Messages_debugon) $retVal .= 'foundkeys: ' . implode (', ', $foundkeys) . BR;
    foreach ($foundkeys as $fk) {
		if ($displayDetail) {
            $out .= '<details';
            if ('show' == $detailOpts) $out .= ' open="open"';
            $out .= '><summary>' . $fk . '</summary>';
		}
        if ($Messages_heading) {
			$out .= '<h3>' . $fk . '</h3>';
		}
        $out .= implode(BR, (array)$outArr[$fk]) . BR;
		if ($displayDetail) {
            $out .= '</details>' . NL;
		}
    }
    $retVal .= \FmtPageName($out, $pagename); # convert $ constants
    return Keep ($retVal . NL . '</section>' . NL);
} # end MessagesReplacement_Parse