<?php
	/**
	 * This file contains the QTreeNav class.
	 *
	 * @package Controls
	 */

	/**
	 * @package Controls
	 *
	 * @property mixed $SelectedItem
	 * @property-read mixed $SelectedValue
	 * @property-read mixed $ChildItemArray
	 * @property mixed $ItemArray
	 * @property string $ItemCssStyle
	 * @property string $ItemSelectedCssStyle
	 * @property string $ItemHoverCssStyle
	 * @property integer $IndentWidth
	 * @property integer $ItemHeight
	 * @property integer $ItemWidth
	 * @property boolean $ExpandOnSelect
	 * @property-write mixed $ItemExpanded
	 */
	class QTreeNav extends QControl {
		protected $strJavaScripts = 'treenav.js';

		protected $strItemCssStyle = 'treenav_item';
		protected $strItemSelectedCssStyle = 'treenav_item treenav_item_selected';
		protected $strItemHoverCssStyle = 'treenav_item treenav_item_hover';
		protected $strItemSelectedHoverCssStyle = 'treenav_item treenav_item_hover treenav_item_selected';

		protected $strLabelForRequired;
		protected $strLabelForRequiredUnnamed;

		protected $intIndentWidth = 15;
		protected $intItemHeight = 15;
		protected $intItemWidth = 0;

		protected $objChildItemArray = array();
		protected $objItemArray = array();
		protected $intNextItemId = 1;
		protected $objSelectedTreeNavItem = null;
		protected $strLoader = null;
		protected $objLoaderParent = null;

		protected $blnIsBlockElement = true;
		protected $blnExpandOnSelect = true;
		protected $blnAllowHover = false;

		public function __construct($objParentObject, $strControlId = null) {
			parent::__construct($objParentObject, $strControlId);

			$this->strLabelForRequired = QApplication::Translate('%s is required');
			$this->strLabelForRequiredUnnamed = QApplication::Translate('Required');
		}

		public function SetLoader($strFuncName, $objParent)
		{
			$this->strLoader = $strFuncName;
			$this->objLoaderParent = $objParent;
		}

		protected function GetItemHtml($objItem) {
			$strItemId = $this->strControlId . '_' . $objItem->ItemId;

			$objChildren = $objItem->ChildItemArray;
			$intChildCount = count($objChildren);

			$strSubNodeHtml = '';
			$strImageHtml = '';
			$strLabelHtml = '';

			if ($intChildCount || $objItem->HasChildren) {
				// This Item has Children -- Must show either Collapsed or Expanded icon
				if ($objItem->Expanded) {
					$strImageHtml = sprintf('<span style="margin-right: 2px;"><img id="%s_image" src="%s/treenav_expanded.png" width="11" height="11" alt="" style="position: relative; top: 2px; cursor: pointer;" onclick="treenavToggleImage(\'%s\')"/></span>',
						$strItemId, __VIRTUAL_DIRECTORY__ . __IMAGE_ASSETS__, $strItemId);

					if($intChildCount == 0 && null !== $this->strLoader && null !== $this->objLoaderParent)
					{
						//this is a dynamic item, call the loader
						$strLoader = $this->strLoader;
						$this->objLoaderParent->$strLoader($objItem);
						
						//now it should have children. Be sure to use them.
						$objChildren = $objItem->ChildItemArray;
						$intChildCount = count($objChildren);
					}

					for ($intIndex = 0; $intIndex < $intChildCount; $intIndex++) {
						$objChildItem = $objChildren[$intIndex];
						$strChildItemId = $this->strControlId . '_' . $objChildItem->ItemId;
						$strSubNodeHtml .= sprintf('<div id="%s">%s</div>', $strChildItemId, $this->GetItemHtml($objChildItem, $strChildItemId));
					}

					$strSubNodeHtml = sprintf('<div id="%s_sub" style="margin-left: %spx;">%s</div>',
						$strItemId,
						$this->intIndentWidth,
						$strSubNodeHtml);
				} else {
					$strSubNodeHtml = sprintf('<div id="%s_sub" style="margin-left: %spx; display: none;"><span class="%s" style="cursor: auto;">%s...</span></div>',
						$strItemId,
						$this->intIndentWidth,
						$this->strItemCssStyle,
						QApplication::Translate('Loading')
					);

					$strCommand = sprintf('onclick="treenavToggleImage(\'%s\'); qc.pA(\'%s\', \'%s\', \'QTreeNav_Expand\', \'%s\')"',
						$strItemId,
						$this->objForm->FormId,
						$this->strControlId,
						$strItemId
					);

					$strImageHtml = sprintf('<span style="margin-right: 2px;"><img id="%s_image" src="%s/treenav_not_expanded.png" width="11" height="11" alt="" style="position: relative; top: 2px; cursor: pointer;" %s/></span>',
						$strItemId, __VIRTUAL_DIRECTORY__ . __IMAGE_ASSETS__, $strCommand);
				}

			} else {
				// No Children -- we are displaying an End Node
				$strImageHtml = '<span style="margin-right: 2px;"><img src="' . __VIRTUAL_DIRECTORY__ . __IMAGE_ASSETS__ .
					'/treenav_child.png" width="11" height="11" alt="" style="position: relative; top: 2px;"/></span>';
			}

			$strCommand = sprintf('onclick="qc.pA(\'%s\', \'%s\', \'QChangeEvent\', \'%s\', \'%s\')"',
				$this->objForm->FormId,
				$this->strControlId,
        $strItemId,
        $this->objForm->DefaultWaitIcon->ControlId
      );
			if($this->blnAllowHover)
				$strLabelHtml = sprintf('<span id="%s_label" class="%s" %s>%s</span>',
					$strItemId,
					($objItem->Selected) ? $this->strItemSelectedCssStyle : $this->strItemCssStyle,
					$strItemId, ($objItem->Selected) ? $this->strItemSelectedHoverCssStyle : $this->strItemHoverCssStyle ,
					$strItemId, ($objItem->Selected) ? $this->strItemSelectedCssStyle : $this->strItemCssStyle,
					$strCommand,
					$objItem->Name);
			else
				$strLabelHtml = sprintf('<span id="%s_label" class="%s" %s>%s</span>',
					$strItemId,
					($objItem->Selected) ? $this->strItemSelectedCssStyle : $this->strItemCssStyle,
					$strCommand,
					$objItem->Name);


			if ($this->intItemWidth > 0)
				return sprintf('<div style="height: %spx; width: %spx;">%s%s</div>%s',
					$this->intItemHeight, $this->intItemWidth, $strImageHtml, $strLabelHtml, $strSubNodeHtml);
			else
				return sprintf('<div style="height: %spx;">%s%s</div>%s',
					$this->intItemHeight, $strImageHtml, $strLabelHtml, $strSubNodeHtml);
		}

		public function AddChildItem(QTreeNavItem $objItem) {
			array_push($this->objChildItemArray, $objItem);
		}

		public function AddItem(QTreeNavItem $objItem) {
			if (array_key_exists($objItem->ItemId, $this->objItemArray))
				throw new QCallerException('Item Id already exists in QTreeNav ' . $this->strControlId . ': ' . $objItem->ItemId, 2);
			$this->objItemArray[$objItem->ItemId] = $objItem;
		}

		public function GetItem($strItemId) {
			if (strpos($strItemId, '_') !== false) {
				$intIndexArray = explode('_', $strItemId);
				$strItemId = $intIndexArray[1];
			}

			if (array_key_exists($strItemId, $this->objItemArray))
				return $this->objItemArray[$strItemId];
			else
				return null;
		}

		public function GenerateItemId() {
			$strToReturn = 'i' . $this->intNextItemId;
			$this->intNextItemId++;
			return $strToReturn;
		}

		public function Clear()
		{
			$this->objChildItemArray = array();
			$this->objItemArray = array();
			$this->intNextItemId = 1;
			$this->objSelectedTreeNavItem = null;
			$this->blnModified = true;
		}

		public function ParsePostData() {
			if (array_key_exists('Qform__FormControl', $_POST) && ($_POST['Qform__FormControl'] == $this->strControlId)) {
				if ($_POST['Qform__FormEvent'] == 'QChangeEvent') {
					$strParameter = $_POST['Qform__FormParameter'];
					$objItem = $this->GetItem($strParameter);

					$this->SelectedItem = $objItem;

					$strItemHtml = $this->GetItemHtml($objItem, $strParameter);
					$strItemHtml = addslashes($strItemHtml);
					QApplication::ExecuteJavaScript('treenavRedrawElement("' . $strParameter . '", "' . $strItemHtml . '")');
				} else if ($_POST['Qform__FormEvent'] == 'QTreeNav_Expand') {
					$strParameter = $_POST['Qform__FormParameter'];
					$objItem = $this->GetItem($strParameter);
					$objItem->Expanded = true;

					$strItemHtml = $this->GetItemHtml($objItem, $strParameter);
					$strItemHtml = addslashes($strItemHtml);
					QApplication::ExecuteJavaScript('treenavRedrawElement("' . $strParameter . '", "' . $strItemHtml . '")');
				}
			}
		}

		public function Validate() {
			// Check for Required
			if ($this->blnRequired && $this->SelectedItem === null)
			{
				if ($this->strName)
					$this->ValidationError = sprintf($this->strLabelForRequired, $this->strName);
				else
					$this->ValidationError = $this->strLabelForRequiredUnnamed;
				return false;
			}

			return true;
		}

		public function GetControlHtml() {
			$strAttributes = $this->GetAttributes();
			$strStyles = $this->GetStyleAttributes();

			if ($strStyles)
				$strStyles = sprintf(' style="%s"', $strStyles);

			$strItemHtml = '';
			foreach ($this->objChildItemArray as $objItem) {
				$strItemId = $this->strControlId . '_' . $objItem->ItemId;
				$strItemHtml .= '<div id="' . $strItemId . '">' . $this->GetItemHtml($objItem) . '</div>';
			}

			return sprintf('<div id="%s" %s%s>%s</div>',
				$this->strControlId,
				$strAttributes,
				$strStyles,
				$strItemHtml);
		}


		/////////////////////////
		// Public Properties: GET
		/////////////////////////
		public function __get($strName) {
			switch ($strName) {
				case "SelectedItem": return $this->objSelectedTreeNavItem;
				case "SelectedValue":
					if ($this->objSelectedTreeNavItem)
						return $this->objSelectedTreeNavItem->Value;
					else
						return null;
				case "ChildItemArray": return (array) $this->objChildItemArray;
				case "ItemArray": return (array) $this->objItemArray;

				case "ItemCssStyle": return $this->strItemCssStyle;
				case "ItemSelectedCssStyle": return $this->strItemSelectedCssStyle;
				case "ItemHoverCssStyle": return $this->strItemHoverCssStyle;

				case "IndentWidth": return $this->intIndentWidth;
				case "ItemHeight": return $this->intItemHeight;
				case "ItemWidth": return $this->intItemWidth;

				case "ExpandOnSelect": return $this->blnExpandOnSelect;

				default:
					try {
						return parent::__get($strName);
					} catch (QCallerException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
			}
		}

		/////////////////////////
		// Public Properties: SET
		/////////////////////////
		public function __set($strName, $mixValue) {
			switch ($strName) {
				case "ItemCssStyle":
					try {
						$this->strItemCssStyle = QType::Cast($mixValue, QType::String);
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
				case "ItemSelectedCssStyle":
					try {
						$this->strItemSelectedCssStyle = QType::Cast($mixValue, QType::String);
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
				case "ItemHoverCssStyle":
					try {
						$this->strItemHoverCssStyle = QType::Cast($mixValue, QType::String);
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
				case "IndentWidth":
					try {
						$this->intIndentWidth = QType::Cast($mixValue, QType::Integer);
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
				case "ItemHeight":
					try {
						$this->intItemHeight = QType::Cast($mixValue, QType::Integer);
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
				case "ItemWidth":
					try {
						$this->intItemWidth = QType::Cast($mixValue, QType::Integer);
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
				case "ExpandOnSelect":
					try {
						$this->blnExpandOnSelect = QType::Cast($mixValue, QType::Boolean);
						break;
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}

				case "SelectedItem":
					try {
						$objItem = QType::Cast($mixValue, "QTreeNavItem");
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}

					// If the currently selected item is $objItem, then do nothing
					if ($objItem && $this->objSelectedTreeNavItem && ((string) $this->objSelectedTreeNavItem->ItemId == (string) $objItem->ItemId))
						return $objItem;

					// Deselect the Old (if applicable)
					if ($this->objSelectedTreeNavItem) {
						// if we are in an AJAX response scenario, we MUST remember to use a javascript update call
						// to "deselect" the old selected item
						QApplication::ExecuteJavaScript(sprintf("treenavItemUnselect('%s_%s_label', '%s')", $this->strControlId, $this->objSelectedTreeNavItem->ItemId, $this->strItemCssStyle));

						// Update deselection in the form state, too
						$this->objSelectedTreeNavItem->Selected = false;
					}

					if ($this->objSelectedTreeNavItem = $objItem) {
						$objItem->Selected = true;

						if ($this->blnExpandOnSelect)
							$objItem->Expanded = true;

						// Ensure that all parents and ancestors are expanded
						$objParent = $this->GetItem($objItem->ParentItemId);

						while ($objParent) {
							$objParent->Expanded = true;
							$objParent = $this->GetItem($objParent->ParentItemId);
						}
					}

					return $objItem;

				case "ItemExpanded":
					$strTokenArray = explode(' ', $mixValue);
					$objItem = $this->GetItem($strTokenArray[0]);
					$objItem->Expanded = $strTokenArray[1];
					return $strTokenArray[1];

				default:
					try {
						parent::__set($strName, $mixValue);
					} catch (QCallerException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
					break;
			}
		}
	}