<?php

/**
 * Base View class
 *
 * Represents a View to be displayed.
 *
 * @version DR1.6.1
 */
class View implements IteratorAggregate {



	/** Dependant views accessible via Iterator interface */
	protected $children = array();

	/** List of accepted events, all events when empty */
	protected $acceptedEvents = array();

	/** Event serializer object */
	protected $eventSerializer;

	/** Template engine object */
	protected $smarty;

	/** Special effect to use on update */
	protected $effect;

	/** Properties mapped to template variables */
	protected $data;

	/** Package name, template directory */
	protected $package = 'core-views';

	/** View identifier required */
	protected $idRequired = false;

	/** View is the root (topmost) in cascade */
	protected $isRootView = false;

	/** Markup element used to wrap element */
	protected $wrappingElement = 'span';



	public function __construct($id = null) {
		$this->id = trim($id);
		if (is_numeric($this->id)) {
			throw new Exception("view identifiers cannot be numeric ({$this->id})");
		}
		if ($this->idRequired and !$this->id) {
			throw new Exception("not null identifier required for this view");
		}
		$this->class = get_class($this);
		$this->template = get_class($this) . '.tpl';
		$this->eventSerializer = new EventSerializer();
	}



	public function add(View $child, $alias = null) {
		$id = $alias ? $alias : $child->getId();
		if ($id) {
    		$this->children[$id] = $child;
			$this->$id = $child;
        } else {
		    $this->children[] = $child;
		}
	}



	public function addText($s) {
		$l = new Label();
		$l->text = $s;
		$this->add($l);
	}



	public function addListener(Listener $listener) {
		if (!empty($this->acceptedEvents) and !in_array($listener->getEvent(), $this->acceptedEvents)) {
			throw new Exception('listener ' . get_class($listener) . ' not allowed for view ' . get_class($this));
		}
		$this->eventSerializer->addListener($listener);
	}



	public function getId() {
		return $this->id;
	}



	public function getIterator() {
 		return new ArrayIterator($this->children);
	}



	/**
	 * Sets visual effect on view update
	 *
	 * @param IVisualEffect
	 * @since DR1.5
	 */
	public function setEffect(IVisualEffect $effect) {
		$this->effect = $effect;
	}



	public function update() {
		if ($this->effect) {
			$this->effect->run($this);
		} else {
			$response = ResponseContext::getInstance();
			$response->update($this);
		}
	}



	public function __get($property) {
		if (isset($this->data[$property])) {
			return $this->data[$property];
		}
	}



	public function __set($property, $value) {
		$this->data[$property] = $value;
	}



	public function __isset($property) {
		return array_key_exists($property, $this->data);
	}



	public function __unset($property) {
		if (array_key_exists($property, $this->data)) {
			unset($this->data[$property]);
		}
	}



	public function hasChildren() {
		return !empty($this->children);
	}



	public function getFamily($filtered = true) {
		$family = array();
		if ($filtered) {
			$iterator = new ViewFilterIterator($this->getIterator());
		} else {
			$iterator = $this->getIterator();
		}
		foreach ($iterator as $id => $child) {
			$family[$id] = $child;
			if ($child->hasChildren()) {
				$family = array_merge($family, $child->getFamily($filtered));
			}
		}
		return $family;
	}



    public function getChildById($id) {
        foreach (new ViewFilterIterator($this->getIterator()) as $k => $child) {
            if ($child->getId() == $id or $k == $id) {
                return $child;
            }
        }
        foreach (new ViewFilterIterator($this->getIterator()) as $child) {
            if ($sub = $child->getChildById($id)) {
                return $sub;
            }
        }
    }



	public function isRootView() {
		return $this->isRootView;
	}



	public function getWrappingElement() {
		return $this->wrappingElement;
	}



	protected function buildTemplateEngine() {
		$smarty = new Smarty();
		$smarty->template_dir = 'view';
		$smarty->compile_dir = 'var/smarty/compile';
		$smarty->config_dir = 'var/smarty/config';
		$smarty->cache_dir = 'var/smarty/cache';
		$smarty->use_sub_dirs = false;
		if (function_exists('smarty_i18n')) {
			$smarty->register_function('__', 'smarty_i18n');
		}
		return $smarty;
	}



	public function __toString() {
		$t = $this->buildTemplateEngine();
		$t->assign('self', $this);
		$t->assign('events', $this->eventSerializer);
		foreach ($this->data as $property => $value) {
			$t->assign($property, $value);
		}

		$f = $this->package . '/' . $this->template;
		if (!file_exists($t->template_dir . '/' . $f)) {
			if (!file_exists($t->template_dir . '/' . ($f = $this->template))) {
				debug_print_backtrace();
				trigger_error("cannot open file '$f' for view " . get_class($this), E_USER_ERROR);
			}
		}
		return $t->fetch($f);
	}



} // class View

?>