<?php
	namespace Mocha\UI\Renderers\HTML;

	use Mocha\Core\InstanceKey;
	use Mocha\Core\InstanceReference;
	use Mocha\Core\KnownAttributeGuids;
	use Mocha\Core\KnownClassGuids;
	use Mocha\Core\KnownInstanceGuids;
	use Mocha\Core\KnownMethodBindingGuids;
	use Mocha\Core\KnownRelationshipGuids;
	use Mocha\Core\OmsContext;
	
	use Mocha\Oms\MySQLDatabaseOms;

	use Mocha\UI\Controls\InstanceBrowser;
	use Mocha\UI\ValidationResult;

	use Phast\QuickSort;
	use Phast\System;
	use Phast\Utilities\Stopwatch;
	use Phast\UUID;
	use Phast\WebControls\Button;
	
	class HTMLRenderer
	{
		/**
		 * @var OmsContext
		 */
		public $Context;

		public $TargetInstance;

		/**
		 * User-defined function to process entire postback (including validations).
		 */
		public $ProcessPostbackFunction;
		/**
		 * User-defined function to process updates (not including validations). Validations will still
		 * be processed by Mocha before this function is called. This function will NOT be called if the
		 * validations fail.
		 */
		public $ProcessUpdatesFunction;

		public $FailedValidations;

		public $FailedValidationElements;

		public $StyleClasses;
		public bool $IsPostback;
		public string $SubmitButtonText;

		public bool $IncludeTopNavigationBar;

		public function __construct(OmsContext $context)
		{
			$this->Context = $context;
			$this->FailedValidations = [];
			$this->FailedValidationElements = [];
			$this->IsPostback = false;
			$this->StyleClasses = [];
			$this->SubmitButtonText = "Save Changes";
			$this->IncludeTopNavigationBar = true;
		}
		/**
		 * Thanks https://stackoverflow.com/a/31107425
		 * 
		 * Generate a random string, using a cryptographically secure 
		 * pseudorandom number generator (random_int)
		 *
		 * This function uses type hints now (PHP 7+ only), but it was originally
		 * written for PHP 5 as well.
		 * 
		 * For PHP 7, random_int is a PHP core function
		 * For PHP 5.x, depends on https://github.com/paragonie/random_compat
		 * 
		 * @param int $length      How many characters do we want?
		 * @param string $keyspace A string of all possible characters
		 *                         to select from
		 * @return string
		 */
		private function random_str(int $length = 64, string $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' ) : string
		{
			if ($length < 1)
			{
				throw new \RangeException("Length must be a positive integer");
			}
			$pieces = [];
			$max = \mb_strlen($keyspace, '8bit') - 1;
			for ($i = 0; $i < $length; ++$i)
			{
				$pieces[] = $keyspace[\random_int(0, $max)];
			}
			return \implode('', $pieces);
		}

		public function getElementContentValue(InstanceReference $elementContent, $defaultValue = null)
		{
			if (isset($_POST["ec_" . $elementContent->InstanceKey]))
			{
				return $_POST["ec_" . $elementContent->InstanceKey];
			}
			return $defaultValue;
		}
		
		public function renderPageHeader(InstanceReference|string $title, InstanceReference|string $subtitle)
		{
			echo ("<div class=\"uwt-page-header\">");
			echo ("<h1>");
			if (is_string($title))
			{
				echo ($title);
			}
			else if ($title instanceof InstanceReference)
			{
				$adw = new InstanceBrowser(array($title));
				$adw->Render();
			}
			echo ("</h1>");

			echo ("<h2>");
			if (is_string($subtitle))
			{
				echo ($subtitle);
			}
			else if ($subtitle instanceof InstanceReference)
			{
				$adw = new InstanceBrowser(array($subtitle));
				$adw->Render();
			}
			echo ("</h2>");
			echo ("</div>");
		}

		private function renderTopNavigationBar()
		{
			$this->renderBeginTag("div", [ ], [ "uwt-header" ]);

			$this->renderBeginTag("div", [ ], [ "uwt-header-item", "uwt-applicationmenu" ]);

			$button = new Button();
			$button->Render();

			$this->renderEndTag("div");

			$this->renderEndTag("div");
		}

		public function renderTask(InstanceReference $task, InstanceReference $relatedInstance)
		{
			/**
			 * @var MySQLDatabaseOms
			 */
			$oms = mocha_get_oms();

			$taskName = $oms->getAttributeValue($task, KnownAttributeGuids::Name);

			$this->renderBeginPage($taskName);

			$this->renderPageHeader($taskName, $relatedInstance);

			if ($relatedInstance !== null)
			{
				$riParentClass = $oms->getParentClass($relatedInstance);
				if ($riParentClass !== null)
				{
					$this->Context->setWorkData($riParentClass, $relatedInstance);
				}
			}
			echo ("<!-- task: " . $task->InstanceKey . " {" . $task->GlobalIdentifier . "} -->");

			$parentClass = $oms->getParentClass($task);
			if ($parentClass->GlobalIdentifier->__is_equal(UUID::parse(KnownClassGuids::SequenceTask)))
			{
				$initiatingElement = $oms->getRelatedInstance($task, KnownRelationshipGuids::Task__has_initiating__Element);
				
				$DO_Singular = $oms->getInstanceByGlobalIdentifier(KnownInstanceGuids::DisplayOption__Singular);
				$this->renderElement($initiatingElement, [ $DO_Singular ]);
			}
			else if ($parentClass->GlobalIdentifier->__is_equal(UUID::parse(KnownClassGuids::HardcodedTask)))
			{
				$className = $oms->getAttributeValue($task, KnownAttributeGuids::ClassName);
				$taskClass = null;
				try
				{
					$taskClass = new $className($task, $relatedInstance);
				}
				catch (\Error $ex)
				{
				}

				if ($taskClass !== null)
				{
					$value = $taskClass->Render();
					if ($value instanceof InstanceReference)
					{
						$pc = $oms->getParentClass($value);
						if ($pc->GlobalIdentifier->__is_equal(UUID::parse(KnownClassGuids::Element)))
						{
							$DO_Singular = $oms->getInstanceByGlobalIdentifier(KnownInstanceGuids::DisplayOption__Singular);
							$this->renderElement($value, [ $DO_Singular ]);				
						}
					}
					else if (is_string($value))
					{
						echo($value);
					}
				}
				else
				{
					echo("could not find hardcoded task class '" . $className . "'");
				}
			}
			else
			{
				echo ("could not find task executor for task type `" . $oms->getInstanceText($parentClass) . "`, " . $parentClass->InstanceKey . " " . $parentClass->GlobalIdentifier);
			}
			$this->renderEndForm();
			$this->renderEndPage();
		}

		private function updateWorkDataWithElementContents()
		{
			/**
			 * @var MySQLDatabaseOms
			 */
			$oms = mocha_get_oms();

			// echo ("updating work datas from element contents...<br/>");
			// echo ("current work data: <br/>");
			// print_r($this->Context->workData);

			foreach ($_POST as $key => $value)
			{
				if (str_starts_with($key, "ec_"))
				{
					$ecid = substr($key, 3);
					$inst_ec = $oms->getInstanceByKey(InstanceKey::Parse($ecid));
					
					$value = $this->Context->getElementParm($inst_ec, $inst_ec->GlobalIdentifier->__toString());

					$targetInst = $oms->getRelatedInstance($inst_ec, KnownRelationshipGuids::Element_Content__has__Instance);

					//? this feels like a hack...
					$this->Context->setWorkData($targetInst->GlobalIdentifier->__toString(), $value);
				}
			}

		}

		public function processPostback(InstanceReference $element)
		{
			/**
			 * @var MySQLDatabaseOms
			 */
			$oms = mocha_get_oms();

			if (is_callable($this->ProcessPostbackFunction))
			{
				// call user-defined ProcessPostbackFunction
				return call_user_func($this->ProcessPostbackFunction, $this, $element);
			}
			else
			{
				$result = null;
				foreach ($_POST as $key => $value)
				{
					if (str_starts_with($key, "ec_"))
					{
						$ecid = substr($key, 3);
						
						$inst_ec = $oms->getInstanceByKey(InstanceKey::Parse($ecid));
						if (!$this->checkValidationsForElementContent($inst_ec, $value))
						{
							$result = false;
						}

						$instParameterAssignments = $oms->getRelatedInstances($inst_ec, KnownRelationshipGuids::Element_Content__has__Parameter_Assignment);
						foreach ($instParameterAssignments as $parmAssignment)
						{
							$assignsToParm = $oms->getRelatedInstance($parmAssignment, KnownRelationshipGuids::Parameter_Assignment__assigns_to__Parameter);

						}
						$this->Context->setElementParm($inst_ec, $inst_ec->GlobalIdentifier->__toString(), $value);
					}
				}

				if ($result === false)
				{
					return false;
				}

				if (is_callable($this->ProcessUpdatesFunction))
				{
					// call user-defined ProcessUpdatesFunction
					return call_user_func($this->ProcessUpdatesFunction, $this, $element);
				}
				else
				{
					$this->updateWorkDataWithElementContents();
					
					$pru = $oms->getRelatedInstance($element, KnownRelationshipGuids::Element__processed_by__Process_Related_Updates_Method);
					if ($pru !== null)
					{
						echo ("<!-- executing pru for " . $element->InstanceKey . " -->");
						$oms->executeMethod($this->Context, $pru);
						return true;
					}
					else
					{
						echo ("<!-- pru is null for " . $element->InstanceKey . " -->");
					}
						
					$contents = $oms->getRelatedInstances($element, KnownRelationshipGuids::Element__has__Element_Content);
					foreach ($contents as $content)
					{
						$contentInstance = $oms->getRelatedInstance($content, KnownRelationshipGuids::Element_Content__has__Instance);
						if ($contentInstance == null)
						{
							echo ("<!-- contentInstance is null for " . $content->InstanceKey . " -->");
							continue;
						}

						$pc = $oms->getParentClass($contentInstance);
						if ($pc->GlobalIdentifier->__is_equal(UUID::parse(KnownClassGuids::Element)))
						{
							echo("<!-- executing sub PRU -->");
							$this->processPostback($contentInstance);
						}
					}
					/*

					// process updates
					foreach ($ecs as $key => $value)
					{
						$inst_ec = $oms->getInstanceByKey(InstanceKey::Parse($key));
						if (!$this->processUpdatesForElementContent($inst_ec, $value))
						{
							return false;
						}
					}
					*/
				}
			}
			return true;
		}

		/**
		 * @return true if the validation passes; false otherwise
		 */
		private function checkValidationForElementContent(InstanceReference $elementContent, string $value, InstanceReference $validation)
		{
			return true;
		}

		/**
		 * @return true if the validations pass (or there are no validations); false otherwise.
		 */
		private function checkValidationsForElementContent(InstanceReference $elementContent, string $value)
		{
			/**
			 * @var MySQLDatabaseOms
			 */
			$oms = mocha_get_oms();

			$displayOptions = $oms->getRelatedInstances($elementContent, $oms->getInstanceByGlobalIdentifier(KnownRelationshipGuids::Element_Content__has__Element_Content_Display_Option));
			if ($oms->instanceSetContains($displayOptions, $oms->getInstanceByGlobalIdentifier(KnownInstanceGuids::DisplayOption__Required)))
			{
				// this is a required parm
				if ($value == null)
				{
					$this->FailedValidations[] = new ValidationResult(null, "Missing required parameter", $oms->getAttributeValue($elementContent, KnownAttributeGuids::Label));
					$this->FailedValidationElements[] = $elementContent;
					return false;
				}
			}

			$validations = $oms->getRelatedInstances($elementContent, KnownRelationshipGuids::Element_Content__has__Validation);
			foreach ($validations as $validation)
			{
				if (!$this->checkValidationForElementContent($elementContent, $value, $validation))
				{
					$this->FailedValidations[] = new ValidationResult($validation, null, null);
					$this->FailedValidationElements[] = $elementContent;
					return false;
				}
			}
			return true;
		}

		private function processUpdatesForElementContent(InstanceReference $elementContent, string $value)
		{
			/**
			 * @var MySQLDatabaseOms
			 */
			$oms = mocha_get_oms();

			/*
			if (!$oms->getAttributeValue($inst_ec, KnownAttributeGuids::DoNotUpdateOnSubmit))
			{
			*/
			$inst_target = $oms->getRelatedInstance($elementContent, KnownRelationshipGuids::Element_Content__has__Instance);
			
			/*
			$value = $oms->getAttributeValue($this->TargetInstance, $ecInst);
			if ($value === null)
			{
				$value = $oms->getAttributeValue($inst_target, $oms->getInstanceByGlobalIdentifier(KnownAttributeGuids::Value));
			}
			*/
			
			$oms->setAttributeValue($inst_target, KnownAttributeGuids::Value, $value);
			/*
			}
			*/
			return true;
		}

		public function shouldRenderElementContentLabel(InstanceReference $elementContent)
		{
			/**
			 * @var MySQLDatabaseOms
			 */
			$oms = mocha_get_oms();

			$displayOptions = $oms->getRelatedInstances($elementContent, $oms->getInstanceByGlobalIdentifier(KnownRelationshipGuids::Element_Content__has__Element_Content_Display_Option));
			
			if ($oms->instanceSetContains($displayOptions, $oms->getInstanceByGlobalIdentifier(KnownInstanceGuids::DisplayOption__WideText)))
			{
				return false;
			}
			$ecLayout = $oms->getRelatedInstance($elementContent, $oms->getInstanceByGlobalIdentifier(KnownRelationshipGuids::Element_Content__has__Layout));
			if ($ecLayout !== null)
			{
				$ecLayoutParentClass = $oms->getParentClass($ecLayout);
				if ($ecLayoutParentClass !== null)
				{
					if (
						($ecLayoutParentClass->GlobalIdentifier->__is_equal(UUID::parse(KnownClassGuids::ButtonLayout)))
						|| ($ecLayoutParentClass->GlobalIdentifier->__is_equal(UUID::parse(KnownClassGuids::ImageLayout)))
					)
					{
						return false;
					}
				}
			}
			return true;
		}
		
		public function shouldRenderElementContent(InstanceReference $elementContent)
		{
			/**
			 * @var MySQLDatabaseOms
			 */
			$oms = mocha_get_oms();
			if ($this->Context->getElementParm($elementContent, "visible", false))
			{
				return true;
			}

			$displayOptions = $oms->getRelatedInstances($elementContent, $oms->getInstanceByGlobalIdentifier(KnownRelationshipGuids::Element_Content__has__Element_Content_Display_Option));
			if ($oms->instanceSetContains($displayOptions, $oms->getInstanceByGlobalIdentifier(KnownInstanceGuids::DisplayOption__DoNotShow)))
				return false;

			if (!$this->Context->getElementParm($elementContent, "visible", true))
			{
				return false;
			}

			return true;
		}

		public function buildStyleAttribute(bool $isBlockElement, array $constantStyles, array $stylesFromInstanceSet)
		{
			/**
			 * @var MySQLDatabaseOms
			 */
			$oms = mocha_get_oms();
			
			$str = "";
			foreach ($constantStyles as $key => $value)
			{
				$str .= $key . ": " . $value . ";";
			}
			foreach ($stylesFromInstanceSet as $ir)
			{
				# $styleHasStyleRule = $oms->getRelatedInstances($ir, KnownRelationshipGuids::Style__has__Style_Rule);
				$styleHasWidthAttr = $oms->getRelatedInstance($ir, KnownRelationshipGuids::Style__has_width__Measurement);
				if ($styleHasWidthAttr !== null)
				{
					$value = $oms->getAttributeValue($styleHasWidthAttr, KnownAttributeGuids::Value);
					$unit = $oms->getRelatedInstance($styleHasWidthAttr, KnownRelationshipGuids::Measurement__has__Measurement_Unit);
					$unitValue = $oms->getAttributeValue($unit, KnownAttributeGuids::CSSValue);
					$str .= "width: " . $value . $unitValue . ";";
				}
				$styleHasHeightAttr = $oms->getRelatedInstance($ir, KnownRelationshipGuids::Style__has_height__Measurement);
				if ($styleHasHeightAttr !== null)
				{
					$value = $oms->getAttributeValue($styleHasHeightAttr, KnownAttributeGuids::Value);
					$unit = $oms->getRelatedInstance($styleHasHeightAttr, KnownRelationshipGuids::Measurement__has__Measurement_Unit);
					$unitValue = $oms->getAttributeValue($unit, KnownAttributeGuids::CSSValue);
					$str .= "height: " . $value . $unitValue . ";";
				}
				$styleHasHorizontalAlignment = $oms->getRelatedInstance($ir, KnownRelationshipGuids::Style__has_horizontal__Alignment);
				if ($styleHasHorizontalAlignment !== null)
				{
					if ($isBlockElement)
					{
						if ($styleHasHorizontalAlignment->GlobalIdentifier->__is_equal(UUID::parse(KnownInstanceGuids::Alignment__Near)))
						{
							$str .= "margin-right: auto;";
						}
						else if ($styleHasHorizontalAlignment->GlobalIdentifier->__is_equal(UUID::parse(KnownInstanceGuids::Alignment__Center)))
						{
							$str .= "margin-left: auto; margin-right: auto;";
						}
						else if ($styleHasHorizontalAlignment->GlobalIdentifier->__is_equal(UUID::parse(KnownInstanceGuids::Alignment__Far)))
						{
							$str .= "margin-left: auto;";
						}
					}
					else
					{
						if ($styleHasHorizontalAlignment->GlobalIdentifier->__is_equal(UUID::parse(KnownInstanceGuids::Alignment__Near)))
						{
							$value = "left";
						}
						else if ($styleHasHorizontalAlignment->GlobalIdentifier->__is_equal(UUID::parse(KnownInstanceGuids::Alignment__Center)))
						{
							$value = "center";
						}
						else if ($styleHasHorizontalAlignment->GlobalIdentifier->__is_equal(UUID::parse(KnownInstanceGuids::Alignment__Far)))
						{
							$value = "right";
						}
						$str .= "text-align: " . $value . ";";
					}
				}
				$styleHasVerticalAlignment = $oms->getRelatedInstance($ir, KnownRelationshipGuids::Style__has_vertical__Alignment);
				if ($styleHasVerticalAlignment !== null)
				{
					if ($styleHasVerticalAlignment->GlobalIdentifier->__is_equal(UUID::parse(KnownInstanceGuids::Alignment__Near)))
					{
						$value = "top";
					}
					else if ($styleHasVerticalAlignment->GlobalIdentifier->__is_equal(UUID::parse(KnownInstanceGuids::Alignment__Center)))
					{
						$value = "center";
					}
					else if ($styleHasVerticalAlignment->GlobalIdentifier->__is_equal(UUID::parse(KnownInstanceGuids::Alignment__Far)))
					{
						$value = "bottom";
					}
					$str .= "vertical-align: " . $value . ";";
				}
			}
			return $str;
		}

		public function renderElementContent(InstanceReference $elementContent, $renderNotEnterable = false)
		{
			/**
			 * @var MySQLDatabaseOms
			 */
			$oms = mocha_get_oms();
			
			$ecInst = $oms->getRelatedInstance($elementContent, $oms->getInstanceByGlobalIdentifier(KnownRelationshipGuids::Element_Content__has__Instance));
			$relatedInstance = $this->Context->getWorkData(KnownInstanceGuids::WorkSet__RelatedInstance);
			echo ("<!-- " . $elementContent->InstanceKey . " ; " . $ecInst->InstanceKey . " -->");

			$ecLayout = $oms->getRelatedInstance($elementContent, $oms->getInstanceByGlobalIdentifier(KnownRelationshipGuids::Element_Content__has__Layout));
			if ($ecLayout !== null)
			{
				$ecStyles = $oms->getRelatedInstances($ecLayout, KnownRelationshipGuids::Layout__has__Style);

				$ecLayoutParentClass = $oms->getParentClass($ecLayout);
				if ($ecLayoutParentClass !== null)
				{
					if ($ecLayoutParentClass->GlobalIdentifier->__is_equal(UUID::parse(KnownClassGuids::ButtonLayout)))
					{
						// override layout : button
						echo ("<a class=\"uwt-button\" href=\"");
						$executesTaskFromIS = $oms->getRelatedInstance($ecLayout, KnownRelationshipGuids::Button_Layout__executes_task_from__Instance_Set);
						if ($executesTaskFromIS !== null)
						{
							echo(System::ExpandRelativePath("~/d/task/" . $executesTaskFromIS->InstanceKey . ".htmld"));
						}
						echo("\">");
						//
						// 3$7872		Button Layout.uses for label Text Attribute 
						// 3$7873		Text Attribute.label for Button Layout 
						// 
						// TODO: get Translatable label text from EC
						// $attrLabel = $oms->getRelatedInstance($ecLayout, KnownRelationshipGuids::Button_Layout__uses_for_label__Text_Attribute);
						// if ($attrLabel !== null)
						// {
						// }
						$attrLabel = $oms->getAttributeValue($elementContent, KnownAttributeGuids::Label, "NO_LABEL_" . $elementContent->InstanceKey);
						echo ($attrLabel);
						echo ("</a>");
					}
					else if ($ecLayoutParentClass->GlobalIdentifier->__is_equal(UUID::parse(KnownClassGuids::ImageLayout)))
					{
						// override layout : image
						echo ("<div class=\"uwt-image\" style=\"");

						// EC instance should be of type `File`
						$attrContentType = $oms->getAttributeValue($ecInst, KnownAttributeGuids::ContentType);
						$attrContentEncoding = $oms->getAttributeValue($ecInst, KnownAttributeGuids::Encoding);
						$attrValue = $oms->getAttributeValue($ecInst, KnownAttributeGuids::Value);

						echo ($this->buildStyleAttribute(true, [
							"background-image" => "url('" . $oms->getAttachmentUrl($ecInst) . "')"
							/*"url('data:" . $attrContentType . ";" . $attrContentEncoding . "," . $attrValue . "')"*/
						], $ecStyles));
						echo("\">&nbsp;</div>");
					}
				}
			}
			else
			{
				// default layout
				if ($ecInst === null)
				{
				}
				else
				{
					if ($this->shouldRenderElementContent($elementContent))
					{
						$displayOptions = $oms->getRelatedInstances($elementContent, $oms->getInstanceByGlobalIdentifier(KnownRelationshipGuids::Element_Content__has__Element_Content_Display_Option));
						
						if ($oms->is_a($ecInst, KnownClassGuids::Element))
						{
							$this->renderElement($ecInst, $displayOptions, $renderNotEnterable);
						}
						else if ($oms->is_a($ecInst, KnownClassGuids::Relationship))
						{
							$value = $oms->getRelatedInstances($relatedInstance, $ecInst);

							$adw = new InstanceBrowser();
							$adw->Name = "ib_" . $elementContent->InstanceKey;
							$adw->SelectedInstances = $value;

							if (!$oms->instanceSetContains($displayOptions, $oms->getInstanceByGlobalIdentifier(KnownInstanceGuids::DisplayOption__NotEnterable)))
							{
								$adw->Editable = true;
							}
							else
							{
								$adw->Editable = false;
							}
							$adw->Render();
						}
						// Executable returning Attribute
						else if ($oms->is_a($ecInst, KnownClassGuids::TextAttribute))
						{
							$this->renderExecutableReturningAttribute($elementContent, $ecInst, $relatedInstance, $renderNotEnterable);
						}
						else if ($oms->is_a($ecInst, KnownClassGuids::BooleanAttribute))
						{
							$this->renderExecutableReturningAttribute($elementContent, $ecInst, $relatedInstance, $renderNotEnterable);
						}
						// Executable returning Attribute
						else if ($oms->is_a($ecInst, KnownClassGuids::ReturnAttributeMethodBinding))
						{
							// I *THINK* Return Attribute Method Bindings aren't supposed to return
							// just strings, they're supposed to actually return *attributes* that
							// can then be edited in the Element Content.
							$context = new OmsContext();
							$rambAttribute = $oms->executeMethod($context, $ecInst);
							if ($rambAttribute !== null)
							{
								$this->renderTextAttribute($context, $rambAttribute);
							}
							else
							{
								echo ("ERR: attribute ( from method " . $ecInst->InstanceKey . " ) is null");
							}
						}
						else if ($oms->is_a($ecInst, KnownClassGuids::Clasz))
						{
							$targetInstance = $ecInst;
							if ($this->Context !== null)
							{
								$targetInstance = $this->Context->getWorkData($ecInst);
							}

							if ($targetInstance !== null)
							{
								$adw = new InstanceBrowser(array($targetInstance));
								$adw->Render();
							}
							else
							{
								echo ("<span class=\"uwt-disabled\">(empty)</span>");
							}
						}
						else
						{
							$adw = new InstanceBrowser(array($ecInst));
							$adw->Render();
							// echo ("<span class=\"mcx-value\">unknown pclass ident " . $oms->getParentClass($ecInst)->GlobalIdentifier . "</span>");
						}
						echo ("</td>");
					}
				}
			}
		}

		public function renderExecutableReturningAttribute(InstanceReference $elementContent, InstanceReference $ecInst, ?InstanceReference $relatedInstance, $renderNotEnterable = false)
		{
			/**
			 * @var MySQLDatabaseOms
			 */
			$oms = mocha_get_oms();

			$displayOptions = $oms->getRelatedInstances($elementContent, KnownRelationshipGuids::Element_Content__has__Element_Content_Display_Option);
			
			// fill in the value from the target instance
			$targetInstance = $relatedInstance;
			if ($targetInstance === null)
			{
				$targetInstance = $this->TargetInstance;

				$firstElementContent = $oms->getNthRelatedInstance($this->__renderingElement, KnownRelationshipGuids::Element__has__Element_Content, 0);
				$firstElementContentInst = $oms->getRelatedInstance($firstElementContent, KnownRelationshipGuids::Element_Content__has__Instance);

				$targetInstance = $oms->getNthInstanceOf($firstElementContentInst, 1);
			}
			if ($targetInstance !== null)
			{
				$value = $oms->getAttributeValue($targetInstance, $ecInst);
			}
			
			if ($value === null)
			{
				$value = $oms->getAttributeValue($ecInst, $oms->getInstanceByGlobalIdentifier(KnownAttributeGuids::Value));
			}

			if ($this->IsPostback && isset($_POST["ec_" . strval($elementContent->InstanceKey)]))
			{
				// fill in the value from the postback
				$value = $_POST["ec_" . strval($elementContent->InstanceKey)];
			}

			if ($oms->instanceSetContains($displayOptions, $oms->getInstanceByGlobalIdentifier(KnownInstanceGuids::DisplayOption__NotEnterable)) || $renderNotEnterable)
			{
				echo($value);
			}
			else
			{
				if ($oms->is_a($ecInst, KnownClassGuids::TextAttribute))
				{
					$this->renderTextAttributeEC($elementContent, $ecInst, $oms->instanceSetContains($displayOptions, $oms->getInstanceByGlobalIdentifier(KnownInstanceGuids::DisplayOption__ObscuredText)), $value);
				}
				else if ($oms->is_a($ecInst, KnownClassGuids::BooleanAttribute))
				{
					$this->renderBooleanAttributeEC($elementContent, $ecInst, $oms->instanceSetContains($displayOptions, $oms->getInstanceByGlobalIdentifier(KnownInstanceGuids::DisplayOption__ObscuredText)), $value);
				}
			}
		}

		public function renderTextAttribute(OmsContext $context, InstanceReference $attr)
		{
			// $adw = new InstanceBrowser(array($rambAttribute));
			// $adw->Render();
			echo($context->getWorkData($attr));
		}

		public function renderTextAttributeEC(InstanceReference $ec, InstanceReference $inst, $password = false, $value = null)
		{
			/**
			 * @var MySQLDatabaseOms
			 */
			$oms = mocha_get_oms();

			$displayOptions = $oms->getRelatedInstances($ec, $oms->getInstanceByGlobalIdentifier(KnownRelationshipGuids::Element_Content__has__Element_Content_Display_Option));
			
			$ecid = $ec->InstanceKey;

			$p = new \Phast\WebControl();
			$p->TagName = "input";
			$p->ClientID = "ec_" . $ecid;
			$p->Attributes[] = new \Phast\WebControlAttribute("name", "ec_" . $ecid);
			$p->Attributes[] = new \Phast\WebControlAttribute("data-ecid", $ecid);
			$p->Attributes[] = new \Phast\WebControlAttribute("value", $value);

			if ($oms->instanceSetContains($displayOptions, $oms->getInstanceByGlobalIdentifier(KnownInstanceGuids::DisplayOption__Required)))
			{
				$p->ClassList[] = "uwt-required";
			}
			if ($this->IsPostback && $oms->instanceSetContains($this->FailedValidationElements, $ec))
			{
				$p->ClassList[] = "uwt-failed-validation";
			}

			$maxLength = $inst->getAttributeValue(KnownAttributeGuids::MaximumLength);
			if ($maxLength !== null)
			{
				$p->Attributes[] = new \Phast\WebControlAttribute("maxlength", $maxLength);
			}
			$p->HasContent = false;
			$attType = new \Phast\WebControlAttribute("type", "text");
			if ($password)
			{
				$attType->Value = "password";
			}
			$p->Attributes[] = $attType;
			$p->Render();
		}
		
		public function renderBooleanAttributeEC(InstanceReference $ec, InstanceReference $inst, $password = false, $value = null)
		{
			/**
			 * @var MySQLDatabaseOms
			 */
			$oms = mocha_get_oms();

			$displayOptions = $oms->getRelatedInstances($ec, $oms->getInstanceByGlobalIdentifier(KnownRelationshipGuids::Element_Content__has__Element_Content_Display_Option));
			
			$ecid = $ec->InstanceKey;

			$p = new \Phast\WebControl();
			$p->TagName = "input";
			$p->ClientID = "ec_" . $ecid;
			$p->Attributes[] = new \Phast\WebControlAttribute("name", "ec_" . $ecid);
			$p->Attributes[] = new \Phast\WebControlAttribute("data-ecid", $ecid);
			$p->Attributes[] = new \Phast\WebControlAttribute("value", $value);
			$p->Attributes[] = new \Phast\WebControlAttribute("type", "checkbox");

			if ($oms->instanceSetContains($displayOptions, $oms->getInstanceByGlobalIdentifier(KnownInstanceGuids::DisplayOption__Required)))
			{
				$p->ClassList[] = "uwt-required";
			}
			if ($this->IsPostback && $oms->instanceSetContains($this->FailedValidationElements, $ec))
			{
				$p->ClassList[] = "uwt-failed-validation";
			}

			$maxLength = $inst->getAttributeValue(KnownAttributeGuids::MaximumLength);
			if ($maxLength !== null)
			{
				$p->Attributes[] = new \Phast\WebControlAttribute("maxlength", $maxLength);
			}
			$p->HasContent = false;
			$attType = new \Phast\WebControlAttribute("type", "text");
			if ($password)
			{
				$attType->Value = "password";
			}
			$p->Attributes[] = $attType;
			$p->Render();
		}
		
		public function renderArray(?array $array, string $separator, string $prefix = "", string $suffix = "")
		{
			if ($array === null)
				return "";
			if (\count($array) > 0)
			{
				return $prefix . \implode($separator, $array) . $suffix;
			}
			return "";
		}
		
		public function renderKeyedArray(?array $array, string $separator, string $prefix = "", string $suffix = "", string $partSeparator = "", string $keyPrefix = "", string $keySuffix = "", string $valuePrefix = "", string $valueSuffix = "")
		{
			if ($array === null)
				return "";

			if (\count($array) > 0)
			{
				$str = $prefix;
				foreach ($array as $key => $value)
				{
					$str .= $keyPrefix . $key . $keySuffix;
					$str .= $partSeparator;
					$str .= $valuePrefix . $value . $valueSuffix;
					$str .= $separator;
				}
				$str .= $suffix;
				return $str;
			}
			return "";
		}

		private Stopwatch $_stopwatch;

		public function renderBeginPage($title = "")
		{
			/**
			 * @var MySQLDatabaseOms
			 */
			$oms = mocha_get_oms();

			$metaCharset = "UTF-8";
			$metaAttributes = [];
			$metaAttributes["viewport"] = "width=device-width, initial-scale=1.0";

			echo ("<!DOCTYPE html>");
			$this->renderBeginTag("html", array("xmlns" => "http://www.w3.org/1999/xhtml"));
			$this->renderBeginTag("head");
			echo ("<meta charset=\"" . $metaCharset . "\" />");
			foreach ($metaAttributes as $key => $value)
			{
				echo ("<meta name=\"" . $key . "\" content=\"" . $value . "\" />");
			}

			$this->renderBeginTag("title");
			echo($title);
			$this->renderEndTag("title");

			$this->renderTag("link", array
			(
				"rel" => "stylesheet",
				"type" => "text/css",
				"href" => System::ExpandRelativePath("~/themes/" . System::GetConfigurationValue("Application.ThemeName") . "/theme.css", false, true)
			));
			
			$this->renderTag("link", array
			(
				"rel" => "stylesheet",
				"type" => "text/css",
				"href" => System::ExpandRelativePath("~/themes/mocha/theme.css", false, true)
			));

			$this->renderTag("script", array("type" => "text/javascript", "src" => System::ExpandRelativePath("~/scripts/phast/System.js.php", false, true)));
			$this->renderTag("script", array("type" => "text/javascript", "src" => System::ExpandRelativePath("~/scripts/mocha.js.php", false, true)));
			$this->renderBeginTag("script", array("type" => "text/javascript"));
			echo("System.BasePath = '" . System::GetConfigurationValue("Application.BasePath") . "';");
			echo("System.IsTenantedHostingEnabled = function() { return false; };");
			echo("System.TenantName = '" . $oms->getTenantName() . "';");
			$this->renderEndTag("script");
			$this->renderEndTag("head");
			$bodyClasses = [ "uwt-header-visible", "uwt-header-inverse" ];
			foreach ($this->StyleClasses as $className)
			{
				$bodyClasses[] = $className;
			}
			if ($this->IsPostback)
			{
				$bodyClasses[] = "uwt-postback";
			}
			$this->renderBeginTag("body", null, $bodyClasses);

			$spotTimerContent = ""; $spotTimerScript = "";
			\mocha_get_spot_timer_script($spotTimerContent, $spotTimerScript);
			echo(<<<MSG_EOF
			<div class="mocha-ams-spot-popup" id="spot_popup"><h1>SPOT:</h1><h3>Stop in: <label id="spot_timer">$spotTimerContent</label></h3></div>
MSG_EOF);
			$this->renderTag("script", [ ], [ ], $spotTimerScript);

			if ($this->IncludeTopNavigationBar)
			{
				$this->renderTopNavigationBar();
			}

			echo("<form method=\"POST\">");
		}
		public function renderEndPage()
		{
			/**
			 * @var MySQLDatabaseOms
			 */
			$oms = mocha_get_oms();

			echo ("</form>");

			echo("<div class=\"uwt-alert-container\">");
			foreach ($oms->Messages as $msg)
			{
				$ik = "";
				echo("<div class=\"uwt-alert uwt-alert-sticky uwt-color-danger\" data-instance-id=\"" . $ik . "\">");
				echo("<div class=\"uwt-title\">" . $msg->Title . "</div>");
				echo("<div class=\"uwt-content\">" . str_replace("\n", "<br />", $msg->Message) . "</div>");
				echo("</div>");
			}
			if ($this->IsPostback)
			{
				if (count($this->FailedValidations) > 0)
				{
					foreach ($this->FailedValidations as $validation)
					{
						$ik = "";
						echo("<div class=\"uwt-alert uwt-color-danger\" data-instance-id=\"" . $ik . "\">");
						echo("<div class=\"uwt-title\">" . $validation->Title . "</div>");
						echo("<div class=\"uwt-content\">" . str_replace("\n", "<br />", $validation->Message) . "</div>");
						echo("</div>");
					}
				}
				/*
				else
				{
					echo("<div class=\"uwt-alert uwt-color-success\">");
					echo("<div class=\"uwt-title\">Saved</div>");
					echo("</div>");
				}
				*/
			}
			echo("</div>");

			$this->renderEndTag("body");
			$this->renderEndTag("html");

			$this->_stopwatch->stop();
			$this->logRequestSuccess($this->_stopwatch->getElapsedTime());
		}

		public function renderInitialElement(InstanceReference $element)
		{
			/**
			 * @var MySQLDatabaseOms
			 */
			$oms = mocha_get_oms();

			$this->_stopwatch = new Stopwatch();
			$this->_stopwatch->start();
			
			$applicationTitle = "Mocha"; // TOOD: get tenant application title
			$pageTitle = null;
			$pageTitle = $oms->getAttributeValue($element, KnownAttributeGuids::Label);

			if ($pageTitle !== null && $applicationTitle !== null)
			{
				$title = $pageTitle . " - " . $applicationTitle;
			}
			else if ($pageTitle !== null)
			{
				$title = $pageTitle;
			}
			else if ($applicationTitle !== null)
			{
				$title = $applicationTitle;
			}
			$this->renderBeginPage($title);
			
			$DO_Singular = $oms->getInstanceByGlobalIdentifier(KnownInstanceGuids::DisplayOption__Singular);
			$this->renderElement($element, [ $DO_Singular ]);
			$this->renderEndForm();

			$this->renderEndPage();
		}

		private function renderEndForm()
		{
			echo("<div class=\"uwt-page-footer\">");
			// echo ("<a class=\"uwt-button\" href=\"#\" onclick=\"history.back(); return false;\">Cancel</a>");
			echo ("<a class=\"uwt-button\" href=\"" . System::ExpandRelativePath("~~/" . System::GetTenantName()) . "\" " . /* onclick=\"history.back(); return false;\" */ ">Cancel</a>");
			echo ("<input class=\"uwt-button uwt-color-primary\" type=\"submit\" value=\"" . $this->SubmitButtonText . "\" />");
			echo("</div>");
		}

		public function logRequestSuccess(float $elapsedTime)
		{
			echo("\n<!--\nSUCCESS!\n\n" .
			"request: " . $_SERVER["REQUEST_METHOD"] . " " . $_SERVER["REQUEST_URI"] . "\n" .
			"ip address: " . $_SERVER["REMOTE_ADDR"] . "\n" .
			"render time: " . $elapsedTime . " ms\n" .
			"\n-->");
		}

		public function renderTag($tagName, ?array $attributeList = null, ?array $classList = null, $content = null)
		{
			$canBeEmpty = true;
			if ($tagName == "script")
			{
				$canBeEmpty = false;
			}

			if ($canBeEmpty && $content === null)
			{
				echo ("<" . $tagName . $this->renderArray($classList, " ", " class=\"", "\"") . $this->renderKeyedArray($attributeList, " ", " ", "", "=", "", "", "\"", "\"") . " />");
			}
			else
			{
				$this->renderBeginTag($tagName, $attributeList, $classList);
				if ($content !== null)
				{
					echo($content);
				}
				$this->renderEndTag($tagName);
			}
		}

		public function renderBeginTag($tagName, ?array $attributeList = null, ?array $classList = null)
		{
			echo ("<" . $tagName . $this->renderArray($classList, " ", " class=\"", "\"") . $this->renderKeyedArray($attributeList, " ", " ", "", "=", "", "", "\"", "\"") . ">");
		}
		public function renderEndTag($tagName)
		{
			echo ("</" . $tagName . ">");
		}

		private function get__EC_Override_Label_or_default_Instance_Title(array $parms)
		{
			/**
			 * @var MySQLDatabaseOms
			 */
			$oms = mocha_get_oms();

			$ecInst = $parms[KnownClassGuids::Instance];
			$elementContent = $parms[KnownClassGuids::ElementContent];
			
			$title = $oms->getAttributeValue($ecInst, $oms->getInstanceByGlobalIdentifier(KnownAttributeGuids::Name));
			$label = $oms->getAttributeValue($elementContent, $oms->getInstanceByGlobalIdentifier(KnownAttributeGuids::Label));
			
			//!HACK
			if ($ecInst->InstanceKey->ClassIndex == 6)
			{
				// ELements should not have a title
				$title = "";
			}
			if ($label !== null)
			{
				$title = $label;
			}
			//$title = "TODO:call Instance@get Title [ramb]";
			return $title;
		}

		private InstanceReference $__renderingElement;

		public function renderElement(InstanceReference $element, array $displayOptions, $renderNotEnterable = false)
		{
			/**
			 * @var MySQLDatabaseOms
			 */
			$oms = mocha_get_oms();

			$this->__renderingElement = $element;
			$parentElementContent = $oms->getRelatedInstance($element, KnownRelationshipGuids::Instance__for__Element_Content);
			
			$renderingNotEnterable =  $renderNotEnterable || $oms->instanceSetContains($displayOptions, $oms->getInstanceByGlobalIdentifier(KnownInstanceGuids::DisplayOption__NotEnterable));

			if ($oms->instanceSetContains($displayOptions, $oms->getInstanceByGlobalIdentifier(KnownInstanceGuids::DisplayOption__ShowSubelementsVertically)))
			{
				echo("<!-- show subelements vertically -->");
				$elementLabel = $oms->getAttributeValue($element, KnownAttributeGuids::Label);
				if ($elementLabel !== null)
				{
					echo("<h2>" . $elementLabel . "</h2>");
				}
				
				# $this->renderBeginTag("table", [ "data-instance-id" => $element->InstanceKey ], [ "uwt-formview", "mcx-element" ]);
				$this->renderBeginTag("div", [ "data-instance-id" => $element->InstanceKey ], [ "uwt-formview", "mcx-element" ]);

				$contents = $oms->getRelatedInstances($element, $oms->getInstanceByGlobalIdentifier(KnownRelationshipGuids::Element__has__Element_Content));
				if (count($contents) > 0)
				{
					$ecInst0 = $oms->getRelatedInstance($contents[0], KnownRelationshipGuids::Element_Content__has__Instance);
					if ($ecInst0 !== null)
					{
						$wd = $this->Context->getWorkData($ecInst0);
						if ($wd !== null)
						{
							$this->Context->setWorkData(KnownInstanceGuids::WorkSet__RelatedInstance, $wd);
						}
					}
				}

				$qs = new QuickSort();
				$sortedElementContents = $qs->Sort_By($contents, function($item)
				{
					/**
					 * @var MySQLDatabaseOms
					 */
					$oms = mocha_get_oms();

					$order = $oms->getAttributeValue($item, KnownAttributeGuids::Order);
					return $order;
				});
				
				foreach ($sortedElementContents as $elementContent)
				{
					if ($elementContent === null)
						continue;

					if (!$this->shouldRenderElementContent($elementContent))
					{
						continue;
					}
					
					$ecInst = $oms->getRelatedInstance($elementContent, KnownRelationshipGuids::Element_Content__has__Instance);
					$mcx_classes = [ "mcx-elementcontent" ];
					if ($ecInst->InstanceKey->ClassIndex == 6)
					{
						$mcx_classes = [ "mcx-element" ];
					}
					if (!$this->shouldRenderElementContentLabel($elementContent))
					{
						$mcx_classes[] = "mcx-hidden-label";
					}

					if ($oms->instanceSetContains($displayOptions, $oms->getInstanceByGlobalIdentifier(KnownInstanceGuids::DisplayOption__Required)))
					{
						$mcx_classes[] = "mcx-required";
					}
					if ($this->IsPostback && $oms->instanceSetContains($this->FailedValidationElements, $elementContent))
					{
						$mcx_classes[] = "mcx-failed-validation";
					}
					if (!$this->shouldRenderElementContentLabel($elementContent))
					{
						$mcx_classes[] = "mcx-hidden-label";
					}
					
					// $this->renderBeginTag("tr", [ "data-instance-id" => $ecInst->InstanceKey, "data-ecid" => $elementContent->InstanceKey ], $mcx_classes);
					$this->renderBeginTag("div", [ "data-instance-id" => $ecInst->InstanceKey, "data-ecid" => $elementContent->InstanceKey ], $mcx_classes);

					$title = $this->get__EC_Override_Label_or_default_Instance_Title(array
					(
						KnownClassGuids::Instance => $ecInst,
						KnownClassGuids::ElementContent => $elementContent
					));

					if ($this->shouldRenderElementContentLabel($elementContent))
					{
						// $this->renderBeginTag("td");
						// $this->renderEndTag("td");
						// $this->renderBeginTag("td");
					}
					else
					{
						// $this->renderBeginTag("td", [ "colspan" => "2" ]);
					}
					echo ("<label for=\"ec_" . $elementContent->InstanceKey . "\">" . $title . "</label>");

					$this->renderBeginTag("div");
					$this->renderElementContent($elementContent, $renderingNotEnterable);
					$this->renderEndTag("div");

					// $this->renderEndTag("td");
					
					// $this->renderEndTag("tr");
					$this->renderEndTag("div");
				}
				// $this->renderEndTag("table");
				$this->renderEndTag("div");
			}
			else
			{
				if ($oms->instanceSetContains($displayOptions, $oms->getInstanceByGlobalIdentifier(KnownInstanceGuids::DisplayOption__Singular)))
				{
					echo("<!-- singular -->");
					$contents = $oms->getRelatedInstances($element, KnownRelationshipGuids::Element__has__Element_Content);
					foreach ($contents as $content)
					{
						$this->renderElementContent($content);
					}
				}
				else
				{
					$title = $oms->getAttributeValue($element, KnownAttributeGuids::Name);
					$label = $oms->getAttributeValue($element, KnownAttributeGuids::Label);
					
					if ($label !== null)
					{
						$title = $label;
					}

					$contents = $oms->getRelatedInstances($element, $oms->getInstanceByGlobalIdentifier(KnownRelationshipGuids::Element__has__Element_Content));

					$instances = [];
					// $bem = $oms->getRelatedInstance($element, KnownRelationshipGuids::Element__returned_by__Build_Element_Method);
					//$bemProcess = $oms->getRelatedInstance($bem, KnownRelationshipGuids::Build_Element_Method__has__BEM_Process);
					if ($parentElementContent !== null)
					{
						$bemProcess = $oms->getRelatedInstance($parentElementContent, KnownRelationshipGuids::Element_Content__built_from__BEM_Process);
						if ($bemProcess != null)
						{
							// execute the `Executable returning Instance Set` to get the rows to display (Primary Instances)
							$instanceExecutable = $oms->getRelatedInstance($bemProcess, KnownRelationshipGuids::BEM_Process__uses_loop__Executable_returning_Instance_Set);
							if ($instanceExecutable !== null)
							{
								//! FIXME: this array should be put into teh specified `returns Work Set` variable
								// ... BUT this works the way it's supposed to , for right now!
								$instances = $oms->executeMethodReturningInstanceSet($instanceExecutable, array());
							}
						}
					}
					$rowCount = count($instances);
					
					echo("<!-- nonsingular -->");
					$this->renderBeginTag("table", [ "data-instance-id" => $element->InstanceKey ], [ "uwt-listview", "mcx-element" ]);

					// $this->renderBeginTag("div", [ "data-instance-id" => $element->InstanceKey ], [ "uwt-listview", "mcx-element", "uwt-listview-view-details", "uwt-gridlines", "uwt-hottracking" ]);

					// $this->renderBeginTag("div", [ ], [ "uwt-listview-caption" ]);
					$this->renderBeginTag("caption", [ ], [ ]);
					
					$this->renderTag("span", [ ], [ "uwt-listview-title" ], $title);
					$this->renderTag("span", [ ], [ "uwt-listview-item-count" ], $rowCount);
					$this->renderTag("span", [ ], [ "uwt-listview-item-count-label" ], "&nbsp;items");
					$this->renderBeginTag("div", [ ], [ "uwt-toolbar", "uwt-listview-controlbox" ]);
					
					// export
					$this->renderBeginTag("a", [ "href" => "#", "title" => "Download" ], [ "uwt-button" ]);
					$this->renderTag("i", [ ], [ "fa", "fa-download"] , "");
					$this->renderEndTag("a");

					// filter
					$this->renderBeginTag("a", [ "href" => "#", "title" => "Filter" ], [ "uwt-button", "uwt-toggle" ]);
					$this->renderTag("i", [ ], [ "fa", "fa-filter"] , "");
					$this->renderEndTag("a");

					// chart
					$this->renderBeginTag("a", [ "href" => "#", "title" => "Chart" ], [ "uwt-button", "uwt-toggle" ]);
					$this->renderTag("i", [ ], [ "fa", "fa-line-chart"] , "");
					$this->renderEndTag("a");

					// columns
					$this->renderBeginTag("a", [ "href" => "#", "title" => "Columns" ], [ "uwt-button", "uwt-toggle" ]);
					$this->renderTag("i", [ ], [ "fa", "fa-columns"] , "");
					$this->renderEndTag("a");

					// expand
					$this->renderBeginTag("a", [ "href" => "#", "title" => "Expand" ], [ "uwt-button", "uwt-toggle" ]);
					$this->renderTag("i", [ ], [ "fa", "fa-expand"] , "");
					$this->renderEndTag("a");

					$this->renderEndTag("div");
					// $this->renderEndTag("div");
					$this->renderEndTag("caption");

					//$this->renderBeginTag("div", [], ["uwt-content"]);

					//$this->renderBeginTag("div", [ ], [ "uwt-listview-column-headers" ]);
					$this->renderBeginTag("thead");
					$this->renderBeginTag("tr");
					foreach ($contents as $content)
					{
						if ($this->shouldRenderElementContent($content))
						{
							$inst = $oms->getRelatedInstance($content, KnownRelationshipGuids::Element_Content__has__Instance);
							$this->renderBeginTag("th");
							//$this->renderBeginTag("div", [], [ "uwt-listview-column-header"]);
							echo ($this->get__EC_Override_Label_or_default_Instance_Title(array
							(
								KnownClassGuids::Instance => $inst,
								KnownClassGuids::ElementContent => $content
							)));
							// $this->renderEndTag("div");
							$this->renderEndTag("th");
						}
					}

					$this->renderEndTag("tr");
					$this->renderEndTag("thead");
					//$this->renderEndTag("div");
	
					//$this->renderBeginTag("div", [], [ "uwt-listview-items" ]);
					$this->renderBeginTag("tbody");

					for ($i = 0; $i < $rowCount; $i++)
					{
						$rowInst = $instances[$i];
						
						$this->Context->setWorkData($oms->normalizeInstanceReference(KnownInstanceGuids::WorkSet__RelatedInstance), $rowInst);
						$this->Context->setWorkData($oms->getParentClass($rowInst), $rowInst);
						
						$this->renderBeginTag("tr", [ ], [ "uwt-listview-item" ]);
						//$this->renderBeginTag("div", [ ], [ "uwt-listview-item" ]);
						foreach ($contents as $content)
						{
							if ($this->shouldRenderElementContent($content))
							{
								$this->renderBeginTag("td");
								//$this->renderBeginTag("div", [], [ "mcx-elementcontent", "uwt-listview-item-column" ]);

								$this->renderElementContent($content);

								//$this->renderEndTag("div");
								$this->renderEndTag("td");
							}
						}
						$this->renderEndTag("tr");
						//$this->renderEndTag("div");

					}

					//$this->renderEndTag("div");
					$this->renderEndTag("tbody");

					//$this->renderEndTag("div");

					$this->renderEndTag("table");
					//$this->renderEndTag("div");
				}
			}
		}


	}
?>