<?php
namespace Modules\Admin\Users;

class Controller extends \App\Mvc\Controller\Window\PaginatedListing\PaginatedEntityListing
{

	/**
	 * Indicates if the entity execution should use or not a transaction. It left to 'null' the
	 * class won't work correctly - the concrete class must set it to true/false.
	 * @var Boolean
	 */
	protected $useSqlTransaction = true;

	/**
	 * The list of columns which support ordering.
	 * @var array
	 */
	protected $listOrderingColumns = ['id', 'lastName', 'idOffice', 'addTime', 'lastClick'];

	/**
	 * (non-PHPdoc)
	 * @see \App\Mvc\Controller\Window\PaginatedListing\PaginatedListing::initialize()
	 */
	protected function initialize()
	{
		parent::initialize();

		$this->actionsCfg['insert'] =         ['checkSection' => true,];
		$this->actionsCfg['update'] =         ['checkSection' => true,];
		$this->actionsCfg['delete'] =         ['checkSection' => true,];
		$this->actionsCfg['changePassword'] = ['checkSection' => true,];
		$this->actionsCfg['copyUserAccess'] = ['checkSection' => true,];
		$this->actionsCfg['userLogout'] =     ['checkSection' => true,];
	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Mvc\Controller\Window\PaginatedListing\PaginatedListing::insert_getDefaultFormData()
	 */
	protected function insert_getDefaultFormData($R)
	{
		$formData = [];
		$formData['idOffice']['id'] = CURRENT_ID_OFFICE;
		return $formData;
	}

	/**
	 * Action insert(non-PHPdoc)
	 * @see \App\Mvc\Controller\Window\PaginatedListing\PaginatedListing::actionInsert()
	 */
	protected function actionInsert($R)
	{
		$msgs = \App\MessagesCollector::create();

		$OK = true;

		// INSTANTIATE THE VIEW
		$view = new View_Insert($this);

		// get form for this operation
		if (!$R[$this->formSubmitButtonName]) {
			$formData = $this->insert_getDefaultFormData($R);
		}
		else {
			$formData = $R;

			// CHECK THAT PASSWORD IS OK
			if ($formData['password'] !== $formData['passwordConfirm']) {
				$OK = false;
				$msgs->addWarning($this->lang['messages']['addIdenticalPasswords']);
			}

			if ($OK) {
				\DB::startTransaction();
				try {
					$entityObj = \Entity\User::insert();
					$entityObj->username = 				$formData['username'];
					$entityObj->webserviceAuth =		(bool)$formData['webserviceAuth'];
					$entityObj->idOffice = 				$formData['idOffice']['id'];
					$entityObj->idDepartment = 			$formData['idDepartment']['id'];
					$entityObj->language = 				LANG;
					if ($formData['title']) {
						$entityObj->title = 			$formData['title'];
					}
					$entityObj->firstName =				$formData['firstName'];
					$entityObj->lastName = 				$formData['lastName'];
					$entityObj->job =					$formData['job'];
					$entityObj->idUsersAccessProfile = 	$formData['idUsersAccessProfile'];
					$entityObj->comments = 				$formData['comments'];
					$entityObj->remarks = 				$formData['remarks'];
					$entityObj->ID_series = 			(String)$formData['ID_series'];
					$entityObj->ID_number =				$formData['ID_number'];
					$entityObj->ID_releaseBy =			$formData['ID_releaseBy'];
					$entityObj->ID_releaseDate =		$formData['ID_releaseDate'] ? $formData['ID_releaseDate'] : null;
					$entityObj->PNC =					$formData['PNC'];
					$entityObj->birthday =				$formData['birthday'] ? $formData['birthday'] :null;
					$entityObj->address =				$formData['address'];
					$entityObj->telephone =				$formData['telephone'];
					$entityObj->fax =					$formData['fax'];
					$entityObj->email =					$formData['email'];
					$entityObj->emailAlternative =		$formData['emailAlternative'];

					$entityObj->save();
					$this->insertIdRecord = $entityObj->getId();

					// set password if user is not marked as DCS user
					if (!$formData['webserviceAuth']) {
						$entityUpdatePasswordObj = \Entity\User::updatePassword($entityObj->getId());
						$entityUpdatePasswordObj->password = $formData['password'];
						$entityUpdatePasswordObj->save();
					}

					// set  default acces options
					$defaultUserAccess = \App\UserAccess\ReferenceUserAccess::getInstance();
					$accessOptionsList = [];
					foreach ($defaultUserAccess->getAllOptionsList() as $option) {
						if ($option['type']=='checkbox') {
							if ($option['value']) {
								$accessOptionsList[$option['id']] = $option['value'];
							}
						}
						elseif ($option['type']=='select') {
							$accessOptionsList[$option['id']] = $option['value'];
						}
					}

					$userEnt = \Entity\User::update($entityObj->getId());
					$userEnt->accessSectionsList = [];
					$userEnt->accessOptionsList = $accessOptionsList;
					$userEnt->save();

					// adds success message(s)
					foreach ($entityObj->getMessages()->getMessages() as $msg) {
						$msgs->addMessage($msg);
					}

					// CLOSE POPUP IF EVERYTHING OK
					if (!$msgs->hasErrorOrWarning()) {
						$view->setClosePopup(true);
					}

					\DB::commitTransaction();
				}
				catch (\App\Entity\Exception $e) {
					\DB::rollbackTransaction();

					$msgs->appendMessages($e->getMessagesCollector());
				}
			}
		}

		// ATTACH MESSAGES TO VIEW
		if ($msgs->hasErrorOrWarning()) {
			$msgs->removeSuccess();
		}
		$msgs->groupMessagesByTypeAndText();
		$view->getMessagesCollector()->appendMessages($msgs);

		// send data to view
		$view->addInput('formData', $formData);
		$view->addInput('formSubmitButtonName', $this->formSubmitButtonName);

		// render the view
		return new \Engine\Mvc\Response\Html($view->render());
	}

	/**
	 * Get default form data for action update
	 * @see \App\Mvc\Controller\Window\PaginatedListing\PaginatedListing::update_getDefaultFormData()
	 */
	protected function update_getDefaultFormData($idEntity)
	{
		$entityObj = \Entity\User::select($idEntity);

		$formData['username'] = 			$entityObj->username;
		$formData['webserviceAuth'] =		$entityObj->webserviceAuth;
		$formData['idOffice']['id'] =		$entityObj->idOffice;
		$formData['idDepartment']['id'] =	$entityObj->idDepartment;
		$formData['job'] =					$entityObj->job;
		$formData['idUsersAccessProfile'] =	$entityObj->idUsersAccessProfile;
		$formData['title'] =				$entityObj->title;
		$formData['lastName'] =				$entityObj->lastName;
		$formData['firstName'] =			$entityObj->firstName;
		$formData['ID_series'] =			$entityObj->ID_series;
		$formData['ID_number'] =			$entityObj->ID_number;
		$formData['ID_releaseBy'] =			$entityObj->ID_releaseBy;
		$formData['ID_releaseDate'] =		$entityObj->ID_releaseDate;
		$formData['PNC'] =					$entityObj->PNC;
		$formData['birthday'] =				$entityObj->birthday;
		$formData['address'] =				$entityObj->address;
		$formData['email'] =				$entityObj->email;
		$formData['emailAlternative'] =		$entityObj->emailAlternative;
		$formData['telephone'] =			$entityObj->telephone;
		$formData['fax'] =					$entityObj->fax;
		$formData['comments'] =				$entityObj->comments;
		$formData['remarks'] =				$entityObj->remarks;

		// extract field in TIMESTAMP format
		$cantLoginUntil = $entityObj->cantLoginUntil;

		if ($cantLoginUntil <= TIMESTAMP) {
			$formData['duration'] = '';
		}
		else {
			$formData['duration'] = \Engine\DateTime::createFromTimestamp($cantLoginUntil)->formatDatetimeIso();
		}

		return $formData;
	}

	/**
	 * Update user.
	 * @see \App\Mvc\Controller\Window\PaginatedListing\PaginatedEntityListing::update_createEntityObject()
	 */
	protected function update_createEntityObject($formData)
	{

		// determine block until
		$cantLoginUntil = $formData['duration'] ? \Engine\DateTime::create($formData['duration'])->getTimestamp() : 0;

		$entityObj = \Entity\User::update($this->updateIdRecord);
		$entityObj->username = 				$formData['username'];
		$entityObj->webserviceAuth =		(bool)$formData['webserviceAuth'];
		$entityObj->idOffice = 				$formData['idOffice']['id'];
		$entityObj->idDepartment = 			$formData['idDepartment']['id'];
		$entityObj->title = 				$formData['title'];
		$entityObj->firstName =				$formData['firstName'];
		$entityObj->lastName = 				$formData['lastName'];
		$entityObj->job =					$formData['job'];
		$entityObj->idUsersAccessProfile = 	$formData['idUsersAccessProfile'];
		$entityObj->comments = 				$formData['comments'];
		$entityObj->remarks = 				$formData['remarks'];
		$entityObj->ID_series = 			$formData['ID_series'];
		$entityObj->ID_number =				$formData['ID_number'];
		$entityObj->ID_releaseBy =			$formData['ID_releaseBy'];
		$entityObj->ID_releaseDate =		$formData['ID_releaseDate'] ? $formData['ID_releaseDate'] : null;
		$entityObj->PNC =					$formData['PNC'];
		$entityObj->birthday =				$formData['birthday'] ? $formData['birthday'] : null;
		$entityObj->address =				$formData['address'];
		$entityObj->telephone =				$formData['telephone'];
		$entityObj->fax =					$formData['fax'];
		$entityObj->email =					$formData['email'];
		$entityObj->emailAlternative =		$formData['emailAlternative'];
		$entityObj->cantLoginUntil =		$cantLoginUntil;

		return $entityObj;
	}


	/**
	 * Rewrite action update for logout if user request it(non-PHPdoc)
	 * @see \App\Mvc\Controller\Window\PaginatedListing\PaginatedListing::actionUpdate()
	 */
	protected function actionUpdate($R)
	{
		$msgs = \App\MessagesCollector::create();

		$OK = true;

		$modelOutput = null;

		$this->updateIdRecord = $R['id'];
		// get form for this operation
		if (!$R[$this->formSubmitButtonName]) {
			$formData = $this->update_getDefaultFormData($this->updateIdRecord);
		}
		else {
			$formData = $R;

			// cannot block current user
			if ($formData['duration'] and $this->updateIdRecord == CURRENT_ID_USER) {
				$OK = false;
				$msgs->addWarning($this->lang['messages']['cantBlockCurrentUser']);

			}
			// do the actually insert
			if ($OK) {
				$modelOutput = $this->update_doUpdate($formData, $msgs);
			}
		}

		// INSTANTIATE THE VIEW
		$view = new View_Update($this);

		// ATTACH MESSAGES TO VIEW
		$view->getMessagesCollector()->appendMessages($msgs);


		// CLOSE POPUP IF EVERYTHING OK
		if ($modelOutput) {
			$view->setClosePopup(true);
		}

		// send data to view
		$view->addInput('formData', $formData);
		$view->addInput('formSubmitButtonName', $this->formSubmitButtonName);

		// render the view
		return new \Engine\Mvc\Response\Html($view->render());
	}

	protected function insert_createEntityObject($formData)
	{
		// defined to satisfy the abstract class
		throw new \Exception("Not defined");
	}

	/**
	 * Delete office(non-PHPdoc)
	 * @see \App\Mvc\Controller\Window\PaginatedListing\PaginatedEntityListing::delete_createEntityObject()
	 */
	protected function delete_createEntityObject($idRecord)
	{
		return \Entity\User::delete($idRecord);
	}

	/**
	 * Change password for selected item
	 * @return String - the output of "change password" action
	 */
	protected function actionChangePassword($R)
	{
		$msgs = \App\MessagesCollector::create();

		$modelOutput = null;

		// get form data for this operation
		if (!$R[$this->formSubmitButtonName]) {
			$formData = $this->changePassword_getDefaultFormData();
		}
		else {
			$formData = $R;

			$OK = true;

			// check if password is identical with confirm password
			if ($formData['password']!==$formData['passwordConfirm']) {
				$OK = false;
				$msgs->addWarning($this->lang['messages']['addIdenticalPasswords']);
			}

			if ($OK) {
				$modelOutput = $this->changePassword_doChangePassword($formData, $msgs);
			}
		}

		// create view object
		$view = new View_ChangePassword($this);

		// attach messages to view
		$view->getMessagesCollector()->appendMessages($msgs);

		// if everything ok close popup
		if ($modelOutput) {
			$view->setClosePopup(true);
		}

		// send data to the view
		$view->addInput('formData', $formData);
		$view->addInput('formSubmitButtonName', $this->formSubmitButtonName);

		// render the view
		return new \Engine\Mvc\Response\Html($view->render());
	}

	/**
	 * Default form data for action "change password"
	 * @return []
	 */
	protected function changePassword_getDefaultFormData()
	{
		return [];
	}

	/**
	 * Create entity object for User
	 * @param Array $formData
	 * @return \Entity\User
	 */
	protected function changePassword_createEntityObj($formData)
	{
		$entityObj = \Entity\User::updatePassword($formData['id']);
		$entityObj->password = $formData['password'];
		return $entityObj;
	}

	/**
	 * Do the actually update of password
	 * @param Array $formData
	 * @param \App\MessagesCollector $msgs
	 * @throws \Exception
	 * @return Bool
	 */
	protected function changePassword_doChangePassword(Array $formData, \App\MessagesCollector $msgs)
	{
		$changePasswordEntityObj = $this->changePassword_createEntityObj($formData);

		// check that it is attached to the current controller
		if (!($changePasswordEntityObj instanceof \App\Entity\GeneralAbstract)) {
			throw new \Exception('The output of `changePasswordEntityObj` must be an instance of `\App\Entity\GeneralAbstract` when calling '.get_called_class());
		}

		// DO THE ACTUAL TRY-CATCH ROUTINE FOR THE ENTITY OPERATIONS //////////////////////////////
		try {
			// start a new transaction
			\DB::startTransaction();

			// executes the save operation
			$changePasswordEntityObj->save();

			// commit the current transaction
			\DB::commitTransaction();

			// adds success message(s)
			foreach ($changePasswordEntityObj->getMessages()->getMessages() as $msg) {
				$msgs->addMessage($msg);
			}

			return true;
		}
		catch (\App\Entity\Exception $e) {
			// rollback the current transaction
			\DB::rollbackTransaction();
			$msgs->appendMessages($e->getMessagesCollector());

			return false;
		}
	}

	/**
	 * Modify user access by clone access from another user which is selected from interface
	 * @return String - the output of "user access" action
	 */
	protected function actionCopyUserAccess($R)
	{
		$msgs = \App\MessagesCollector::create();

		$modelOutput = false;

		// get form data for this operation
		if (!$R[$this->formSubmitButtonName]) {
			$formData = $this->copyUserAccess_getDefaultFormData($R);
		}
		else {
			$formData = $R;

			$OK = true;
			$sourceUser = null;

			// check the source user id
			if (!$formData['idSourceUser']['id']) {
				$OK = false;
				$msgs->addWarning($this->lang['messages']['noSrcUser']);
			}
			else {
				$sourceUser = \Entity\User::select($formData['idSourceUser']['id']);

				if (!$sourceUser->recordExists()) {
					$OK = false;
					$msgs->addError($this->lang['messages']['srcUserInvalid']);
				}
				elseif ($sourceUser->deleted) {
					$OK = false;
					$msgs->addWarning($this->lang['messages']['scrUserDeleted']);
				}
			}

			// check if selected user is not with current user
			if ($OK and $formData['id'] == $formData['idSourceUser']['id']) {
				$OK = false;
				$msgs->addWarning($this->lang['messages']['cantSelectSameUser']);
			}

			// if everything is ok
			if ($OK) {
				$modelOutput = $this->copyUserAccess_doUserAccess($formData, $msgs, $sourceUser);
			}
		}

		// create view object
		$view = new View_CopyUserAccess($this);

		// attach messages to view
		$view->getMessagesCollector()->appendMessages($msgs);

		// if everything ok close popup
		if ($modelOutput) {
			$view->setClosePopup(true);
		}

		// send data to the view
		$view->addInput('formData', $formData);
		$view->addInput('formSubmitButtonName', $this->formSubmitButtonName);

		// render the view
		return new \Engine\Mvc\Response\Html($view->render());

	}


	/**
	 * Get default form data for action "user access"
	 * @return Array
	 */
	protected function copyUserAccess_getDefaultFormData($R)
	{
		return ['id' => $R['id'] ];
	}

	/**
	 * Create entity object for add user access
	 * @param Array $formData
	 * @param \Entity\User $sourceUser
	 * @return \Entity\User
	 */
	protected function copyUserAccess_createEntityObj(Array $formData, \Entity\User $sourceUser)
	{
		$entityObj = \Entity\User::update($formData['id']);
		$entityObj->accessSectionsList = 	$sourceUser->accessSectionsList;
		$entityObj->accessOptionsList = 	$sourceUser->accessOptionsList;

		return $entityObj;
	}

	/**
	 * Do actually save of user access
	 * @param array $formData
	 * @param \App\MessagesCollector $msgs
	 * @param \Entity\User $sourceUser
	 * @throws \Exception
	 */
	protected function copyUserAccess_doUserAccess(Array $formData, \App\MessagesCollector $msgs, \Entity\User $sourceUser)
	{
		$userAccessEntityObj = $this->copyUserAccess_createEntityObj($formData, $sourceUser);

		// check that it is attached to the current controller
		if (!($userAccessEntityObj instanceof \App\Entity\GeneralAbstract)) {
			throw new \Exception('The output of `userAccessEntityObj` must be an instance of `\App\Entity\GeneralAbstract` when calling '.get_called_class());
		}

		// check that the entity operation is ok
		if ($userAccessEntityObj->getOperation() != 'update') {
			throw new \Exception('The operation of the `userAccessEntityObj` must be `update` when calling '.get_called_class());
		}

		// DO THE ACTUAL TRY-CATCH ROUTINE FOR THE ENTITY OPERATIONS //////////////////////////////
		try {

			// start a new transaction
			\DB::startTransaction();

			// executes the save operation
			$userAccessEntityObj->save();

			// commit the current transaction
			\DB::commitTransaction();

			// adds success message(s)
			foreach($userAccessEntityObj->getMessages()->getMessages() as $msg) {
				$msgs->addMessage($msg);
			}

			return true;

		} catch (\App\Entity\Exception $e) {

			// rollback the current transaction
			\DB::rollbackTransaction();
			$msgs->appendMessages($e->getMessagesCollector());

			return false;
		}
	}

	/**
	 * Logout action
	 */
	protected function bulkActionUserLogout(array $recordsIds, \App\MessagesCollector $msgs, array $R)
	{
		// loop selected records and block them
		foreach ($recordsIds as $id) {
			\DB::startTransaction();
			try {
				$loginSessionDelete = \Entity\UserLoginSession::delete($id);
				$loginSessionDelete->save();
				\DB::commitTransaction();

				$msgs->addSuccess($this->lang['messages']['usersLoggedOut']);
			} catch (\App\Entity\Exception $e) {
				// rollback the current transaction
				\DB::rollbackTransaction();
				$msgs->appendMessages($e->getMessagesCollector());
			}
		}

		$msgs->groupMessagesByTypeAndText();
	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Mvc\Controller\Window\PaginatedListing\ListingAbstract::optionDefineParams()
	 */
	protected function optionDefineParams(\App\Mvc\View\Utils\RowOptions\Option\Option $option, $selection)
	{

		switch($option->getId()){
			case 'insert':
			break;

			case 'userAccess':
				foreach ($selection as $selectedItem) {
					if (!\App\UserAccess\CurrentUserAccessFast::getInstance()->hasAccessToOption('Modules/Admin/Users-updateUserAccess')) {
						$option->setDisabled();
						$option->addMessage('warning', $this->globalLang['noAccessToOption']);
						break;
					}

					if (\Entity\User::select($selectedItem)->idUsersAccessProfile) {
						$option->addMessage('warning', $this->lang['index']['options']['reasons']['userAccess']['profileActive']);
						$option->setDisabled();
						break;
					}
				}
			break;

			case 'update': case 'changePassword': case 'copyUserAccess':
				foreach ($selection as $selectedItem) {
					if (!$option->determineOperationAvailability(\Entity\User::update($selectedItem))) {
						break;
					}
				}
			break;

			case 'delete':
				foreach ($selection as $selectedItem) {
					if (!$option->determineOperationAvailability(\Entity\User::delete($selectedItem))) {
						break;
					}
				}
			break;

			case 'userLogout':
				// logout always available
			break;

			default:
				return false;
		}

		return true;

	}

	/**
	 * (non-PHPdoc)
	 * @see \App\Mvc\Controller\Window\PaginatedListing\ListingAbstract::optionGetEntityName()
	 */
	protected function optionGetEntityName($id)
	{
		return \Entity\User::select($id)->name;
	}

}