<?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
/* Messages Replacement
  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-05-05 add key= parameter to maintain compatibility with built in directive
# 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;
#
    $localDmsg = false;
    if (!function_exists('dmsg')) {
       # if (!isset ($MessagesFmt[__NAMESPACE__])) $MessagesFmt[__NAMESPACE__] = []; # initialise
	   $localDmsg = true;
        function dmsg (string $smsgprefix, $smsgdata) { # local instance
            global $MessagesFmt;
            $MessagesFmt[__NAMESPACE__] [] = 
                '<span class="dmsgprefix">' . $smsgprefix . '</span>= ' . (is_array ($smsgdata)
				    ? @implode (BR . '<span class="dmsgprefix">' . $smsgprefix . '</span>= ', \PHSC ($smsgdata))
					: \PHSC ($smsgdata));
        } # end function
    } # end if
    # insert into HTML <head> metadata element
    \SDV($HTMLStylesFmt[__NAMESPACE__], NL
        . '.dmsgprefix {display:inline-block; font-style:italic; min-width:9rem;}' . NL
        . '.dmsgpre {font-size:smaller;}' . 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, ''); # set details display (show or hide)
    $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_detailOpts = (('show' === $MessagesReplacementDetails) || ('hide' === $MessagesReplacementDetails)) ? $MessagesReplacementDetails : '';

    # Version date
    $RecipeInfo[MESSAGESNAME]['Version'] = '2024-05-31' . $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']);
        dmsg ('Messages_detailOpts', $Messages_detailOpts);
    }
    # 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; # import data array for reading
    global $Messages_heading, $Messages_detailOpts;
    $retVal = '<:block><section class="messages">' . NL; # start a block-level element, i.e. break out of the paragraphs.
    if (empty ($MessagesFmt)) {
		return ($Messages_debugon) ? 'No messages' . BR : ''; # no messages
    }
	if ($Messages_debugon) {
		$retVal .= '<details><summary>' . __NAMESPACE__ . '</summary>' . NL;
		$retVal .= 'm[]: '          . (is_array ($m)            ? "'" . implode ("' '", $m)            . "'" : '"' . $m . '"') . BR;
	} # end debug
    $detailOpts = $Messages_detailOpts; # initialise
    $args[''] = ['*']; # initialise, defualt is all messages
    $msgKeys  = ''; # initialise
    if (isset($m[1])) { # parameters supplied
		$args = \ParseArgs($m[1]); # contains all text within directive
        if (!empty($args ['keys'])) { # some keys were specified
            $msgKeys .= mb_strtolower ($args['keys']); # parameters case insensitive
            if ($Messages_debugon) {
                $retVal .= "args['keys']: " . (is_array ($args['keys']) ? "'" . implode ("' '", $args['keys']) . "'" : '"' . $args['keys'] . '"') . BR;
            }
        }
        if (!empty($args [''])) { # some keys were specified
            $msgKeys .= mb_strtolower (implode (',', $args[''])); # parameters case insensitive
            if ($Messages_debugon) {
                $retVal .= "args['']: "     . (is_array ($args[''])     ? "'" . implode ("' '", $args[''])     . "'" : '"' . $args[''] . '"') . BR;
            }
        }
        if (array_key_exists ('details', $args)) {
            $detailOpts = mb_strtolower ($args ['details']);
        }
        $debugOpt    = array_key_exists ('debug', $args)    ? $args['debug'] === 'true' : false;
		if ($debugOpt) $Messages_debugon = true; # set on
    }
    $displayDetail = ('show' === $detailOpts) || ('hide' === $detailOpts);
    if ($Messages_debugon) {
        $retVal .= 'detailOpts: ' . (is_array ($detailOpts)   ? "'" . implode ("' '", $detailOpts)   . "'" : '"' . $detailOpts . '"') . BR;
        $retVal .= 'displayDetail: "' . var_export($displayDetail, true) . '"' . BR;
    } # end debug
    if (empty ($msgKeys)) $msgKeys = '*'; # display all keys
    if ($Messages_debugon) {
        $retVal .= 'msgKeys: '      . (is_array ($msgKeys)      ? "'" . implode ("' '", $msgKeys)      . "'" : '"' . $msgKeys . '"') . BR;
    }
    $outArr = []; # store messages by key
    foreach ($MessagesFmt as $key=>$val) {
        # set up a case insensitive key, numeric keys are coalesced to the key ''
        $mky = $key;
        if (is_integer($key)) $mky = ''; # coalesce numeric keys
        if (is_string($key)) $mky = mb_strtolower ($key); # treat keys as case insensitive
        if (!isset($outArr[$mky])) $outArr[$mky] = []; # initialise
        # extract all the values for this key to the output array
        foreach((array)$val as $vvl) {
            $outArr[$mky][] =  $vvl;
        } # end foreach
    } # end foreach
    if ($Messages_debugon) {
        $retVal .=  'MsgF keys: ' . implode (', ', array_keys($outArr)) . BR;
    }
    $out = '';
    $foundkeys = \MatchNames (array_keys($outArr), $msgKeys); # PmWiki function
    if ($Messages_debugon) {
        $retVal .= 'foundkeys: ' . implode (', ', $foundkeys) . BR;
		$retVal .= '</details>' . NL;
    }
    foreach ($foundkeys as $fky) {
		if ($displayDetail) {
            $out .= '<details';
            if ('show' == $detailOpts) $out .= ' open="open"';
            $out .= '><summary>' . $fky . '</summary>';
		}
        if ($Messages_heading) {
			$out .= '<h3>' . $fky . '</h3>';
		}
        $out .= implode(BR, $outArr[$fky]) . BR;
		if ($displayDetail) {
            $out .= '</details>' . NL;
		}
    } # end foreach
    $retVal .= \FmtPageName($out, $pagename); # convert $ constants
    return Keep ($retVal . NL . '</section>' . NL);
} # end MessagesReplacement_Parse