<?php
/**
 * Please see weepie-framework.php for more details.
 */

namespace WpieFw\Helpers;

if( ! defined( 'ABSPATH' ) ) exit;

/**
 * WpieFormHelper class
 *
 * Helper class for rendering formfields
 *
 * Depending on the type (e.a text, select, checkbox etc.) the corresponding HTML is returned
 *
 * @author $Author: Vincent Weber <weepie-plugins@outlook.com> $
 *
 * @since 0.1
 */
class WpieFormHelper
{
	/**
	 * The type of the formfield
	 *
	 * @since 0.1
	 *
	 * @var string
	 */
	protected static $type;

	/**
	 * The name of the formfield
	 *
	 * @since 0.1
	 *
	 * @var string
	 */
	protected static $name;


	/**
	 * The name of the formfield
	 *
	 * @since 1.1.8
	 *
	 * @var string
	 */
	protected static $nameOri;


	/**
	 * The namespace of the formfield
	 *
	 * @since 0.1
	 *
	 * @var string
	 */
	protected static $nameSpace;

	/**
	 * The value of the formfield
	 *
	 * @since 0.1
	 *
	 * @var string
	 */
	protected static $value;

	/**
	 * HTML attributes
	 *
	 * @since 0.1
	 *
	 * @var array
	 */
	protected static $attributes;


	/**
	 * Element types that dont need the 'name' and 'value' attribute
	 *
	 * @since 0.1
	 *
	 * @var array
	 */
	protected static $attributesEscapeNameValue = ['div', 'buttonsubmitnoname', 'linkbutton', 'button', 'checkboxmulti'];


	/**
	 * Element types that are 'buttons'
	 *
	 * @since 0.1
	 *
	 * @var array
	 */
	protected static $attributesButton = ['buttonsubmitnoname', 'buttonsubmit', 'linkbutton', 'buttonsubmitconfirm', 'button'];


	/**
	 * Element types that don't need tabindex attribute
	 *
	 * @since 1.1.5
	 *
	 * @var array
	 */
	protected static $tabindexEscapeFields = ['hidden', 'disabled', 'div'];


	/**
	 * InnerHtml
	 *
	 * e.a. <a>{innerHtml}</a>
	 *
	 * @since 0.1
	 *
	 * @var string
	 */
	protected static $innerHtml;

	/**
	 * The options for a <select> element
	 *
	 * @since 0.1
	 *
	 * @var array
	 */
	protected static $options = [];

	/**
	 * The selectboxes for a multi selectbox element
	 *
	 * @since 1.1.8
	 *
	 * @var array
	 */
	protected static $checkboxes = [];


	/**
	 * Flag if current element has Attributes
	 *
	 * @since 0.1
	 *
	 * @var bool
	 */
	protected static $hasAttributes = false;

	/**
	 * Flag if current element has innerHtml
	 *
	 * @since 0.1
	 *
	 * @var bool
	 */
	protected static $hasInnerHtml = false;

	/**
	 * Flag if current element is a <select> field
	 *
	 * @since 0.1
	 *
	 * @var bool
	 */
	protected static $isSelect = false;


	/**
	 * Flag if current element is a checkbox multi field
	 *
	 * @since 1.1.8
	 *
	 * @var bool
	 */
	protected static $isCheckboxMulti = false;


	/**
	 * Counts the number of fields during the current page request
	 *
	 * @since 1.1.5
	 *
	 * @var int
	 */
	protected static $fieldCount = 1;


	/**
	 * Flag if current element is a disabled field
	 *
	 * i.e. an attribute disabled="disabled" is added
	 *
	 * @since 1.0
	 *
	 * @var bool
	 */
	protected static $isDisabled = false;

	/**
	 * Placeholder for element attributes
	 *
	 * @since 0.1
	 *
	 * @var string
	 */
	const ATTR_MASK = '{attr}';

	/**
	 * Placeholder for element attributes
	 *
	 * @since 0.1
	 *
	 * @var string
	 */
	const NAME_MASK = '{name}';

	/**
	 * Placeholder for element attributes
	 *
	 * @since 0.1
	 *
	 * @var string
	 */
	const VALUE_MASK = '{value}';

	/**
	 * Placeholder for element attributes
	 *
	 * @since 0.1
	 *
	 * @var string
	 */
	const SELECT_OPTIONS_MASK = '{options}';

	/**
	 * Placeholder for element attributes
	 *
	 * @var string
	 */
	const INNER_HTML_MASK = '{innerhtml}';

	/**
	 * HTML string for case selected with, e.a. <option selected="selected"></option>
	 *
	 * @since 0.1
	 *
	 * @var string
	 */
	const SELECTED_STR = 'selected="selected"';

	/**
	 * HTML string for case checked with, e.a. <input type="radio" checked="checked" />
	 *
	 * @since 0.1
	 *
	 * @var string
	 */
	const CHECKED_STR = 'checked="checked"';


	/**
	 * Handle the formfield based on type
	 *
	 * @acces public
	 *
	 * @param string $type the type of the formfield
	 * @param string $name the name of the formfield
	 * @param string $value the value of the formfield (optional)
	 * @param array $attributes the HTML attributes (optional)
	 * @param string $innerHtml the innerHtml (optional)
	 * @param array $selectOptions selectbox options (optional)
	 * @param bool $render (optional)
	 *
	 * @since 0.1
	 *
	 * @return string the formfield returned by self::render()
	 */
	public static function formField( $type = '', $name = '', $value = '', $nameSpace = '', $attributes = [], $innerHtml = '', $options = [], $render = true )
	{
		if( '' === (string)$type )
			return 'No formfield type defined.';

		$hasMissingParams = false;
		$missingParams = [];

		self::$type = (string)$type;

		self::$name = (string)$name;

		self::$nameSpace = ( '' !== $nameSpace ) ? $nameSpace : false;

		// @todo: need stripslashes_deep here for arrays?
		if( !is_array( $value ) ) {
			$value = stripslashes((string)$value);
		}

		self::$value = $value;

		if( !empty($attributes) )
		{
			self::$hasAttributes = true;
			self::$attributes = $attributes;
		}

		self::$isDisabled = ( in_array( 'disabled', $attributes ) || 'disabled' === self::$type );

		if( !empty($innerHtml) )
		{
			self::$hasInnerHtml = true;
			self::$innerHtml = $innerHtml;
		}

		if( 'textarea' === self::$type )
		{
			self::$hasInnerHtml = true;
			self::$innerHtml = self::$value;
		}

		if( 'select' === self::$type )
		{
			if( !empty( $options ) ) {
				self::$isSelect = true;
				self::$options = $options;
			} else {
				$hasMissingParams = true;
				$missingParams[] = 'options';
			}
		}

		if( 'checkboxmulti' === self::$type )
		{
			if( !empty( $options ) ) {
				self::$value = (array)$value;
				self::$isCheckboxMulti = true;
				self::$checkboxes = $options;

				if( '' === self::$name ) {
					self::$name = $type;
				}
			} else {
				$hasMissingParams = true;
				$missingParams[] = 'options';
			}
		}

		// return here if having missing params
		if( $missingParams ) {
			self::_reset();
			return 'Formfield "'.$type.'" has missing params: '. join( ', ', $missingParams );
		}

		self::_prepareAttributes();

		if( true === $render )
			return self::_render();
	}


	/**
	 * Wether a selectbox is selected or not
	 *
	 * @access public
	 *
	 * @param string $currentValue
	 * @param string $value
	 *
	 * @since 0.1
	 *
	 * @return bool true if yes and false if no
	 */
	public static function isSelected( $currentValue, $value='' )
	{
		if( isset($value) && '' !== $value )
		{
			if(is_string($value))
				$currentValue=strval($currentValue);

			if(is_int($value))
				$currentValue=intval($currentValue);

			if(is_bool($value))
				(boolean) intval($currentValue);

			return ($currentValue === $value);

		}else
			return false;
	}


	/**
	 * Wether a checkbox is checked or not
	 *
	 * @access public
	 *
	 * @param string $value
	 *
	 * @since 0.1
	 *
	 * @return bool true if yes and false if no
	 */
	public static function isChecked( $value = '' )
	{
		if( isset( $value ) ) {

			if( is_string( $value ) )
				$value = intval( $value );

			(boolean) $value;

			return ( true == $value );

		} else
			return false;
	}


	/**
	 * Validate email address
	 *
	 * @param string $email
	 *
	 * @uses filter_var with the FILTER_VALIDATE_EMAIL option
	 *
	 * @since 1.2.3
	 *
	 * @return bool true or false
	 */
	public static function isEmail( $email = '' )
	{
		if( '' === $email ) {
			return false;
		}

		return filter_var( $email, FILTER_VALIDATE_EMAIL );
	}


	/**
	 * Set name attribute based on given namespace
	 *
	 * If no namespace is passed, ignore and just set the name attribute normally
	 *
	 * @since 1.1.8
	 *
	 * @access protected
	 */
	protected static function _getNamespaceName( $name = '' )
	{
		$name = ( '' !== $name ) ? $name : self::$name;

		return ( false === self::$nameSpace ) ? $name : self::$nameSpace.'['.$name.']';
	}


	/**
	 * Prepare attributes bases on type
	 *
	 * @since 0.1
	 *
	 * @access protected
	 */
	protected static function _prepareAttributes()
	{
		self::$attributes['id'] = ( isset( self::$attributes['id'] ) ) ? self::$attributes['id'] : WpieMiscHelper::convertToHyphens( self::$name );
		self::$attributes['class'] = ( isset( self::$attributes['class'] ) && '' !== self::$attributes['class'] ) ? self::$attributes['class'] : '';
		self::$attributes['class'] = ( 'color' === self::$type ) ? self::$attributes['class'].' colorinput' : self::$attributes['class'];

		if( self::$isDisabled )
		{
			self::$attributes['class'] = ( isset( self::$attributes['class'] ) && '' !== self::$attributes['class'] ) ? self::$attributes['class'].' disabled' : 'disabled';
		}

		if( !in_array( self::$type, self::$attributesEscapeNameValue ) )
		{
			self::$nameOri = self::$name;
			self::$attributes['name'] = self::_getNamespaceName();
			//self::$name = self::$attributes['name'];
			self::$attributes['value'] =  ( '' !== self::$value ) ? self::$value : '';
		}

		if( 'textarea' === self::$type )
		{
			unset( self::$attributes['value'] );
		}

		if( 'checkbox' === self::$type )
		{
			if( self::isChecked( self::$value ) )
			{
				self::$attributes['checked'] = 'checked';
				self::$attributes['class'] = ( isset( self::$attributes['class'] ) && '' !== self::$attributes['class'] ) ? self::$attributes['class'].' checked' : 'checked';
			}
		}

		if( 'checkboxmulti' === self::$type )
		{
			self::$nameOri = self::$name;
			self::$attributes['_multiselect'] = ( isset( self::$attributes['_multiselect'] ) ) ? self::$attributes['_multiselect'] : 'false';
			if( 'true' ===  self::$attributes['_multiselect'] )
			{
				self::$attributes['_name'] = [];
				foreach( self::$checkboxes as $value => $label ) {
					self::$attributes['_name'][$value] = self::$name.'['.$value.']';
				}
				self::$attributes['_value'] = self::$value;
			}
		}

		if( 'checkboxcheckall' === self::$type )
		{
			if( self::isChecked( self::$value ) )
			{
				self::$attributes['checked'] = 'checked';
				self::$attributes['class'] = ( isset( self::$attributes['class'] ) && '' !== self::$attributes['class'] ) ? self::$attributes['class'].' checked' : 'checked';
			}
			$parent = ( isset( self::$attributes['_parent'] ) ) ? "'".self::$attributes['_parent']."'" : 'false';
			self::$attributes['onClick'] = 'wpieCheckAll(this, '. $parent .');';
			self::$attributes['class'] = ( isset( self::$attributes['class'] ) && '' !== self::$attributes['class'] ) ? self::$attributes['class'].' wpie-check-all' : 'wpie-check-all';
		}

		if( in_array( self::$type, self::$attributesButton ) )
		{
			self::$attributes['class'] = ( isset( self::$attributes['class'] ) && '' !== self::$attributes['class'] ) ? self::$attributes['class'].' button' : 'button';
		}

		if( !in_array( self::$type, self::$tabindexEscapeFields ) ) {
			self::$attributes['tabindex'] = self::$fieldCount;
		}

		if( isset( self::$attributes['_no_name'] ) )
		{
			unset( self::$attributes['name'] );
		}
	}


	/**
	 * Substitute the attributes into the formfield
	 *
	 * For HTML selectboxes and checkboxes, a 'checked' class is added
	 * For HTML buttons, a 'button' class is added
	 *
	 * @access protected
	 *
	 * @param object $field (passed by reference)
	 *
	 * @since 0.1
	 *
	 * @return void
	 */
	protected static function _substituteAttributes( &$field )
	{
		if( preg_match_all('/{([a-zA-Z]+?)}/', $field, $matches) )
		{
			foreach( $matches[0] as $k => $attr )
			{
				switch($attr)
				{
					case  self::ATTR_MASK:
						$field = preg_replace("/$attr/", self::_getAttributeString(), $field);
						break;

					case  self::SELECT_OPTIONS_MASK:
						$field = preg_replace("/$attr/", self::_getSelectOptions(self::$options) , $field);
						break;

					case  self::INNER_HTML_MASK:
						$field = preg_replace("/$attr/", self::$innerHtml, $field);
						break;

					default:
					    if( isset( self::$attributes[$matches[1][$k]] ) ) {
					        $field = preg_replace("/$attr/", self::$attributes[$matches[1][$k]], $field);
					    }
						break;
				}
			}
		}
	}


	/**
	 * Print JavaScript for a confirm submit button
	 *
	 * @access protected
	 *
	 * @since 1.1.8
	 */
	protected static function _printConfirmJs()
	{
		static $didConfirmJs = false;
		if( !$didConfirmJs ): ?>
		<script type='text/javascript'>
		function wpieMaybeFormSubmit(e, btn, idHidden, msg, ns) {
			try {
				e = e || window.event

				if('undefined' == typeof btn || null == btn)
					return false;

				if('undefined' == typeof idHidden || null == idHidden)
					return false;

				if('undefined' == typeof msg || null == msg)
					msg = 'Submit?';

				var form = btn.form;

				if (confirm(msg)) {
					document.getElementById(idHidden).value = '1';

					// if no namespace is passed by argument, get the namespace from the wpieData object
					if('' === ns && 'undefined' !== typeof wpieData && null !== btn) {
						ns = wpieData.curr_sett_ns;
					}
					if( '' !== ns ) {
						var nsElem = document.createElement('input');
						nsElem.type = "hidden";
						nsElem.name = 'ns';
						nsElem.value = ns;
						form.appendChild(nsElem);
					}
					form.submit();
				} else {
					e.preventDefault();
					return false;
				}
			} catch(exc) {
			    WPIE.log(exc.message);
			}
		}
		</script>
		<?php $didConfirmJs = true; endif ?>
		<?php
	}


	/**
	 * Print JavaScript for the check all checkbox
	 *
	 * @access protected
	 *
	 * @since 1.2
	 */
	protected static function _printCheckAllJs()
	{
		static $did = [];
		if( !$did ): ?>
		<script type='text/javascript'>
		function wpieCheckAll(toggler, parent) {
			try {
				if(null == toggler || null == parent) {
					return false;
				}
				if(false === parent) {
					// @todo create solution to find parent <table>
					return;
				}
				var tbl = document.getElementById(parent),
					c = new Array(),
					i;
				c = tbl.getElementsByTagName('input');
				for (i=0; i<c.length; i++) {
					if (c[i].type == 'checkbox') {
						c[i].checked = (toggler.checked);
					}
				}
			} catch(exc) {
			    WPIE.log(exc.message);
			}
		}
		</script>
		<?php $did = true; endif ?>
		<?php
	}


	/**
	 * Manually substitute an attribute
	 *
	 * @access protected
	 *
	 * @param string $field the HTML string for a field (passed by reference)
	 * @param string $mask the string to replace
	 * @param string $replace the string to replace the mask with
	 *
	 * @since 1.0
	 */
	protected static function _substituteAttributesManual( &$field, $mask='', $replace='' )
	{
		if('' !== $mask)
			$field = preg_replace("/$mask/", $replace, $field);
	}


	/**
	 * Get the HTML for input type text
	 *
	 * @access protected
	 *
	 * @since 0.1
	 *
	 * @return string
	 */
	protected static function _getInputText()
	{
		return '<input '.self::ATTR_MASK.' type="text" />';
	}


	/**
	 * Get the HTML for input type text
	 *
	 * @access protected
	 *
	 * @since 0.1
	 *
	 * @return string
	 */
	protected static function _getInputNumber()
	{
		return '<input '.self::ATTR_MASK.' type="number" />';
	}


	/**
	 * Get the HTML for input type text with no name and value attribute
	 *
	 * @access protected
	 *
	 * @since 1.0
	 *
	 * @return string
	 */
	protected static function _getInputTextNoNameValue()
	{
		return '<input '.self::ATTR_MASK.' type="text" />';
	}


	/**
	 * Get the HTML for input type checkbox
	 *
	 * @access protected
	 *
	 * @since 0.1
	 *
	 * @return string
	 */
	protected static function _getInputCheckbox()
	{
		$html = '<input '.self::ATTR_MASK.' type="checkbox" value="1" />';

		if( isset( self::$attributes['_label'] ) ) {
			$html .= '<label '.((self::$isDisabled)?'class="disabled"':'').' for="'.self::$attributes['id'].'">'.self::$attributes['_label'].'</label>';
		}

		return $html;
	}


	/**
	 * Get the HTML for input type checkboxmulti
	 *
	 * @access protected
	 *
	 * @since 1.1.8
	 *
	 * @return string
	 */
	protected static function _getInputCheckboxMulti()
	{
		$html = '<div id="'.self::$nameOri.'">';

		if( self::$isCheckboxMulti )
		{
			$_nameOri = self::$nameOri;
			$_attributes = self::$attributes;
			$_value = self::$attributes['_value'];
			$_name = self::$attributes['_name'];

			$i = 0;
			foreach ( self::$checkboxes as $id => $lable )
			{
				$name = ( isset( $_name[$id] ) ) ? $_name[$id] : $_nameOri.'['.$id.']';
				$value = ( isset( $_value[$id] ) ) ? $_value[$id] : '';
				$attributes = ( isset( $_attributes[$id] ) ) ? $_attributes[$id] : [];

				if( !isset( $attributes['id'] ) ) {
					$attributes['id'] = WpieMiscHelper::convertToHyphens( $_nameOri.'_'.$id );
				}
				$attributes['_label'] = $lable;

				$html .= '<div>'. self::formField( 'checkbox', $name, $value, '', $attributes ) . '</div>';
				$i++;
			}
		}
		$html .= '</div>';
		return $html;
	}


	/**
	 * Get the HTML for input type checkboxcheckall
	 *
	 * @access protected
	 *
	 * @since 1.2
	 *
	 * @return string
	 */
	protected static function _getInputCheckboxCheckAll()
	{
		self::_printCheckAllJs();

		return '<input '.self::ATTR_MASK.' type="checkbox" value="1" />';
	}


	/**
	 * Get the HTML for input type radio
	 *
	 * @access protected
	 *
	 * @since 0.1
	 *
	 * @return string
	 */
	protected static function _getInputRadio()
	{
		return '<input '.self::ATTR_MASK.' type="radio" />';
	}


	/**
	 * Get the HTML for input type file
	 *
	 * @access protected
	 *
	 * @since 0.1
	 *
	 * @return string
	 */
	protected static function _getInputFile()
	{
		return '<input '.self::ATTR_MASK.' type="file" />';
	}


	/**
	 * Get the HTML for input type hidden
	 *
	 * @access protected
	 *
	 * @since 0.1
	 *
	 * @return string
	 */
	protected static function _getInputHidden()
	{
		return '<input '.self::ATTR_MASK.' type="hidden" />';
	}


	/**
	 * Get the HTML for input type text that is disabled
	 *
	 * @access protected
	 *
	 * @since 1.0
	 *
	 * @return string
	 */
	protected static function _getInputDisabled()
	{
		return '<input '.self::ATTR_MASK.' type="text" disabled="disabled" />';
	}


	/**
	 * Get the HTML for input type submit
	 *
	 * @access protected
	 *
	 * @since 0.1
	 *
	 * @return string
	 */
	protected static function _getInputSubmit()
	{
		return '<input '.self::ATTR_MASK.' type="submit" />';
	}


	/**
	 * Get the HTML for a plain button
	 *
	 * @access protected
	 *
	 * @since 1.2.8
	 *
	 * @return string
	 */
	protected static function _getButton()
	{
		return '<button '.self::ATTR_MASK.'>'.self::INNER_HTML_MASK.'</button>';
	}



	/**
	 * Get the HTML for button type submit
	 *
	 * @access protected
	 *
	 * @since 0.1
	 *
	 * @return string
	 */
	protected static function _getButtonSubmit()
	{
		return '<button '.self::ATTR_MASK.' type="submit" name="'.self::NAME_MASK.'">'.self::INNER_HTML_MASK.'</button>';
	}


	/**
	 * Get the HTML for button type submit with a input hidden
	 *
	 * @access protected
	 *
	 * @since 1.1.8
	 *
	 * @return string
	 */
	protected static function _getButtonSubmitConfirm()
	{
		$html = '';
		$name = ( isset( self::$attributes['_hidden_name'] ) ) ? self::$attributes['_hidden_name'] : self::$name;
		$ns = ( isset( self::$attributes['_namespace'] ) ) ? self::$attributes['_namespace'] : '';

		$hiddenName = self::_getNamespaceName( $name );

		$hidden = self::_getInputHidden();
		self::_substituteAttributesManual( $hidden, self::ATTR_MASK, 'id="'.$name.'" name="'.$hiddenName.'" value=""' );

		unset( self::$attributes['_hidden_name'] );
		unset( self::$attributes['_namespace'] );

		self::_printConfirmJs();

		$msg = ( isset( self::$attributes['_msg'] ) ) ? self::$attributes['_msg'] : '';

		$html .= $hidden;
		$html .= '<button '.self::ATTR_MASK.' type="submit" name="'.self::NAME_MASK.'" onclick="wpieMaybeFormSubmit(event, this, \''.$name.'\', \''.$msg.'\', \''.$ns.'\' )">'.self::INNER_HTML_MASK.'</button>';

		return $html;
	}


	/**
	 * Get the HTML for button type submit
	 *
	 * @access protected
	 *
	 * @since 0.1
	 *
	 * @return string
	 */
	protected static function _getButtonSubmitNoName()
	{
		return '<button '.self::ATTR_MASK.' type="submit">'.self::INNER_HTML_MASK.'</button>';
	}


	/**
	 * Get the HTML for input type button
	 *
	 * @access protected
	 *
	 * @since 0.1
	 *
	 * @return string
	 */
	protected static function _getInputButton()
	{
		return '<input '.self::ATTR_MASK.' type="button" />';
	}


	/**
	 * Get the HTML for a link button
	 *
	 * @access protected
	 *
	 * @since 1.0
	 *
	 * @return string
	 */
	protected static function _getLinkButton()
	{
		return '<a '.self::ATTR_MASK.'>'.self::INNER_HTML_MASK.'</a>';
	}


	/**
	 * Get the HTML for a selectbox
	 *
	 * @access protected
	 *
	 * @since 0.1
	 *
	 * @return string
	 */
	protected static function _getSelect()
	{
		$hiddenSpan = (self::$isDisabled) ? '<span class="disabled-value">'.self::$selectOptions[self::$value].'</span>' : '';

		return '<select '.self::ATTR_MASK.'>'.self::SELECT_OPTIONS_MASK.'</select>'.$hiddenSpan;
	}

	/**
	 * Get the HTML for a textarea
	 *
	 * @access protected
	 *
	 * @since 0.1
	 *
	 * @return string
	 */
	protected static function _getTextarea()
	{
		return '<textarea '.self::ATTR_MASK.'>'.self::INNER_HTML_MASK.'</textarea>';
	}

	/**
	 * Get the HTML for a div element
	 *
	 * @access protected
	 *
	 * @since 0.1
	 *
	 * @return string
	 */
	protected static function _getDiv()
	{
		return '<div '.self::ATTR_MASK.'>'.self::INNER_HTML_MASK.'</div>';
	}


	/**
	 * Get the HTML for a colorpicker element
	 *
	 * @access protected
	 *
	 * @since 0.1
	 *
	 * @return string
	 */
	protected static function _getColorPicker()
	{
		return self::_getInputText();
	}


	/**
	 * Reset all class members to defaults
	 *
	 * @access protected
	 *
	 * @since 0.1
	 *
	 * @return void
	 */
	protected static function _reset()
	{
		self::$type='';
		self::$name='';
		self::$nameSpace='';
		self::$value='';
		self::$attributes=[];
		self::$innerHtml='';
		self::$options=[];
		self::$hasAttributes = false;
		self::$hasInnerHtml = false;
		self::$isSelect = false;
		self::$isCheckboxMulti = false;
		self::$isDisabled = false;
	}


	/**
	 * Get selectbox options
	 *
	 * @access private
	 *
	 * @param array $options
	 *
	 * @uses self::isSelected to detetmine the selected option
	 *
	 * @since 0.1
	 *
	 * @return bool false with no options or string the options HTML
	 */
	private static function _getSelectOptions( $options = [] )
	{
		if( empty( $options ) )
			return false;

		$value = self::$attributes['value'];

		$optionsStr = "";
		foreach( $options as $currentValue => $v )
		{
			$selected = ( self::isSelected($currentValue, $value) )? ' '.self::SELECTED_STR:'';

			$optionsStr .= "<option value=\"$currentValue\"$selected>".$v."</option>";
		}

		return $optionsStr;
	}


	/**
	 * Get the attributes string
	 *
	 * For selectboxes, checkboxes and 'private' attributes the 'value' attribute is escaped
	 * Attributes prefixed with a '_' (underscore) are escaped
	 *
	 * @access private
	 *
	 * @since 0.1
	 *
	 * @return string the attributes
	 */
	private static function _getAttributeString()
	{
		$str = '';
		foreach(self::$attributes as $attr => $v)
		{
			if( 'select' === self::$type && 'value' === $attr ) {
				continue;
			} elseif( 'checkbox' === self::$type && 'value' === $attr ) {
				continue;
			} elseif( 'checkboxcheckall' === self::$type && 'value' === $attr ) {
				continue;
			} elseif( 'checkboxmulti' === self::$type && ( 'value' === $attr || 'id' === $attr ) ) {
				continue;
			} elseif( 0 === strpos($attr, '_') ) { // private attributes
				continue;
			} else {
				$str .= " $attr=\"$v\"";
			}
		}

		return $str;
	}


	/**
	 * Render the formfield
	 *
	 * @access private
	 *
	 * @since 0.1
	 *
	 * @return string the formfield or an error message
	 */
	private static function _render()
	{
		switch( self::$type )
		{
			case 'text':
				$field = self::_getInputText();
				break;

			case 'number':
				$field = self::_getInputNumber();
				break;

			case 'textnoname':
				$field = self::_getInputTextNoNameValue();
				break;

			case 'checkbox':
				$field = self::_getInputCheckbox();
				break;

			case 'checkboxmulti':
				$field = self::_getInputCheckboxMulti();
				break;

			case 'checkboxcheckall':
				$field = self::_getInputCheckboxCheckAll();
				break;

			case 'radio':
				$field = self::_getInputRadio();
				break;

			case 'file':
				$field = self::_getInputFile();
				break;

			case 'hidden':
				$field = self::_getInputHidden();
				break;

			case 'disabled':
				$field = self::_getInputDisabled();
				break;

			case 'submit':
				$field = self::_getInputSubmit();
			break;

			case 'button':
				$field = self::_getButton();
				break;

			case 'buttonsubmit':
				$field = self::_getButtonSubmit();
				break;

			case 'buttonsubmitconfirm':
				$field = self::_getButtonSubmitConfirm();
				break;

			case 'buttonsubmitnoname':
				$field = self::_getButtonSubmitNoName();
				break;

			case 'button':
				$field = self::_getInputButton();
				break;

			case 'linkbutton':
				$field = self::_getLinkButton();
				break;

			case 'select':
				$field = self::_getSelect();
				break;

			case 'textarea':
				$field = self::_getTextarea();
				break;

			case 'div':
				$field = self::_getDiv();
				break;

			case 'color':
			case 'colorpicker':
				$field = self::_getColorPicker();
				break;

			default:
				return sprintf( 'Invalid formfield type: %s', self::$type);
				break;
		}

		self::_substituteAttributes( $field );

		// flush members
		self::_reset();

		self::$fieldCount++;

		return $field;
	}
}