<?php

namespace Engine\Form\Button;

/**
 * The class manages the standard form button functionalities.
 * @phpstan-consistent-constructor - hack to allow different calls for the same constructor (although they have the same fingerprint
 * @author Lucian
 */
abstract class GeneralAbstract
{
	// this will determine the CSS and TPL files used, it will be overwritten by each class that extends this one
	protected $class = "general";

	// the field label
	protected $label = "";

	// the field id, optional, will be determined from the field name if not specified
	protected $id = "";

	// the field tooltip
	protected $tooltip = "";

	// field can be rendered (the old 'invisible' tag from formRow). Setting this to false will stop its rendering routine
	protected $dontRender = false;

	// the disabled attribtue. If true, this form should not get submitted, and should be readonly/disabled when displayed
	protected $disabled = false;

	// field settings, not style-related
	protected $settings = [];

	protected $name='';

	// current element style
	protected $style = [
		'width' => 		null,
		'height' => 	null,
		'CSSClass' => 	null,
		'special' => 	false,
	];

	// default element style
	protected $defaultStyle = [];

	// transient, only filled in when rendering, used in the "style inheritance" mechanism
	protected $computedStyle = [];

	protected $parent = null;

	/**
	 * Create a new input button.
	 * @param String $label (optional)
	 * @param String $name (optional)
	 * @param Mixed $value (optional)
	 * @param Mixed $possibleValues (optional)
	 */
	static public function create($label=null)
	{
		return new static($label);
	}

	/**
	 * Class constructor
	 * @param string $label
	 * @param string $name
	 */
	public function __construct($label=null, $name='')
	{
		$this->style['CSSClass'] = "button-{$this->class}";

		if ($label){
			$this->setLabel($label);
		}
		if ($name) {
			$this->setName($name);
		}
	}

	/**
	 * Try to determine if this is a container. All base elements will return false
	 * @return Bool false
	 */
	public function isContainer()
	{
		return false;
	}

	/**
	 * Get the computed(internal) styles. Used by child elements.
	 * @return Array
	 */
	public function getComputedStyle()
	{
		return $this->computedStyle;
	}

	/**
	 * Get the defined(real) internal styles
	 */
	public function getStyles()
	{
		return $this->style;
	}

	/**
	 * Get a specific element style (real stle, not inherited or defaults).
	 * @param String $key
	 */
	public function getStyle($key)
	{
		return $this->style[$key];
	}

	/**
	 * Get the button width
	 */
	public function getWidth()
	{
		return $this->getStyle('width');
	}

	/**
	 * Set the button width
	 * @param int $width
	 */
	public function setWidth($width)
	{
		return $this->setStyle('width', $width);
	}

	/**
	 * Mark the button's parent, so the button may access the containers input elements
	 * @param \Engine\Form\Container\GeneralAbstract $parent
	 */
	public function setParent(\Engine\Form\Container\GeneralAbstract $parent)
	{
		return $this->parent = $parent;
	}

	/**
	 * Sets the button name
	 */
	public function setName($name)
	{
		$this->name=$name;
		return $this;
	}

	/**
	 * Set a specific element style (real stle, not inherited or defaults).
	 * @param String $key the style name
	 * @param Mixed $value the value
	 */
	public function setStyle($key, $value=true)
	{
		$this->style[$key] = $value;
		return $this;
	}


	/**
	 * Get the element label
	 * @return String
	 */
	public function getLabel()
	{
		return $this->label;
	}

	/**
	 * Set the element label
	 * @param String $label
	 */
	public function setLabel($label)
	{
		$this->label = $label;
		return $this;
	}

	/**
	 * Get the element tooltip
	 * @return String
	 */
	public function getTooltip()
	{
		return $this->tooltip;
	}

	/**
	 * Set the element tooltip
	 * @param String $tooltip
	 */
	public function setTooltip($tooltip)
	{
		$this->tooltip = $tooltip;
		return $this;
	}

	/**
	 * Get the element ID to be used in the output html
	 */
	public function getId()
	{
		return $this->elementId();
	}

	/**
	 * Set the element ID to be used in the output html
	 * @param String $id
	 */
	public function setId($id)
	{
		$this->id = $id;
		return $this;
	}

	/**
	 * Get the element ID, choosing between generating one, or using the one already specified by the user. Basically this function will always return something
	 * @return String
	 */
	public function elementId()
	{
		$id = $this->id;
		if(!$id){
			$id = \Engine\Form\Container\GeneralStaticMethods::generateUniqueID("button");
			$this->id = $id;
		}

		return $id;
	}

	/**
	 * Disable element rendering routines (compatibility with the old formRow 'invisibile' tag name)
	 * @param Bool $disabled
	 */
	public function setInvisible($dontRender=true)
	{
		$this->dontRender = $dontRender;
		return $this;
	}

	/**
	 * Detect whenever the input should be rendered or not in the interface (compatibility with the old formRow 'invisibile' tag name)
	 * @return Bool
	 */
	public function getInvisible()
	{
		return $this->dontRender;
	}

	/**
	 * Add a user-defined js callback on a certain event
	 * @param String $event - onClick supported for now
	 * @param String $jsCallback - js function name to be called. Also specify parameters
	 */
	public function addEvent($event, $jsCallback)
	{
		if (!isset($this->settings['events'][$event])) {
			$this->settings['events'][$event] = [];
		}

		// use str_replace to replace all double-quotes, wich are used in the html to define the onChange event
		$this->settings['events'][$event][] = $jsCallback;

		return $this;
	}


	/**
	 * Handle the general onClick event
	 */
	public function onClick($jsCallback)
	{
		return $this->addEvent('onClick', $jsCallback);
	}

	/**
	 * Get the output TPL file path
	 * @param String $templateClass (optional)
	 * @throws \Exception
	 * @return String
	 */
	public function getTemplatePath($templateClass = null)
	{
		if($templateClass==null){
			$templateClass = $this->class;
		}

		$templateFile = "";
		if (is_file(APP_REAL_PATH."/_templates/application/formField/buttons/{$templateClass}.tpl")) {
			$templateFile = APP_REAL_PATH."/_templates/application/formField/buttons/{$templateClass}.tpl";
		} elseif (is_file(APP_REAL_PATH."/_templates/engine/formField/buttons/{$templateClass}.tpl")) {
			$templateFile = APP_REAL_PATH."/_templates/engine/formField/buttons/{$templateClass}.tpl";
		}

		if (!$templateFile) {
			throw new \Exception("Cannot find template for button element '{$templateClass}'");
		}

		return $templateFile;
	}

	/**
	 * Get the parameters that will be sent down to the TPL file
	 * @param String $templateClass
	 * @param \Engine\Form\Input\ElementAbstract $parentElement
	 */
	public function getTemplateParams($templateClass = null, $parentElement = null)
	{
		// the element ID that will be used in the TPL file
		$id = $this->elementId();

		// inherit local(specified) styles
		$style = $this->style;

		// save the computed styles
		$this->computedStyle = $style;

		// translate the label, some of the LANG vars are directly loaded from LANG, some are re-mapped as needed
		$_LANG = \App\Loader_Lang::getInstance()->getGlobal();
		$translation = $this->label;
		switch($this->label){
			case 'close': case 'cancel': case 'save':
				$translation = $_LANG[$this->label];
			break;

			case 'insert':
				$translation = $_LANG['add'];
			break;
			case 'update':
				$translation = $_LANG['edit'];
			break;
		}

		// this will be assigned to Smarty
		return [
			'class' =>		$this->class,
			'name' =>		$this->name,
			'label' =>		$translation,
			'id' => 		$id,

			////////////////////////////////////////////////
			'settings' =>	$this->settings,
			'disabled' =>	$this->disabled,
			'style' =>		$style,

			////////////////////////////////////////////////
			'parent' =>		$this->parent,
			'methods' =>	$this,
			'element' =>	$this,
		];
	}

	/**
	 * Render the element, and return the html string describing it
	 * @param String $templateClass
	 * @param \Engine\Form\Input\ElementAbstract $parentElement
	 * @return String
	 */
	public function render($templateClass = null, $parentElement = null)
	{
		if($this->dontRender){	// DO NOT use the getInvisible() function, as that may be overloaded and return a false negative (see the hidden text input class for an example)
			return '';	// old-style invisible item (from formRow) :)
		}

		if($templateClass==null){
			$templateClass = $this->class;
		}

		$smarty = \Engine\Smarty\SmartyMultiton::getInstance();

		$oldElement = $smarty->getTemplateVars('button');

		$smarty->assign('button', $this->getTemplateParams($templateClass, $parentElement));
		$return = $smarty->fetch($this->getTemplatePath($templateClass));
		$smarty->clearAssign('button');

		if($oldElement){
			$smarty->assign('button', $oldElement);
		}

		return $return;
	}

}

