<?php
	namespace Mocha\UI\Pages;
	
	use Mocha\Core\KnownAttributeGuids;
	use Mocha\Core\KnownClassGuids;
	use Mocha\Core\KnownInstanceGuids;
	use Mocha\Core\KnownMethodBindingGuids;
	use Mocha\Core\KnownRelationshipGuids;

	use Mocha\Core\Oms;
	use Mocha\Core\OmsContext;
	
	use Mocha\UI\Renderers\HTML\HTMLRenderer;
	use Mocha\UI\Renderers\HTML\HTMLRenderer2;
	use Phast\CancelEventArgs;
	use Phast\EventArgs;
	use Phast\RenderingEventArgs;
	use Phast\System;
	use Phast\WebPage;

	class LoginPage extends WebPage
	{
		protected function OnInitializing(CancelEventArgs $e)
		{
			$oms = mocha_get_oms();
			
			$tenantName = System::GetTenantName();
			$tenant = $oms->getTenantByName($tenantName);
			if ($tenant == null)
			{
				System::Redirect("~/" . $tenantName, true);
				exit();
			}
			$oms->setTenant($tenant);
			$oms->initializeInstanceCache();
			
			// echo (" login token: " . $_SESSION["user_token_" . System::GetTenantName()]);
			/*
			if (mocha_get_current_user() !== null)
			{
				System::Redirect("~/");
				exit();
			}
*/
			$this->Page->MasterPage->ClassReference->RequireLogin = false;
			return 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);
		}
		
		protected function OnRendering(RenderingEventArgs $re)
		{
			parent::OnRendering($re);
			
			/**
			 * @var Oms
			 */
			$oms = mocha_get_oms();
			
			$pageElement = $oms->getInstanceByGlobalIdentifier(KnownInstanceGuids::Element__LoginPage);
			if ($pageElement === null)
			{
				print ("could not find element 'LoginPage'");
				die();
			}
		
			// _flowExecutionKey=e1s1&1233/wd:Select_All=1&_eventId_validate=1233%2Fwd%3ASelect_All&sessionSecureToken=4735eafa-da24-4566-b7b2-196719eca3ed&clientRequestID=405dd7011a41450792b5af6a219f39e7
			// $response = $oms->processElement($pageElement);
			$context = $oms->createContext();
			$renderer = new HTMLRenderer2($context);
			$renderer->IncludeTopNavigationBar = false;

			$json = $oms->getResponse($pageElement);
			$json = $json["value"];

			$renderer->ProcessPostbackFunction = function($sender, $element, $array)
			{
				$oms = mocha_get_oms();
				if ($array["result"] == "success")
				{
					if (array_key_exists("sessionSecureToken", $array))
					{
						// update token if we have
						$token = $array["sessionSecureToken"];
						$_SESSION["user_token_" . System::GetTenantName()] = $token;
					}

					if ($array["type"] == "redirect")
					{
						// response redirect
						System::Redirect($array["destinationUrl"]);
						return false;
					}
				}
			};

			$renderer->ProcessUpdatesFunction = function($sender, $element)
			{
				/**
				 * @var Oms
				 */
				$oms = mocha_get_oms();
	
				$ec_UserName = $oms->getInstanceByGlobalIdentifier(KnownInstanceGuids::ElementContent__UserNameForLoginPage);
				$ecinst_UserName = $oms->getRelatedInstance($ec_UserName, KnownRelationshipGuids::Element_Content__has__Instance);
				$ec_Password = $oms->getInstanceByGlobalIdentifier(KnownInstanceGuids::ElementContent__PasswordForLoginPage);
				$ecinst_Password = $oms->getRelatedInstance($ec_Password, KnownRelationshipGuids::Element_Content__has__Instance);
	
				// $ct = $oms->getRelatedInstance($element, $oms->getInstanceByGlobalIdentifier(KnownRelationshipGuids::Element__processed_by__Control_Transaction_Method));
				// Login Page@ Login Page Edit(CT)*S
				// uses Build Response Method Binding...
				// 
	
				$userName = $sender->Context->getWorkData($ecinst_UserName); // $_POST["ec_56$4"];
				$password = $sender->Context->getWorkData($ecinst_Password); // $_POST["ec_56$5"];
				
				$mbUser__get__User_for_User_Name_parm = $oms->getInstanceByGlobalIdentifier(KnownMethodBindingGuids::User__get__User_for_User_Name_parm);
				if ($mbUser__get__User_for_User_Name_parm === null)
				{
					echo("`User@get User for User Name parm`: method not found ('" . KnownMethodBindingGuids::User__get__User_for_User_Name_parm . "')");die();
				}

				$instUser = $oms->execute($sender->Context, $mbUser__get__User_for_User_Name_parm);
				$instUser = $sender->Context->getWorkData($instUser);
				
				if ($instUser !== null)
				{
					$passwordSalt = $oms->getAttributeValue($instUser, $oms->getInstanceByGlobalIdentifier(KnownAttributeGuids::PasswordSalt));
					$passwordHashExpected = $oms->getAttributeValue($instUser, $oms->getInstanceByGlobalIdentifier(KnownAttributeGuids::PasswordHash));
					$passwordHashActual = hash('sha512', $password . $passwordSalt);
					
					if ($passwordHashExpected == $passwordHashActual)
					{
						$token = $this->random_str();
	
						// create the instance of `System Account Signon`
						$instLogin = $oms->createInstanceOf($oms->getInstanceByGlobalIdentifier(KnownClassGuids::UserLogin));
						if ($instLogin !== null)
						{
							// FIXME:  these attribute should be defined in the Mocha/ZQ
							// FIXME:  they should be wrapped in a conditional which checks if we are serving in a GDPR compliant region
							// should we be storing this information then? probably not...
							
							$oms->setAttributeValue($instLogin, KnownAttributeGuids::Token, $token);
							$oms->setAttributeValue($instLogin, KnownAttributeGuids::IPAddress, $_SERVER["REMOTE_ADDR"]);
							$oms->assignRelationship($instLogin, $oms->getInstanceByGlobalIdentifier(KnownRelationshipGuids::User_Login__has__User), $instUser);
						}
						else
						{
							echo ("could not create login token");
							die();
						}
						
						$_SESSION["user_token_" . System::GetTenantName()] = $token;
						$oms->setCurrentUser($instUser);
						
						echo ($_SESSION["user_token_" . System::GetTenantName()]); die();
						
						System::RedirectFromLoginPage();
						exit();
					}
					else
					{
						//$this->Page->GetControlByID("literal1")->EnableRender = true;
	
						//System::RedirectToLoginPage(true);
						echo ("invalid user name or password");
						die();
					}
				}
				$ecPasswordMsg = $oms->getInstanceByGlobalIdentifier("684f1e039ecd43d58acadcf5b84c71f8");
				$sender->Context->setElementParm($ecPasswordMsg, "visible", true);
			};

			$renderer->IsPostback = $this->Page->IsPostback;
			$renderer->StyleClasses[] = "mcx-loginpage";
			if ($this->Page->IsPostback)
			{
				$renderer->processPostback($pageElement);
			}
			$renderer->SubmitButtonText = "Log In";
			$renderer->renderResponse($json);

			$re->Cancel = true;
			$re->Handled = true;
			exit();
		}

		protected function OnRendering2(RenderingEventArgs $re)
		{
			parent::OnRendering($re);
			
			/**
			 * @var Oms
			 */
			$oms = mocha_get_oms();

			//mocha_init_spot_timer($this);
			$path =  System::GetVirtualPath();
			$tenantName = "";
			if ($path[0] == "madi")
			{
				$tenantName = $path[2];
			}
			else
			{
				$tenantName = $path[0];
			}

			$tenant = $oms->getTenantByName($tenantName);
			$oms->setTenant($tenant);

			$pageElement = $oms->getInstanceByGlobalIdentifier(KnownInstanceGuids::Element__LoginPage);
			if ($pageElement === null)
			{
				print ("could not find element 'LoginPage'");
				die();
			}

			$context = new OmsContext();
			
			$renderer = new HTMLRenderer($context);
			$renderer->IncludeTopNavigationBar = false;
			
			# $contents = $pageElement->getRelatedInstances($oms->getInstanceByGlobalIdentifier(KnownRelationshipGuids::Element__has__Element_Content));
			$renderer->ProcessUpdatesFunction = function($sender, $element)
			{
				/**
				 * @var Oms
				 */
				$oms = mocha_get_oms();
	
				$ec_UserName = $oms->getInstanceByGlobalIdentifier(KnownInstanceGuids::ElementContent__UserNameForLoginPage);
				$ecinst_UserName = $oms->getRelatedInstance($ec_UserName, KnownRelationshipGuids::Element_Content__has__Instance);
				$ec_Password = $oms->getInstanceByGlobalIdentifier(KnownInstanceGuids::ElementContent__PasswordForLoginPage);
				$ecinst_Password = $oms->getRelatedInstance($ec_Password, KnownRelationshipGuids::Element_Content__has__Instance);
	
				// $ct = $oms->getRelatedInstance($element, $oms->getInstanceByGlobalIdentifier(KnownRelationshipGuids::Element__processed_by__Control_Transaction_Method));
				// Login Page@ Login Page Edit(CT)*S
				// uses Build Response Method Binding...
				// 
				
				$userName = $sender->Context->getWorkData($ecinst_UserName); // $_POST["ec_56$4"];
				$password = $sender->Context->getWorkData($ecinst_Password); // $_POST["ec_56$5"];
				
				$mbUser__get__User_for_User_Name_parm = $oms->getInstanceByGlobalIdentifier(KnownMethodBindingGuids::User__get__User_for_User_Name_parm);
				if ($mbUser__get__User_for_User_Name_parm === null)
				{
					echo("`User@get User for User Name parm`: method not found ('" . KnownMethodBindingGuids::User__get__User_for_User_Name_parm . "')");die();
				}

				$instUser = $oms->execute($sender->Context, $mbUser__get__User_for_User_Name_parm);
				$instUser = $sender->Context->getWorkData($instUser);
				
				if ($instUser !== null)
				{
					$passwordSalt = $oms->getAttributeValue($instUser, $oms->getInstanceByGlobalIdentifier(KnownAttributeGuids::PasswordSalt));
					$passwordHashExpected = $oms->getAttributeValue($instUser, $oms->getInstanceByGlobalIdentifier(KnownAttributeGuids::PasswordHash));
					$passwordHashActual = hash('sha512', $password . $passwordSalt);
					
					if ($passwordHashExpected == $passwordHashActual)
					{
						$token = $this->random_str();
	
						// create the instance of `System Account Signon`
						$instLogin = $oms->createInstanceOf($oms->getInstanceByGlobalIdentifier(KnownClassGuids::UserLogin));
						if ($instLogin !== null)
						{
							// FIXME:  these attribute should be defined in the Mocha/ZQ
							// FIXME:  they should be wrapped in a conditional which checks if we are serving in a GDPR compliant region
							// should we be storing this information then? probably not...
							
							$oms->setAttributeValue($instLogin, KnownAttributeGuids::Token, $token);
							$oms->setAttributeValue($instLogin, KnownAttributeGuids::IPAddress, $_SERVER["REMOTE_ADDR"]);
							$oms->assignRelationship($instLogin, $oms->getInstanceByGlobalIdentifier(KnownRelationshipGuids::User_Login__has__User), $instUser);
						}
						else
						{
							echo ("could not create login token");
							die();
						}
						
						$_SESSION["user_token_" . System::GetTenantName()] = $token;
						$oms->setCurrentUser($instUser);
						System::RedirectFromLoginPage();
						exit();
					}
					else
					{
						//$this->Page->GetControlByID("literal1")->EnableRender = true;
	
						//System::RedirectToLoginPage(true);
					}
				}
				$ecPasswordMsg = $oms->getInstanceByGlobalIdentifier("684f1e039ecd43d58acadcf5b84c71f8");
				$sender->Context->setElementParm($ecPasswordMsg, "visible", true);
			};

			$renderer->IsPostback = $this->Page->IsPostback;
			$renderer->StyleClasses[] = "mcx-loginpage";
			if ($this->Page->IsPostback)
			{
				$renderer->processPostback($pageElement);
			}
			$renderer->SubmitButtonText = "Log In";
			$renderer->renderInitialElement($pageElement);

			exit();
			
		}

	}
?>