<?php if (!defined('PmWiki')) exit();

/* cluster.php  for PmWiki 2
 * DESCRIPTION:  The Cluster recipe adds group-clustering
 * functionality to PmWiki, in a pseudo-hierarchical way.
 * For info, see http://www.pmwiki.org/wiki/Cookbook/Cluster
 * Based on Hg by Dan Vis  <editor àt fast döt st>, Copyright 2007.
 *  see http://www.pmwiki.org/wiki/Cookbook/Hg
 * Modified by: Kathryn Andersen
 * ----
 * Further mods by Hans Bracker
 * An mangled again by Feral, 2007-03-28, 2007-04-01
 * ... Even if someone does not like to leave attribution in the source file ...
 */
$RecipeInfo['Cluster']['Version'] = '2007-04-01';
/* -[Feral:091/07@06:56]---------------------------------------------------
 * 2007-04-01: Added offset param to ClusterCountLevels(); Set to a number to add that to the return value; Usefull for $g01, i.e. $g0+1
 * Added [[|-- and [[|- link shortcuts for $ClusterPageTitlespaced and $ClusterPageTitle respectivly.
 * changed $pagename to $pn for the PTVs, *sigh* I thought they were the same thing...
 * An example PageListTemplate that uses the $g01
----
!!!fmt=#currentclusterdesc
Inspired by Kathryn Andersen's #hgtitle2
[@
[[#currentclusterdesc]]
(:template defaults order=group list=group:)
(:template first {=$Group}:)
(:if equal "{=$g0}" "{*$g01}" :)
*[[{=$Group}|{=$GroupTitlespaced}]]
->'-%smaller italic small-caps%Description:%% {{=$Group}$Description}-'
(:ifend:)
[[#currentclusterdesc]]
@]
----

With the page list:
	(:pagelist group={$Group}{$CS}* fmt=#currentclusterdesc:)

For the pages:
	Kingdom-Plant/
	Kingdom-Animal/
will give you:
	* Plant
	Description: Flora!
	* Animal
	Description: Friend!

For the pages:
	Kingdom-Plant-Grass/
	Kingdom-Animal-Canine/
will give you:
	* Grass
	Description: Pretty...
	* Canine
	Description: Puppies!

At least in my tests anyway ;)

 * made the name=1 param of ClusterSlice er work. If the page has $Title, it is used; Else If pagename is a $Group.$Group, the the last segment of the cluster name is used; Else what $Title returns is used. ($Name or $Namespaced)
 * --------------------------------------------------------------------- */



/* ================================================================
 * Customisation Variables for use in local config file
 */
SDV($ClusterSeparator, '-');
SDV($ClusterMaxLevels, 7);
SDV($ClusterBreadCrumbSeparator, " $ClusterSeparator ");
SDV($ClusterEnableTitles, false);
SDV($ClusterEnableSpaces, false);


/* ================================================================
 * script Setup
 */
$pagename = ResolvePageName($pagename);
$EnablePGCust = 0;
$m = preg_split('/[.\\/]/', $pagename);
$group = $m[0];
$name = $m[1];

/* ================================================================
 * Page Variables
 */
/*
 * Please note:
 * Page Variables CANNOT be set by using a function which processes
 * the current page name, however efficient that may be.
 * They MUST be set by giving them a function to call, because
 * the *current* pagename is not the only pagename they might need
 * to process.  Inside pagelists, they are given the pagename of
 * the page in the list of pages that the pagelist is processing.
 * Therefore, if you use the current $pagename, then you break all use
 * inside pagelists.
 */
$FmtPV['$g01'] = 'ClusterCountLevels($group, +1)';
$FmtPV['$g0'] = 'ClusterCountLevels($group)';
$FmtPV['$n0'] = 'ClusterCountLevels($name)';
for ($i=0; $i < $ClusterMaxLevels; $i++) {
	$groupvar = "g" . ($i + 1);
	$namevar = "n" . ($i + 1);
	$FmtPV['$' . $groupvar] = 'ClusterSplitName($group, ' . $i . ')';
	$FmtPV['$' . $namevar] = 'ClusterSplitName($name, ' . $i . ')';
}


$FmtPV['$ClusterSideBar']	= 'ClusterPageName($group, "SideBar")';
$FmtPV['$ClusterRightBar']	= 'ClusterPageName($group, "RightBar")';

$FmtPV['$BreadCrumb']		= 'ClusterSlice($pn, "separator=\''.$ClusterBreadCrumbSeparator.'\' return=links title=false space=false")';
$FmtPV['$BreadCrumbTitle']	= 'ClusterSlice($pn, "separator=\''.$ClusterBreadCrumbSeparator.'\' return=links title=true space=true")';
$FmtPV['$GroupTitle']		= 'ClusterSlice($pn, "last=-1 return=names title=true space=false")';
$FmtPV['$GroupTitlespaced']	= 'ClusterSlice($pn, "last=-1 return=names title=true space=true")';
$FmtPV['$ClusterPageTitle']			= 'ClusterSlice($pn, "name=1 last=-1 return=names title=true space=false")';
$FmtPV['$ClusterPageTitlespaced']	= 'ClusterSlice($pn, "name=1 last=-1 return=names title=true space=true")';

$FmtPV['$CS']				= '"'.$ClusterSeparator.'"';
$FmtPV['$CBCS']				= '"'.$ClusterBreadCrumbSeparator.'"';
$FmtPV['$ClusterFirst1']	= 'ClusterSlice($pn, "first=1")';
$FmtPV['$ClusterFirst2']	= 'ClusterSlice($pn, "first=2")';
$FmtPV['$ClusterFirst3']	= 'ClusterSlice($pn, "first=3")';
$FmtPV['$ClusterFirst4']	= 'ClusterSlice($pn, "first=4")';
$FmtPV['$ClusterFirst5']	= 'ClusterSlice($pn, "first=5")';
$FmtPV['$ClusterFirst6']	= 'ClusterSlice($pn, "first=6")';
$FmtPV['$ClusterFirst7']	= 'ClusterSlice($pn, "first=7")';
$FmtPV['$ClusterLast1']		= 'ClusterSlice($pn, "last=1")';
$FmtPV['$ClusterLast2']		= 'ClusterSlice($pn, "last=2")';
$FmtPV['$ClusterLast3']		= 'ClusterSlice($pn, "last=3")';
$FmtPV['$ClusterLast4']		= 'ClusterSlice($pn, "last=4")';
$FmtPV['$ClusterLast5']		= 'ClusterSlice($pn, "last=5")';
$FmtPV['$ClusterLast6']		= 'ClusterSlice($pn, "last=6")';
$FmtPV['$ClusterLast7']		= 'ClusterSlice($pn, "last=7")';

Markup('[[|-', '<[[|',
  "/(?>\\[\\[([^|\\]]+))\\|\\s*-\\s*]]/e",
  "Keep(MakeLink(\$pagename, PSS('$1'),
                 PageVar(MakePageName(\$pagename,PSS('$1')), '\$ClusterPageTitle')
                ),'L')");
Markup('[[|--', '<[[-',
  "/(?>\\[\\[([^|\\]]+))\\|\\s*--\\s*]]/e",
  "Keep(MakeLink(\$pagename, PSS('$1'),
                 PageVar(MakePageName(\$pagename,PSS('$1')), '\$ClusterPageTitlespaced')
                ),'L')");

//{*$ClusterPageTitlespaced}
//                 FeralLastElement(MakePageName(\$pagename,PSS('$1') ), PSS('$2') )
//function FeralLastElement($what, $how=null)
//{
//	global $AsSpacedFunction;
//	global $ClusterSeparator;
//
//	$pagename = ResolvePageName($what);
//	$m = preg_split('/[.\\/]/', $pagename);
//	$group = $m[0];
//	$name = $m[1];
//
//	if	(	$group == $name)
//	{
//		$results = explode($ClusterSeparator, $name);
//		$element = array_pop($results);
//	}
//	else
//	{
//		$element = $name;
//	}
//
//	if	(	$how)
//	{
//		$element = $AsSpacedFunction($element);
//	}
//
//	return $element;
//}

/* ================================================================
 * Set up clustered configurations
 */

$found_config = false;
$found_grouphead = false;
$found_groupfoot = false;
$found_attr = false;
if (file_exists("$LocalDir/$group.$name.php")) {
	include_once("$LocalDir/$group.$name.php");
	$found_config = true;
}
$PageCSSListFmt["pub/css/$group.$name.css"] = "$PubDirUrl/css/$group.$name.css"; 
$groups = explode($ClusterSeparator, $group);
while (count($groups) != 0) {
	$cluster_group = implode ($ClusterSeparator, $groups);
	if (!$found_config && file_exists("$LocalDir/$cluster_group.php")) {
		include_once("$LocalDir/$cluster_group.php");
		$found_config = true;
	}
	if (!$found_grouphead && PageExists("$cluster_group.GroupHeader")) {
		$GroupHeaderFmt = "(:nl:)(:include $cluster_group.GroupHeader:)(:nl:)";
		$found_grouphead = true;
	}
	if (!$found_groupfoot && PageExists("$cluster_group.GroupFooter")) {
		$GroupFooterFmt = "(:nl:)(:include $cluster_group.GroupFooter:)(:nl:)";
		$found_groupfoot = true;
	}
	if (!$found_attr && PageExists("$cluster_group.GroupAttributes")) {
		$GroupAttributesFmt = "$cluster_group.GroupAttributes";
		$found_groupfoot = true;
	}
	$PageCSSListFmt["pub/css/$cluster_group.css"] = "$PubDirUrl/css/$cluster_group.css"; 
	array_pop($groups);
}

// set the defaults for those that haven't been found
if (!$found_config && file_exists("$LocalDir/$default.php")) {
	include_once("$LocalDir/$default.php");
}
if (!$found_grouphead && PageExists("$SiteGroup.GroupHeader")) {
	$GroupHeaderFmt = "(:nl:)(:include $SiteGroup.GroupHeader:)(:nl:)";
}
if (!$found_groupfoot && PageExists("$SiteGroup.GroupFooter")) {
	$GroupFooterFmt = "(:nl:)(:include $SiteGroup.GroupFooter:)(:nl:)";
}
$PageCSSListFmt["pub/css/local.css"] = "$PubDirUrl/css/local.css";
$PageCSSListFmt = array_reverse($PageCSSListFmt, true);


/* ================================================================
 * Functions
 */
function ClusterCountLevels($name,$offset=0) {
	global $ClusterSeparator;
	$parts = explode($ClusterSeparator, $name);
	settype($offset, 'int');
	return (count($parts)+$offset);
}

function ClusterSplitName($name, $ind) {
	global $ClusterSeparator;
	$parts = explode($ClusterSeparator, $name);
	if ($ind < 0 || $ind >= count($parts)) return '';
	else return $parts[$ind];
}



# return the pagename of the closest ancestor
function ClusterPageName($group, $name) {
	global $ClusterSeparator;
	global $SiteGroup;
	$groups = explode($ClusterSeparator, $group);
	while (count($groups) != 0) {
		$cluster_group = implode ($ClusterSeparator, $groups);
		if (PageExists("$cluster_group.$name")) {
			// short-circuit return!
			return "$cluster_group.$name";
		}
		array_pop($groups);
	}
	return "$SiteGroup.$name";
}


/* ================================================================
 * Markup for link shortcuts
 */
Markup('[[cluster','<var','/\\[\\[(-|[\\*\\^]+)(.*?)\\]\\]/e', 'ClusterLinks($pagename, "$1", "$2")');
function ClusterLinks($pagename, $prefix, $inlink) {
	global $ClusterSeparator;
	$group = FmtPageName('$Group', $pagename);
	if ($prefix == "-") {
		return "[[$group$ClusterSeparator$inlink]]";
	}
	$groups = explode($ClusterSeparator, $group);
	$c = strlen($prefix);
	$i = 0;
	while ($i < $c) {
		if (substr($prefix, 0, 1) == "*") $link = $link . $groups[$i] . $ClusterSeparator;
		if (substr($prefix, 0, 1) == "^") array_pop($groups);
		$i = $i + 1;
	}
	if (substr($prefix, 0, 1) == "*") $link = substr ($link, 0, -1);
	if (substr($prefix, 0, 1) == "^") $link = implode($ClusterSeparator, $groups);
	return "[[$link$inlink]]";
}

// ------------------------------------------------------------------------
function ClusterHelper_FetchGroupTitle($group)
{ // {{{
	global $DefaultName;

	$pagepossibles = array(
		"$group.GroupAttributes",
		"$group.GroupHeader",
		"$group.GroupFooter",
		"$group.$group",
		"$group.$DefaultName",
	);
	$group_title=null;
	foreach($pagepossibles as $try)
	{
		if	(	PageExists($try) )
		{
			$page = ReadPage($try, READPAGE_CURRENT);
			if	(	$page['title'])
			{
				$group_title = $page['title'];
				break;
			}
		}
	}
	return $group_title;
} // }}}
// ------------------------------------------------------------------------

// ------------------------------------------------------------------------
Markup('clusterslice', 'directives',
	'/\\(:clusterslice\\s*(.*?):\\)/ei',
	"ClusterSlice(\$pagename, PSS('$1'))"
	);
// return a 'slice' of the cluster trail;
function ClusterSlice($pagename, $opt)
{ // {{{

	// For $usetitles = true;
	// In pmwiki.php: $PagePathFmt = array('{$Group}.$1','$1.$1','$1.{$DefaultName}');
	global $DefaultName, $AsSpacedFunction, $FmtPV;
	global $ClusterSeparator, $ClusterEnableTitles, $ClusterEnableSpaces;

	$defaults = array(
		'left' => 0,			// Num elements to remove from left side of group
		'right' => 0,			// Num elements to remove from right side of group

		'first' => null,		// EXCLUSIVE to last, synonym to firstlast=+
		'last' => null,			// EXCLUSIVE to first, synonym to firstlast=-
		'firstlast' => null,	// return this many + first, - last elements.

//		'separator' => $ClusterBreadCrumbSeparator,
		'separator' => $ClusterSeparator,

		'return' => 'names',	// 'links', 'groups' or 'names', lower case please.
		'title' => $ClusterEnableTitles,
		'space' => $ClusterEnableSpaces,

		'name' => null,			// If name is set then use the page $Name rather than $Group for our parts;
	);

	$opt = array_merge($defaults, ParseArgs($opt));
//print("<br/>opt:");var_dump($opt);
//print("<hr/>");
	$title = $opt['title'];
	if	(	$title == 'false') { $title = false; }
	settype($title, "boolean");

	$space = $opt['space'];
	if	(	$space == 'false') { $space = false; }
	settype($space, "boolean");


//	print("<br/>title:");var_dump($title);
//	print("<br/>space:");var_dump($space);

	$parts = ClusterHelper_FetchParts($pagename, $opt);

//	print("<br/>groups:");var_dump($parts);
//	die;

//groups:array(4) {
//[0]=> array(2) { ["name"]=> string(7) "GameDev" ["path"]=> string(7) "GameDev" }
//[1]=> array(2) { ["name"]=> string(5) "Games" ["path"]=> string(13) "GameDev-Games" }
//[2]=> array(2) { ["name"]=> string(7) "Expanse" ["path"]=> string(21) "GameDev-Games-Expanse" }
//[3]=> array(2) { ["name"]=> string(9) "WhatIWant" ["path"]=> string(31) "GameDev-Games-Expanse-WhatIWant" }
//}


//print("<br/>opt:");var_dump($opt);
//die;

	// Return links;
	$index = 0;
	foreach($parts as $item)
	{

		if	(	$index != 0)
		{
			$out.= $opt['separator'];
		}


//print("<hr/>");
//print("<br/>pagename:");var_dump($pagename);

		$group_title_found=0;
		$group_title=null;
		if	(	$title)
		{
			$groupname = explode('.', $pagename);

			if	(	isset($opt['name'])
				and	$groupname[0] == $groupname[1]
				)
			{ // If we are a $Group.$Group Homepage style name.

//print("<br/>pagename:");var_dump($pagename);
//  '$Title' => '@$page["title"] ? $page["title"] : ($GLOBALS["SpaceWikiWords"] ? $AsSpacedFunction($name) : $name)',
				$group_title = PageVar($pagename,'$Title');

				if	(	$GLOBALS["SpaceWikiWords"])
				{
					$group_pagename = PageVar($pagename,'$Namespaced');
				}
				else
				{
					$group_pagename = PageVar($pagename,'$Name');
				}

//print("<br/>Page's Title:");var_dump($group_title);
//print("<br/>Page's Name:");var_dump($group_pagename);
				if	(	$group_title == $group_pagename)
				{	// No $Title, use last part of name cluster rather than full $Name
					$group_title = $item['name'];
//print("<br/>NO:Page Title:");var_dump($group_title);

				}

//print("<br/>name part:");var_dump($group_title);
//die;

				$group_title_found=1;
			}
			elseif	(	isset($opt['name'] ) )
			{
				$group_title = PageVar($pagename,'$Title');
				$group_title_found=1;
			}
			else
			{
				$group_title = ClusterHelper_FetchGroupTitle($item['path']);
				$group_title_found=2;
			}

//print("<br/>Title:");var_dump($group_title);
		}
		if	(	!$group_title)
		{
			$group_title = $item['name'];
			$group_title_found=3;
//print("<br/>NO:Title:");var_dump($group_title);

		}

		if	(	$space and $group_title_found != 2)
		{ // only space non titles;
			$group_title = $AsSpacedFunction($group_title);
//print("<br/>Spaced:");var_dump($group_title);
		}

//print("<br/>group_title_found:");var_dump($group_title_found);
//print("<br/>final:");var_dump($group_title);
//die;


		if	(	$opt['return'] == 'links' )
		{
			$out.= "[[{$item['path']}/|$group_title]]";
//print("<br/>outed(l):");print("[[{$item['path']}/|$group_title]]");
		}
		elseif	(	$opt['return'] == 'groups' )
		{
			$out.= $item['path'];
//print("<br/>outed(g):");var_dump($item['path'] );
		}
		elseif	(	$opt['return'] == 'names' )
		{
			$out.= $group_title;
//print("<br/>outed(n):");var_dump($group_title);
		}
		else
		{
			return ("Error:ClusterSlice:Unknown return style: ".$opt['return']);
		}

		$index++;
	}
//die;

	return $out;
} // }}}
// ------------------------------------------------------------------------

// ------------------------------------------------------------------------
function ClusterHelper_FetchParts($pagename, $opt)
{ // {{{
	global $ClusterSeparator;

	if	(	isset($opt['name']))
	{
		$part = PageVar($pagename,'$Name');
	}
	else
	{
		$part = PageVar($pagename,'$Group');
	}

	$groups_orig = explode($ClusterSeparator, $part);
	$groups = array();
	$index = 0;
	foreach($groups_orig as $item)
	{
		$path = '';
		for	($num = 0; $num <= $index; $num++)
		{
			if	(	$num != 0)
			{
				$path.= $ClusterSeparator;
			}

			$path.= $groups_orig[$num];
		}

		$groups[] = array('name' => $item, 'path' =>$path);

		$index++;
	}
//	print("<br/>groups:");var_dump($groups);
//	die;

//	'first' => 0,		// EXCLUSIVE to last, synonym to firstlast=+
//	'last' => 0,		// EXCLUSIVE to first, synonym to firstlast=-
//	'firstlast' => 0,	// return this many + first, - last elements.

	if	(	isset($opt['last']))
	{
		$firstlast = -abs($opt['last']);
	}
	if	(	isset($opt['first']))
	{
		$firstlast = +abs($opt['first']);
	}
	if	(	isset($opt['firstlast']))
	{
		$firstlast = (int)$opt['firstlast'];
	}


	# Remove right side first;
	$trim = abs($opt['right']);
	if	(	$trim >= count($groups) )
	{
		$trim = count($groups)-1;
	}
	$index = 0;
	while ($index < $trim)
	{
		array_pop($groups);
		$index++;
	}

	# Remove left side second, but leave at least 1 element (right most);
	$trim = abs($opt['left']);
	if	(	$trim >= count($groups) )
	{
		$trim = count($groups)-1;
	}

	$index = 0;
	while ($index < $trim)
	{
		array_shift($groups);
		$index++;
	}


	if	(	$firstlast and $firstlast != 0)
	{

		if	(	$firstlast > 0)
		{ // Positive; I.e. first
			$firstlast = abs($firstlast);
			$groups = array_slice($groups, 0, $firstlast);
		}
		else
		{ // Negitive; I.e. last
			$firstlast = abs($firstlast);
			$groups = array_slice($groups, -$firstlast);
		}
	}



//	print_r($groups);
//	var_dump($groups);
//	die;
	return $groups;
} // }}}
// ------------------------------------------------------------------------