<?php
/**
 * Prolog plug-in : Rule-based System for Groupware.
 * 
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
 * @author     Paweł Kupka <pawel.kupka@gmail.com>
 */
 
// must be run within Dokuwiki
$webroot = reset(split('lib',dirname(__FILE__)));
if(!defined('DOKU_INC')) define('DOKU_INC', $webroot);
if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins');
if(!defined('DOKU_PAGES')) define('DOKU_PAGES',DOKU_INC.'data/pages');
if(!defined('DOKU_TMP')) define('DOKU_TMP',DOKU_INC.'data/tmp');
//require_once (DOKU_INC.'inc/pageutils.php');
//require_once (DOKU_INC.'inc/utf8.php');
require_once('prolog_tag.php');

class AttributeInclude extends PrologTag {

	var $name = 'include';

	/**
	 * Constructor
	 */
	function AttributeInclude() 
	{
		$this->setAttributePattern($this->name);
	}
	
	/**
	 * Gets the list of files to be included
	 * @param string $includeValue the attribute include value 
	 * @param string $fileID the file ID in the DokuWiki notation
	 * @param array $filesList list of files
	 * @return array $filesList list of files
	 */
	function getFilesList($includeValue = null, $fileID = null, &$filesList = array()) 
	{	
		if(empty($fileID))
			$fileID = getID();
					
		$includedIDs = $this->sliceIncludeValue($includeValue);
		$joinedIDs = array();
		foreach($includedIDs as $includeID)
			array_push($joinedIDs, $this->joinID($fileID,$includeID));
				
		foreach($joinedIDs as $ID) 
		{
			$files = $this->scan($ID);
			foreach($files as $file) 
			{
				if(in_array($file, $filesList) || $file == wikiFN(getID()))
					continue;
				else 
				{
					array_push($filesList, $file);
					$tags = $this->getEntryTags($file);
					foreach($tags as $tag) 
						$this->getFilesList($this->getAttributeValue($tag), $this->abs2ID($file), $filesList);
				}
			}
		}
		return $filesList;
	}
	
	/**
	 * Gets the files paths from the place designated by the ID
	 * @param string $ID ID of the page or namespace
	 * @return array $foundFiles list of files paths
	 */
	function scan($ID = null) 
	{
		$path = $this->ID2abs($ID);
			
		if(!$this->isReadable($path))
			return array();
					
		if($this->getScanType($ID) == 'SINGLE_PAGE') 
			return array($path);
		elseif($this->getScanType($ID) == 'RECURSIVE_DIRECTORY') 
			return  $this->scanDirectory($path, true);
		elseif($this->getScanType($ID) == 'SINGLE_DIRECTORY') 
			return $this->scanDirectory($path);
		else
			return array();
	}
	
	/**
	 * cuts the include attribute value
	 * @param string $value attribute include value
	 * @return array $exValues array of ID values from the include attribute
	 */
	function sliceIncludeValue($value = null) 
	{
		$parts = array();
		$exValues = explode(',', $value);
		foreach($exValues as $part) {
			$part = trim($part);
			if(!empty($part))
				array_push($parts, $part);
		}
		return $parts;
	}
	
	/**
	 * Checks if the file/directory exists and is readable
	 * @param $file file path
	 * @return bool
	 */
	function isReadable($file = null) 
	{
		if((is_file($file) || is_dir($file)) && file_exists($file) && is_readable($file))
			return true;
		else
			return false;
	}
	
	/**
	 * Returns the scan type based on the given ID
	 * @param string $ID 
	 * @return string scan type
	 */
	function getScanType($ID = null) 
	{
		$IDparts = explode(':', $ID);
		if(preg_match('/[\w]+/', end($IDparts)))
			return 'SINGLE_PAGE';
		elseif(preg_match('/\*\*/', end($IDparts)))
			return 'RECURSIVE_DIRECTORY';
		elseif(preg_match('/\*/', end($IDparts)))
			return 'SINGLE_DIRECTORY';
		else
			return 'NONE';
	}
	
	/**
	 * Reads the code from all Prolog tags in the given text files
	 * @param array $files list of files path
	 * @return string $readCode prolog source code read from the prolog tags
	 */
	function readCode($files = array()) 
	{
		$readCode = '';
		foreach($files as $file) 
		{
			$fileContent = file_get_contents($file);
			$splitedByEntry = preg_split($this->entryTagPattern,$fileContent);
			array_shift($splitedByEntry);
			foreach($splitedByEntry as $splited) 
			{
				if(!preg_match($this->exitTagPattern,$splited))
					continue;
				$splitedByExit = preg_split($this->exitTagPattern, $splited,-1,PREG_SPLIT_NO_EMPTY);
				$readCode .= $splitedByExit[0];
			}
		}
		return $readCode;
	}
	
	/**
	 * Joins two IDs into one
	 * @param string $headID base namespace
	 * @param string $tailID additional namespace
	 * @return string $joinedID
	 */
	function joinID($headID = null, $tailID = null) {
		$headNS = getNS($headID);
		$tailIDParts = explode(':',$tailID);
		$joinedIDParts = explode(':',$headNS);
		
		if(empty($headNS))
			return $tailID;
		
		foreach($tailIDParts as $tailIDPart) 
		{
			if(empty($tailIDPart))
				return implode(':',$tailIDParts);
				
			if($tailIDPart == '.') 
			{
				array_shift($tailIDParts);
				$joinedIDParts = $tailIDParts;
				break;
			}
			elseif($tailIDPart == '..')
				array_pop($joinedIDParts);
			else
				array_push($joinedIDParts, $tailIDPart);
		}
		$joinedID = implode(':',$joinedIDParts);
		
		return $joinedID;
	}
	
	/**
	 * Converts ID into the absolute path to the file/directory
	 * @param string $ID page/namespace ID
	 * @return string $abs absolute path to file/directory
	 */
	function ID2abs($ID = null) {
		if(noNS($ID) == '*' || noNS($ID) == '**')
			$abs = DOKU_PAGES.'/'.str_replace(':','/',getNS($ID));
		else
			$abs = DOKU_PAGES.'/'.str_replace(':','/',$ID.'.txt');
			
		return str_replace('\\', '/', $abs);
	}
	
	/**
	 * Converts the file/directory absolute path into ID
	 * @param string $abs absolute path to file/directory
	 * @return string $ID page/namespace ID
	 */
	function abs2ID($abs = null) {
		$abs = str_replace(realpath(DOKU_PAGES), '', realpath($abs));
		
		if(substr($abs,-4) == '.txt')
			$abs = substr($abs, 0, -4);
		else
			$abs .= '/*';
		
		$ID = preg_replace('/[\/\\\]/',':',$abs);

		if(substr($ID,0,1) == ':')
			$ID = substr($ID,1);
			
		return $ID;
	}	
	
	/**
	 * Scans the given directory in search of text files
	 * @param string $directory path to the directory
	 * @param bool $recursive if set to "true", it also scans all sub-directories, if set to "false", only the main directory (parameter value)
	 * @param array $file_list list of found text files
	 * @return array $file_list list of found text files
	 */
	function scanDirectory($directory = null, $recursive = false, &$file_list = array())
	{			
		if(substr($directory,-1) == '/' || substr($directory,-1) == '\\')
			$directory = substr($directory,0,-1);

		$directory_list = opendir($directory);

		while (false !== ($file = readdir($directory_list))) 
		{
			if($file != '.' && $file != '..') 
			{
				$path = $directory.'/'.$file;
				if(is_readable($path)) 
				{
					$subdirectories = explode('/',$path);
					if(is_dir($path) && $recursive)
							$this->scanDirectory($path, $recursive, $file_list);
					elseif(is_file($path))
					{
						$extension = end(explode('.',end($subdirectories)));
						if($extension == 'txt')
							array_push($file_list,$path);
					}
				}
			}
		}
		closedir($directory_list); 
		return $file_list;
	}
}
?>