<?php
/*
EasyGUI XP is free software released under the terms of the following BSD license.

Copyright (c) 2005,2006 Gaetan Lauff (glauff@plansoft.de)
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

- Redistributions of source code must retain the above copyright notice, 
this list of conditions and the following disclaimer. 

- Redistributions in binary form must reproduce the above copyright notice, 
this list of conditions and the following disclaimer in the documentation 
and/or other materials provided with the distribution.

- Neither the name of the developer nor the names of its contributors may 
be used to endorse or promote products derived from this software without 
specific prior written permission. 

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**
* Is the root class for all template components.
*
* All other components are derived from this.
*
* @author Gaetan Lauff <glauff@plansoft.de>
* @version	XP $Id: class.ek_tcomponent.php 112 2006-07-13 15:15:57Z glauff $
* @package 	components
*/
class EK_TComponent
{
	/**
	 * Component ID
	 *
	 * @var string
	 */
	var $id;
	
	/**
	 * EK Object
	 * 
	 * @var object
	 */
	var $ekObject;
	
	/**
	 * @var string
	 */
	var $component;	
	/**
	* Holds all component objects that are part of this component.
	* @var	array
	*/
	var $components;
	
	/**
	* Depth of the component used to indent the source code.
	* @var	integer
	*/
	var $depth;
	
	/**
	* All attributes of component.
	* @var	array
	*/
	var $attribs;
	
	/**
	* Map for components
	*
	* @var object Map
	* @see Map
	*/
	var $map;
	
	/**
	* Nested Components
	* @var string;
	*/
	var $nestedComponents = array();
	
	/**
	 * Settings
	 * @var	array
	 */
	var $settings;
	
	/**
	 * Namespace for Component
	 * @var string
	 */
	var $namespace;
	
	/**
	 * Parent Element of Current Component
	 *
	 * @var unknown_type
	 */
	var $parentElement;
	
	/**
	* Constructor
	*
	* @param	array	Attributes
	* @param	string	Component Name
	* @param	object	Map
	* @param	integer	depth
	* @param	array	Settings
	*/
	function EK_TComponent(&$attribs, $name, $namespace, &$map, $depth, &$ekObject)
	{
		$this->ekObject =& $ekObject;
		
		if (is_array( $attribs ) ) {
			$this->attribs	=& $attribs;
			$this->attribs 	= array_change_key_case($this->attribs);
		}
		
		$this->id				= $this->attribs['id'];
			
		$this->componentName 	=& $name;
		$this->componentName 	= strtolower($this->componentName);
		
		$this->depth 			=& $depth;
		$this->map 				=& $map;
		$this->settings			=& $this->ekObject->settings;
	
		$this->namespace		= $namespace;
		$this->components 		= array();
	}

	/**
	* Creates all inner components of the actual component.
	*
	* @return	string
	*
	* @access 	private
	*/
	function buildComponents( $parent = '' )
	{
		$this->parentElement = $parent;
		
		foreach ($this->components as $component) {
			if ( (	
					( isset($component->attribs['preventoutput'] ) && 
					$component->attribs['preventoutput'] == 'false' ) || 
					!isset( $component->attribs['preventoutput'] ) 
				 ) &&
				 
				 !isset( $this->ekObject->preventOutput[$this->id] )
					
				) {
						//$this->nestedComponents .= $component->buildComponents( $this->id );
						array_push( $this->nestedComponents, $component->buildComponents( $this->id ));
					}
		}
		
		if (isset( $this->ekObject->preventOutput[$this->id] ) ) {
			return '';	
		}
		
		return $this->renderComponent();
	}
	
	/**
	 * Returns a replacement to the current index
	 *
	 * @param string	index
	 *
	 * @return string
	 *
	 * @access public
	 */
	function getReplacement( $idx, $pre = null )
	{
		if ( isset($this->attribs[$idx]) ) {
			return $this->attribs[$idx];
		}
		
		if ( is_array( $this->settings) && isset($this->settings[$idx]) ) {
			return $this->settings[$idx];
		}
			
		if ( is_a($this->settings, 'DOMElement') ){
			$setting = EK_SimpleXML::getAttribute( $this->settings, $idx );
			
			if (!empty($setting)) {
				return $setting;
			}
		}
		 
		if ( defined($idx) ) {
			return constant($idx);
		}
		
		if ( isset( $_SESSION[$idx] ) ) {
			return $_SESSION[$idx];
		}
		
		if ( $idx == 'components' ) {
			return $this->serializeNestedComponents();
		}
		
		if ( $idx == 'component' ) {
			return $this->componentName;
		}
		
		if ( $idx == 'parent' ) {
			return $this->parentElement;
		}

		return null;
	}
	
	/**
	 * Serializes the Nested Components
	 *
	 * @param unknown_type $separator
	 * @return unknown
	 */
	function serializeNestedComponents( $separator = "\r\n" ) 
	{
		if (!sizeof( $this->nestedComponents ) )
			return '';
		
		return implode( $separator, $this->nestedComponents );
	}
			
	/**
	* Renders the actual component according to given Maps
	* Can be overwritten by any custom component
	*
	* @return string
	*
	* @access public
	*/
	function renderComponent()
	{
		$template = $this->map->getMapEntry( $this->componentName, $this->namespace );
		$this->replaceAttributes( $template );
		
		return $template;
	}
	
	function replaceAttributes( &$template ) 
	{
		$this->replaceExpressions( $template );
		$this->replaceValues( $template );
		$this->setStyleList( $template );
		$this->setAttributeList( $template );
	}
	
	/**
	 * Set the style list for component
	 *
	 * @param reference $template
	 */
	function setStyleList( &$template ) {
		preg_match_all( "/\{(styles[^}]*)\}/", $template, $styles, PREG_PATTERN_ORDER);
		$this->setList( $styles[1], $template );
	}
	
	/**
	 * Sets Attribute List for component
	 *
	 * @param reference $template
	 */
	function setAttributeList( &$template )
	{
		preg_match_all( "/\{(attributes[^}]*)\}/", $template, $attribs, PREG_PATTERN_ORDER);
		$this->setList( $attribs[1], $template );
	}
	
	/**
	 * Replaces all values enclosed in Double %
	 *
	 * @param reference $template
	 */
	function replaceValues( &$template )
	{
		preg_match_all( "/\%(\w+)\%/", $template, $attributes, PREG_PATTERN_ORDER );
		
		foreach ( $attributes[1] as $key => $idx ) {
			$replacement = $this->getReplacement( $idx );
			$template = str_replace( "%$idx%", $replacement, $template );
		}
	}
	
	/**
	 * Replaces all expressions inside the component template
	 * ( text|label )
	 * @todo eval regular expressions inside of templates
	 *
	 * @param unknown_type $template
	 */
	function replaceExpressions( &$template )
	{
		preg_match_all( "/\(([^)]*)\)[^;]/", $template, $expressions, PREG_PATTERN_ORDER );
		
		foreach ( $expressions[1] as $key => $expression ) {
			$replacement = null;
			$replace = $expression;
			$expr = explode( '|', $expression );
			
			foreach ( $expr as $exp ) {
				$replacement = $this->getReplacement( $exp );
				
				if (!empty($replacement)) {
					$template = str_replace( "(".$expression.")", $replacement, $template );
					break;
				}
			}
			
			if (empty($replacement)) {
				$template = str_replace( "(".$expression.")" , $replacement, $template );
			}
		}
	}

	/**
	 * Sets the Lists
	 *
	 * @param array $lists
	 * @param reference $template
	 */
	function setList( &$lists, &$template )
	{
		foreach( $lists as $key => $map ) {
			$replace = $map;
			$renderAttribs = null;
			$list = null;
			$attribs = explode( ':', $map );
			
			if ( isset($attribs[1]) ) {
				$renderAttribs = explode( ';', $attribs[1] );
			}
			
			if ( is_array( $renderAttribs ) ) {
				foreach( $renderAttribs as $renderAttrib ) {
					if ( preg_match( '/\!(.*)/', $renderAttrib, $matches ) ) {
						$unsets[$matches[1]] = $this->attribs[$matches[1]];
						unset( $this->attribs[$matches[1]] );
					} else {
						$list[] = $renderAttrib;
					}
				}
			}
			
			$map = $attribs[0];
			
			$replacement = $this->map->getReplacements( $map, $this->attribs, $this->namespace, $list );
			
			if ( $replacement != 'NOMAP' ) {	
				$template = str_replace( "{{$replace}}", $replacement, $template );
			}
			
			foreach($unsets as $unset => $val ) {
				$this->attribs[$unset] = $val;
			}
			
			unset($unsets);
		}
	}
	
	/**
	* @return string
	*/
	function getIndent()
	{
		return str_repeat( "\t", $this->depth );
	}
	
	/**
	* Registers a component in actual component.
	*
	* @param 	object	&$componentObj 	Component
	*/
	function register( &$componentObj )
	{
		array_push( $this->components, $componentObj );
	} 

	/**
	* Function to get a specific attribute of the actual component.
	*
	* @param	string	$attr	Name of Attribute to get value from
	*
	* @return	mixed	Attribute Value if it's set, FALSE otherwise
	*
	* @access 	private
	*/
	function getAttribute( $attr, $default = null ) 
	{		
		if ( isset( $this->attribs[$attr]) && $this->attribs[$attr] != '' ) { // && !empty($this->attribs[$attr])) {
			$value = $this->attribs[$attr];
	
			switch ( $value ) {
				case 'true':
					$value = TRUE;
					break;
					
				case 'false':
					$value = FALSE;
					break;
					
				case is_numeric( $value ):
					$value = 0 + $value;
					break;
					
				default:
					break;
			}
			
			return $value;
		} else {
			if ( !is_null( $default ) ) {
				return $default;
			}
		}
			
		return false;
	}
	
	/**
	 * Function to set a specific attribute of the actual component
	 *
	 * @param 	string	$attr	attribute Name
	 * @param 	string	$value	Value of attribute
	 *
	 * @return 	void
	 *
	 * @access 	public
	 */
	function setAttribute( $attr, $value )
	{
		$this->attribs[$attr] = $value;
	}
	
	/**
	 * Function to delete specific attribute of the actual component
	 *
	 * @param 	mixed	list of attributes to delete
	 *
	 * @access 	public
	 */
	function delAttribute( $attribs )
	{
		if ( is_array( $attribs ) ) {
			foreach ($attribs as $attrib) {
				$this->attribs[$attrib] = null;
			}
		} else {
			$this->attribs[$attribs] = null;
		}
	}
} // end class Component.
?>