<?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
 */

/**
 * propertyBackendFeatureAction Class
 *
 * Backend controller action for managing features of properties.
 *
 * @package clearFusionCMS\module\property
 */
class propertyBackendFeatureAction extends cfBackendControllerAction {

	/**
	 * Access to features.
	 *
	 * @var propertyFeatures
	 */
	protected $features;

	/**
	 * propertyBackendFeatureAction constructor.
	 *
	 * @param flController $controller The controller object.
	 */
	function __construct(flController $controller) {
		parent::__construct($controller);

		$this->features = new propertyFeatures($this->db);
	}

	/**
	 * 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 _buildTree($id, $current) {
		$results = $this->features->getChildren($id, array('featureid', 'title'));
		if(!count($results))
			return null;

		$d = $this->features->getDepth($id);
		$icon = $d == 0 ? flWidgetTreeNode::CONTAINER : flWidgetTreeNode::NODE;

		$tree = new flWidgetTreeView();
		foreach($results as $row) {
			$child = $this->_buildTree($row['featureid'], $current);
			$link = $this->assemble('property', 'feature', array('edit', $row['featureid']));
			$tree->addNode($child ? new flWidgetTreeChildNode("$row[title] [$row[featureid]]", $child, false, $link, $icon, $row['featureid']) : new flWidgetTreeLinkNode("$row[title] [$row[featureid]]", $link, $icon, $row['featureid']))
				->setLastNodeActive($current == $row['featureid']);
		}

		return $tree;
	}

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

		$rootTree->setAJAXMoveHandler($this->assemble('property', 'feature', array('dragdrop', $current ? $current : 0)));

		return $rootTree;
	}


	/**
	 * Render the page.
	 *
	 * @param string $id The current item.
	 * @param string $content The page content.
	 */
	function renderAdminPage($id, $content) {

		// Create the tree
		$rootTree = $this->buildTree($id);

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

		$fam->addAction('add', __('Add feature', 'propertyBackend'), $this->assemble('property', 'feature', array('add', $id ? $id : 0)));

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

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

	/**
	 * Process the request
	 */
	function process() {
		$this->testPermission('cfProperty.features');
		$this->breadcrumbTrail->addCrumb(__('Features', 'propertyBackend'), $this->assemble('property', 'feature'));

		$currid = '';
		$content = '';
		$msg = $this->session->flashGet('cfManagerOpSuccess', '');

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

		// If adding
		if($params = $addRoute->match($this->request->getParam('*', array()))) {
			$currid = $parent = $params['id'];

			// If parent depth > 1 then attached to this nodes parent
			if($parent && ($d = $this->features->getDepth($parent)) > 1)
				$parent = $this->features->getParent($parent);

			$this->breadcrumbTrail->addCrumb($parent ? __('Add Feature', 'propertyBackend') : __('Add Feature Group', 'propertyBackend'), $this->assemble('property', 'feature', array('add')));

			$form = new flForm($this->request, 'addfeatureform', $this->assemble('property', 'feature', array('add', $parent)), true);
			$form->setCSS('metaSSubmit');

			$name = new flFormElementText($form, 'title');
			$name->setLabel($parent ? __('Feature Title', 'propertyBackend') : __('Feature Group Title', 'propertyBackend'))
				->setRequired()
				->addValidator(new flFormValidatorRequired());

			$submit = new flFormElementSubmit($form, 'add');
			$submit->setCaption($parent ? __('Create Feature', 'propertyBackend') : __('Create Group', 'propertyBackend'))->setCSS('primary');

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

			$form->focusElement($name);

			// Capture ctrl+s for save, implement select all/none, check user really wants to leave page
			$this->tpl->getCodeBuffer()->bufferHeadJS('system/app/js/cfMetaS.js');
			$this->confirmLeavePage();

			if($form->isSubmitted() && $form->isValid()) {

				// Create
				if($id = $this->features->insertNode($parent, flDbNestedSet::UNDER, array(
					'title' => $name->getValue()
				))) {
					propertyAudit::createFeature($id, $name->getValue());
					$this->cache->flush();

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

			$content = $this->renderBlock($parent ? __('Add Feature', 'propertyBackend') : __('Add Feature Group', 'propertyBackend'), $form->render($this->tpl->getCodeBuffer()), '', $msg);
		}
		// If editing
		else if($params = $editRoute->match($this->request->getParam('*', array()))) {
			$currid = $params['id'];

			$group = $this->features->getDepth($currid) == 1;

			$this->breadcrumbTrail->addCrumb($group ? __('Edit Feature Group', 'propertyBackend') : __('Edit Feature', 'propertyBackend'), $this->assemble('property', 'feature', array('edit', $currid)));

			if(($content = $this->lockObject('property.features', $currid)) === true) {
				$data = $this->features->getNodeData($currid, array('title'));

				$form = new flForm($this->request, 'editfeatureform', $this->assemble('property', 'feature', array('edit', $currid)), true);
				$form->setCSS('metaSSubmit');

				$name = new flFormElementText($form, 'title', $data['title']);
				$name->setLabel($group ? __('Feature Group Title', 'propertyBackend') : __('Feature Title', 'propertyBackend'))
					->setRequired()
					->addValidator(new flFormValidatorRequired());

				$submit = new flFormElementSubmit($form, 'update');
				$submit->setCaption($group ? __('Update Group', 'propertyBackend') : __('Update Feature', 'propertyBackend'))->setCSS('primary');

				$delete = new flFormElementSubmit($form, 'delete');
				$delete->setCaption($group ? __('Delete Group', 'propertyBackend') : __('Delete Feature', 'propertyBackend'))->setCSS('danger');

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

				$form->focusElement($name);

				// Capture ctrl+s for save, implement select all/none, check user really wants to leave page
				$this->tpl->getCodeBuffer()->bufferHeadJS('system/app/js/cfMetaS.js');
				$this->confirmLeavePage();

				if($form->isSubmitted() && $form->isValid()) {

					if($delete->getValue())
						$this->response->redirectTo($this->assemble('property', 'feature', array('delete', $currid), true));

					// Update the zone
					if($this->features->updateNodeData($currid, array(
						'title' => $name->getValue()
					))) {
						propertyAudit::updateFeature($currid, $name->getValue(), $data);
						$this->cache->flush();

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

				$content = $this->renderBlock(sprintf($group ? __('Edit Group: %s', 'propertyBackend') : __('Edit Feature: %s', 'propertyBackend'), $data['title']), $form->render($this->tpl->getCodeBuffer()), '', $msg);
			}
		}
		// If deleting
		else if($params = $deleteRoute->match($this->request->getParam('*', array()))) {
			$currid = $params['id'];
			$group = $this->features->getDepth($currid) == 1;

			$this->breadcrumbTrail->addCrumb($group ? __('Delete Group', 'propertyBackend') : __('Delete Feature', 'propertyBackend'), $this->assemble('property', 'feature', array('delete', $currid)));

			if(($content = $this->lockObject('property.features', $currid)) === true) {
				$data = $this->features->getNodeData($currid, array('title'));

				$form = new flForm($this->request, 'delfeatureform', $this->assemble('property', 'feature', array('delete', $currid)), true);

				$name = new flFormElementText($form, 'name', $data['title']);
				$name->setLabel($group ? __('Feature Group Title', 'propertyBackend') : __('Feature Title', 'propertyBackend'))
					->setDisabled()
					->setReadonly();

				$delSubmit = new flFormElementSubmit($form, 'delete');
				$delSubmit->setCaption($group ? __('Permanently Delete The Feature Group', 'propertyBackend') : __('Permanently Delete The Feature', 'propertyBackend'))
					->setCSS('danger')
					->setDescription($this->alertMessage('Warning', __('This action cannot be undone and may affect the ability to locate properties.', 'propertyBackend')));

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

					// Get all child nodes & delete from properties
					$children = $this->features->getChildren($currid, array('featureid'), 0, true, 2);
					foreach($children as $c) {
						$this->db->delete('cf_property_features_map_' . clearFusionCMS::getSite(), 'featureid=?', $c);
					}
					$this->db->delete('cf_property_features_map_' . clearFusionCMS::getSite(), 'featureid=?', $currid);

					// Delete the feature/group and children
					$this->features->deleteNode($currid);

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

				$content = $this->renderBlock(sprintf($group ? __('Delete Group: %s', 'propertyGroup') : __('Delete Feature: %s', 'propertyBackend'), $data['title']), $form->render($this->tpl->getCodeBuffer()), '', $msg);
			}
		}
		// If drag n drop
		else if($params = $dragRoute->match($this->request->getParam('*', array()))) {
			$active = $params['id'];

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

			if(!$this->user->hasPermission('cfProperty.features')) {
				$result['success'] = false;
				$result['msg'] = __('Internal failure, please try again.', 'cfBackend');
				$active = '-1';
			}

			$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']) {
				$group = $this->features->getDepth($moving) == 1;

				// If moving a group then target must be before/after another group
				if($group) {
					if($position == 0)
						$position = 1;

					if($p = $this->features->getParent($target)) {
						$target = $p;
					}
				}
				// If feature target must be group
				else {
					$d = $this->features->getDepth($target);
					if($position == 0 && $d > 1)
						$position = 1;

					if($target == 0)
						$result['success'] = false;
				}

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

				if($result['success'] && $this->features->moveNode($moving, $target, $position == 0 ? propertyFeatures::UNDER : ($position == -1 ? propertyFeatures::BEFORE : propertyFeatures::AFTER))) {
					propertyAudit::moveFeature(
						$moving,
						$position == 0 ? 'into' : ($position == -1 ? 'before' : 'after'),
						implode(' / ', $origPath),
						implode(' / ', $newPath)
					);

					if($group)
						$result['msg'] = __('The feature group has been moved to the new location.', 'propertyBackend');
					else
						$result['msg'] = __('The feature has been moved to the new location.', 'propertyBackend');
					$this->cache->flush();
				}
				else {
					$result['msg'] = __('Unable to move this item 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;
		}

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