<?php

/**
 * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * @package clearFusionCMS\module\property
 * @copyright Copyright (c) 2016 - 2017 clearFusionCMS. All rights reserved.
 * @link http://clearfusioncms.com
 */

/**
 * propertyBackendRegionAction Class
 *
 * Backend controller for editing & managing regions.
 *
 * @package clearFusionCMS\module\property
 */
class propertyBackendRegionAction extends cfBackendControllerAction {

	/**
	 * Recursive function to build a tree of the regions.
	 *
	 * @param int $id ID of the node to build the tree for.
	 * @param int $current ID of the current active node.
	 * @return flWidgetTreeNode The tree.
	 */
	protected function buildRegionTree($id, $current) {
		$results = $this->db->query('SELECT regionid,title FROM cf_property_region_' . clearFusionCMS::getSite() . ' WHERE parentid=? ORDER BY lft', $id);
		if(!$results->numRows())
			return null;

		$tree = new flWidgetTreeView();
		while($row = $results->fetch()) {
			$child = $this->buildRegionTree($row['regionid'], $current);
			$link = $this->assemble('property', 'region', array('edit', $row['regionid']));
			$tree->addNode($child ? new flWidgetTreeChildNode("$row[title] [$row[regionid]]", $child, false, $link, flWidgetTreeNode::IMAGE, $row['regionid']) : new flWidgetTreeLinkNode("$row[title] [$row[regionid]]", $link, flWidgetTreeNode::IMAGE, $row['regionid']))
				->setLastNodeActive($current == $row['regionid']);
		}

		return $tree;
	}

	/**
	 * Build the tree.
	 *
	 * @param string $current The current node.
	 * @return flWidgetTreeView The tree widget.
	 */
	protected function buildTree($current) {
		$rootTree = new flWidgetTreeView('cfProperty.region.tree');
		$tree = $this->buildRegionTree(0, $current);
		$rootTree->addNode($tree ? new flWidgetTreeChildNode(__('Regions', 'propertyBackend'), $tree, null, $this->assemble('property', 'region'), 'container', 0) : new flWidgetTreeNode(__('Regions', 'propertyBackend'), flWidgetTreeNode::CONTAINER, 0));
		if(!$current)
			$rootTree->setLastNodeActive(true);

		if($this->user->hasPermission('cfProperty.regions'))
			$rootTree->setAJAXMoveHandler($this->assemble('property', 'region', array('dragdrop', $current)));

		return $rootTree;
	}

	/**
	 * Render the page.
	 *
	 * @param string $rgn The current region.
	 * @param string $content The page content.
	 * @param bool $orderChange true if changing the order.
	 */
	function renderAdminPage($rgn, $content, $orderChange = false) {

		$rootTree = $this->buildTree($rgn);

		// Create the toolbar
		$fam = new flWidgetFloatingActionMenu();
		$fam->setCSS('[[+css]]')
			->setDefaultToOpen($orderChange);

		$fam->addAction('add', __('Add Region', 'propertyBackend'), $this->assemble('property', 'region', array('add', $rgn)));

		$this->tpl->registerChunk('fam', $fam->render());

		// Render the page
		$this->renderPage(__('Region', 'propertyBackend'), $content, $this->renderBlock('', $rootTree->render($this->tpl->getCodeBuffer())));
	}

	/**
	 * Process the request
	 */
	function process() {
		$this->testPermission('cfProperty.regions');
		$this->breadcrumbTrail->addCrumb(__('Property Regions', 'propertyBackend'), $this->assemble('property', 'region'));

		$addRoute = new flRouterRoute('add/:id', array(), array('id' => '\d+'));
		$editRoute = new flRouterRoute('edit/:id', array(), array('id' => '\d+'));
		$delRoute = new flRouterRoute('delete/:id', array(), array('id' => '\d+'));
		$dragRoute = new flRouterRoute('dragdrop/:id', array(), array('id' => '\d+'));

		// If drag n drop move
		if($params = $dragRoute->match($this->request->getParam('*', array()))) {
			$active = $params['id'];

			$result = array(
				'success' => true,
				'msg' => '',
				'dismiss' => __('Dismiss', 'cfBackend'),
				'tree' => ''
			);

			$position = $this->request->getPost('position', '');
			$moving = $this->request->getPost('moving', '');
			$target = $this->request->getPost('target', '');

			if(!preg_match('/^-?[01]$/', $position) || !preg_match('/^\d+$/', $moving) || !preg_match('/^\d+$/', $target)) {
				$result['success'] = false;
				$result['msg'] = __('Internal failure, please try again.', 'cfBackend');
			}

			if($result['success']) {
				$regions = new propertyRegions($this->db);

				// Update search
				$newParent = $target;
				if($position) {
					// Find the parent
					$newParent = $regions->getParent($target);
				}

				// Find the old search path
				$res = $regions->getPath($moving, array('regionid'));
				$segsOld = array('property');
				foreach($res as $r)
					$segsOld[] = $r['regionid'];

				// Find the new search path
				$res = $regions->getPath($newParent, array('regionid'));
				$segsNew = array('property');
				foreach($res as $r)
					$segsNew[] = $r['regionid'];


				$res = $regions->getPath($moving, ['title']);
				$origPath = [];
				foreach ($res as $row) {
					$origPath[] = $row['title'];
				}
				$res = $regions->getPath($target, ['title']);
				$newPath = [];
				foreach ($res as $row) {
					$newPath[] = $row['title'];
				}

				// Move the node
				if($regions->moveNode($moving, $target, $position == 0 ? propertyRegions::UNDER : ($position == -1 ? propertyRegions::BEFORE : propertyRegions::AFTER))) {
					propertyAudit::moveRegion(
						$moving,
						$position == 0 ? 'into' : ($position == -1 ? 'before' : 'after'),
						implode(' / ', $origPath),
						implode(' / ', $newPath)
					);

					// Update search
					clearFusionCMS::getEventManager()->notify(new flEvent('system.searchMove', array(
						'src' => $segsOld,
						'dst' => $segsNew
					)));

					$result['msg'] = __('The region has been moved to the new location.', 'propertyBackend');
					$this->cache->flush();
				}
				else {
					$result['msg'] = __('Unable to move this region to the selected location, please try again.', 'propertyBackend');
					$result['success'] = false;
				}
			}

			// Rebuild the tree
			$rootTree = $this->buildTree($active);
			$result['tree'] = $rootTree->render(null, true);

			// Send AJAX response
			$this->response->setContentType('application/json')
				->noCache()
				->setBody(json_encode($result))
				->sendResponse();
			die;
		}
		elseif($param = $editRoute->match($this->request->getParam('*', array()))) {
			$this->editRegion($param['id']);
		}
		elseif($param = $addRoute->match($this->request->getParam('*', array()))) {
			$this->addRegion($param['id']);
		}
		elseif($param = $delRoute->match($this->request->getParam('*', array()))) {
			$this->deleteRegion($param['id']);
		}
		else {
			$this->renderAdminPage(0, '');
		}
	}

	/**
	 * Edit a region.
	 *
	 * @param int $id The ID of the region to edit.
	 */
	protected function editRegion($id) {
		$this->testPermission('cfProperty.regions');
		$this->breadcrumbTrail->addCrumb(__('Edit Region', 'propertyBackend'), $this->assemble('property', 'region'));

		$msg = $this->session->flashGet('cfManagerOpSuccess') ? $this->session->flashGet('cfManagerOpSuccess') : '';

		if(($content = $this->lockObject('property.region', $id)) === true && ($data = $this->db->fetchRow('SELECT * FROM cf_property_region_' . clearFusionCMS::getSite() . ' WHERE regionid=?', $id))) {

			$form = new flForm($this->request, 'regionform', $this->assemble('property', 'region', array('edit', $id)), true);

			$name = new flFormElementText($form, 'name', $data['title']);
			$name->setLabel(__('Region Name', 'propertyBackend'))
				->setRequired()
				->addValidator(new flFormValidatorRequired())
				->addValidator(new flFormValidatorMaxLength(255));

			// Get content fields and build content inputs
			$flds = $this->db->fetchAll('SELECT rgnfldid,fieldname,fldtype,editor FROM cf_property_rgnflds_' . clearFusionCMS::getSite() . ' ORDER BY disporder');
			$contents = array();
			foreach($flds as $f) {
				if($f['fldtype'] == 2) {
					$contents[$f['fldid']] = new flFormElementText($form, 'content' . $f['rgnfldid'], isset($data["content$f[fldid]"]) ? $data["content$f[rgnfldid]"] : '');
					$contents[$f['rgnfldid']]->setLabel($f['fieldname'])
						->addValidator(new flFormValidatorMaxLength(255));
				}
				else {
					$contents[$f['rgnfldid']] = cfEditor::create(
						$f['fldtype'] < 2 ? $this->getEditorModule($f['editor']) : $this->getCodeEditorModule(),
						$form,
						'content' . $f['rgnfldid'],
						isset($data["content$f[rgnfldid]"]) ? $data["content$f[rgnfldid]"] : '',
						$f['fldtype'] == 1 || $f['fldtype'] == 4 ? false : true
					);
					$contents[$f['rgnfldid']]->setLabel(sprintf(__('%s (HTML) &#91;%d&#93;', 'propertyBackend'), $f['fieldname'], $f['rgnfldid']))
						->addValidator(new flFormValidatorMaxLength(2097152));
				}
			}

			$submit = new flFormElementSubmit($form, 'update');
			$submit->setCaption(__('Update Region', 'propertyBackend'))->setCSS('primary');

			$del = new flFormElementSubmit($form, 'delete');
			$del->setCaption(__('Delete Region', 'propertyBackend'))
				->setCSS('danger');

			$form->groupElements('leftcolinfo', array($name));

			if(count($contents))
				$form->groupElements('fullcolctnt', $contents);

			$form->groupElements('submit', array($submit, $del), new flFormDecoratorList(new flFormDecoratorDl()))
				->focusElement($name);

			$this->confirmLeavePage();

			if($form->isSubmitted() && $form->isValid()) {
				if($del->getValue())
					$this->response->redirectTo($this->assemble('property', 'region', array('delete', $id), true));

				$d = array(
					'title' => $name->getValue()
				);

				foreach($contents as $cid => $elem) {
					$d["content$cid"] = $elem->getValue();
				}

				if($this->db->update('cf_property_region_' . clearFusionCMS::getSite(), $d, 'regionid=?', $id)) {
					propertyAudit::updateRegion($id, $name->getValue(), $data);

					$this->cache->flush();
					$this->session->flashSet('cfManagerOpSuccess', $this->alertMessage('Success', __('The region has been updated.', 'propertyBackend')));
					$this->response->redirectTo($this->assemble('property', 'region', array('edit', $id), true));
				}
				else
					$msg = $this->alertMessage('Error', __('Unable to update the region, please try again.', 'propertyBackend'));
			}

			$content = $this->renderBlock(__('Edit Region', 'propertyBackend'), $form->render($this->tpl->getCodeBuffer()), '', $msg);
		}

		$this->renderAdminPage($id, $content);
	}

	/**
	 * Add region action.
	 *
	 * @param int $parent The ID of the parent region.
	 */
	protected function addRegion($parent) {
		$this->testPermission('cfProperty.regions');
		$this->breadcrumbTrail->addCrumb(__('Create Region', 'propertyBackend'), $this->assemble('property', 'addregion'));

		$form = new flForm($this->request, 'regionform', $this->assemble('property', 'region', array('add', $parent)), true);

		$name = new flFormElementText($form, 'name');
		$name->setLabel(__('Region Name', 'propertyBackend'))
			->setRequired()
			->addValidator(new flFormValidatorRequired())
			->addValidator(new flFormValidatorMaxLength(255));

		// Get content fields and build content inputs
		$flds = $this->db->fetchAll('SELECT rgnfldid,fieldname,fldtype,editor FROM cf_property_rgnflds_' . clearFusionCMS::getSite() . ' ORDER BY disporder');
		$contents = array();
		foreach($flds as $f) {
			if($f['fldtype'] == 2) {
				$contents[$f['fldid']] = new flFormElementText($form, 'content' . $f['rgnfldid'], isset($data["content$f[fldid]"]) ? $data["content$f[rgnfldid]"] : '');
				$contents[$f['rgnfldid']]->setLabel($f['fieldname'])
					->addValidator(new flFormValidatorMaxLength(255));
			}
			else {
				$contents[$f['rgnfldid']] = cfEditor::create(
					$f['fldtype'] < 2 ? $this->getEditorModule($f['editor']) : $this->getCodeEditorModule(),
					$form,
					'content' . $f['rgnfldid'],
					isset($data["content$f[rgnfldid]"]) ? $data["content$f[rgnfldid]"] : '',
					$f['fldtype'] == 1 || $f['fldtype'] == 4 ? false : true
				);
				$contents[$f['rgnfldid']]->setLabel(sprintf(__('%s (HTML)', 'propertyBackend'), $f['fieldname']))
					->addValidator(new flFormValidatorMaxLength(2097152));
			}
		}

		$submit = new flFormElementSubmit($form, 'create');
		$submit->setCaption(__('Create Region', 'propertyBackend'))->setCSS('primary');

		$form->focusElement($name);

		$form->groupElements('leftcolinfo', array($name));

		if(count($contents))
			$form->groupElements('fullcolctnt', $contents);

		$form->groupElements('submit', array($submit), new flFormDecoratorList(new flFormDecoratorDl()));

		$this->confirmLeavePage();

		$msg = '';
		if($form->isSubmitted() && $form->isValid()) {
			$regions = new propertyRegions($this->db);

			$d = array(
				'title' => $name->getValue()
			);

			foreach($contents as $cid => $elem) {
				$d["content$cid"] = $elem->getValue();
			}

			if($id = $regions->insertNode($parent, propertyRegions::UNDER, $d)) {
				propertyAudit::createRegion($id, $name->getValue());
				$this->cache->flush();
				$this->session->flashSet('cfManagerOpSuccess', $this->alertMessage('Success', __('The new region has been created.', 'propertyBackend')));
				$this->response->redirectTo($this->assemble('property', 'region', array('edit', $id), true));
			}
			else
				$msg = $this->alertMessage('Error', __('Unable to create the new region, please try again.', 'propertyBackend'));
		}

		$content = $this->renderBlock(__('Create Region', 'propertyBackend'), $form->render($this->tpl->getCodeBuffer()), '', $msg);

		$this->renderAdminPage($parent, $content);
	}

	/**
	 * Action for deleting a region.
	 *
	 * @param int $id The ID of the region to delete.
	 */
	protected function deleteRegion($id) {
		$this->testPermission('cfProperty.regions');
		$this->breadcrumbTrail->addCrumb(__('Delete Region', 'propertyBackend'), $this->assemble('property', 'delregion'));

		$regions = new propertyRegions($this->db);
		$content = '';
		if(($content = $this->lockObject('property', 'region:' . $id)) === true && ($data = $this->db->fetchRow('SELECT * FROM cf_property_region_' . clearFusionCMS::getSite() . ' WHERE regionid=?', $id))) {

			// Find all the contained regions
			$nodes = array(
				$id
			);
			$childNodes = $regions->getChildren($id, array('regionid'), 0, true, 0);
			foreach($childNodes as $c)
				$nodes[] = $c['regionid'];

			// Count the number of properties contained within the regions
			$sql = 'SELECT COUNT(*) FROM cf_property_' . clearFusionCMS::getSite() . ' WHERE regionid IN(?' . str_repeat(',?', count($nodes) - 1) . ')';
			$propCount = $this->db->fetchOne($sql, $nodes);

			$msg = '';
			if($propCount) {
				$form = new flForm($this->request, 'deleteregionform', $this->assemble('property', 'delregion', array($id)), true);
				new flFormElementPane($form, 'info', $this->alertMessage('Warning', __('Unable to delete the region as it contains properties.')));
			}
			else {
				$form = new flForm($this->request, 'deleteregionform', $this->assemble('property', 'region', array('delete', $id)), true);

				$name = new flFormElementText($form, 'name', $data['title']);
				$name->setLabel(__('Delete Region', 'propertyBackend'))
					->setDisabled()
					->setReadonly();

				$delSubmit = new flFormElementSubmit($form, 'deletesubmit');
				$delSubmit->setCaption(__('Permanently Delete The Region', 'propertyBackend'))
					->setCSS('danger')
					->setDescription($this->alertMessage('Warning', __('This operation cannot be undone and will delete the region and child regions.', 'propertyBackend')));

				if ($form->isSubmitted() && $form->isValid()) {
					propertyAudit::deleteRegion($id, $data);

					// Remove from search
					$res = $regions->getPath($id, array('regionid'));
					$segs = array('property');
					foreach($res as $r)
						$segs[] = $r['regionid'];
					clearFusionCMS::getEventManager()->notify(new flEvent('system.searchDelete', array(
							'path' => $segs
						))
					);

					$regions->deleteNode($id);

					$this->cache->flush();
					$this->response->redirectTo($this->assemble('property', 'region', array(), true));
				}
			}

			$content = $this->renderBlock(__('Delete Region', 'propertyBackend'), $form->render($this->tpl->getCodeBuffer()), '', $msg);

			$this->renderAdminPage($id, $content);
		}
		else
			$this->response->redirectTo($this->assemble('property', 'region', array(), true));
	}
}
