<?php
namespace SabaiApps\Directories\Component\Form\Field;

use SabaiApps\Directories\Component\Form\Form;
use SabaiApps\Directories\Exception;

/**
 * FieldsetField provides a container for grouping multiple form fields.
 * Handles initialization, value processing, cleanup, and rendering for child fields.
 */
class FieldsetField extends AbstractField {

	/**
	 * Initializes the fieldset and its child fields.
	 *
	 * @param string $name
	 * @param array  $data
	 * @param Form   $form
	 */
	public function formFieldInit( $name, array &$data, Form $form ) {
		if ( isset( $form->settings['#tree_allow_override'] ) && ! $form->settings['#tree_allow_override'] ) {
			// #tree setting not allowed to be overridden for the whole form
			$data['#tree_allow_override'] = false;
			$data['#tree']                = $form->settings['#tree'];
		} elseif ( ! empty( $form->settings['#tree'] ) && ! isset( $data['#tree'] ) ) {
			// Inherit form #tree setting
			$data['#tree'] = true;
		}

		foreach ( array_keys( $data['#children'] ) as $weight ) {
			if ( ! is_int( $weight ) ) {
				continue;
			}

			foreach ( array_keys( $data['#children'][ $weight ] ) as $_key ) {
				$_data  =& $data['#children'][ $weight ][ $_key ];
				$_data += $form->defaultFieldSettings();
				// Propagate required/disabled settings from parent if not set on child
				if ( ! isset( $_data['#required'] ) && isset( $data['#required'] ) ) {
					$_data['#required'] = $data['#required'];
				}
				if ( ! isset( $_data['#required_error_message'] ) && isset( $data['#required_error_message'] ) ) {
					$_data['#required_error_message'] = $data['#required_error_message'];
				}
				if ( ! isset( $_data['#disabled'] ) && ! empty( $data['#disabled'] ) ) {
					$_data['#disabled'] = true;
				}
				// Append parent element name if #tree is true for the parent element and #tree is not set to false explicitly for the current element
				if ( ! empty( $data['#tree'] ) && ( ! $data['#tree_allow_override'] || false !== @$_data['#tree'] ) ) {
					$_data['#tree'] = true;
					$_name          = sprintf( '%s[%s]', $name, $_key );
					if ( ! isset( $_data['#value'] ) && isset( $data['#value'][ $_key ] ) ) {
						$_data['#value'] = $data['#value'][ $_key ];
					}
					if ( ! isset( $_data['#default_value'] ) && isset( $data['#default_value'][ $_key ] ) ) {
						$_data['#default_value'] = $data['#default_value'][ $_key ];
					}
				} else {
					$_name = $_key;
				}
				$_data['#tree_allow_override'] = empty( $data['#tree_allow_override'] ) ? false : ! empty( $_data['#tree_allow_override'] );
				$form->initField( $_name, $_data );
			}
		}
	}

	/**
	 * Handles value processing and validation for the fieldset and its children on submit.
	 *
	 * @param mixed $value
	 * @param array $data
	 * @param Form  $form
	 */
	public function formFieldSubmit( &$value, array &$data, Form $form ) {
		if ( ! empty( $data['#tree'] ) ) {
			if ( ! is_array( $value ) ) {
				$value = array();
			}
			foreach ( array_keys( $data['#children'] ) as $weight ) {
				if ( ! is_int( $weight ) ) {
					continue;
				}

				foreach ( array_keys( $data['#children'][ $weight ] ) as $key ) {
					$_data =& $data['#children'][ $weight ][ $key ];
					if ( ! empty( $_data['#disabled'] )
						|| in_array( $_data['#type'], array( 'addmore' ) )
						|| $form->isInvisibleField( $_data )
					) {
						unset( $value[ $key ] );
						continue;
					}

					if ( empty( $value ) || ! array_key_exists( $key, $value ) ) {
						$value[ $key ] = null;
					}

					// Send form submit notification to the field
					try {
						$this->_application->Form_Fields_impl( $_data['#type'], $form->settings['#build_id'] )->formFieldSubmit( $value[ $key ], $_data, $form );
					} catch ( Exception\IException $e ) {
						// Catch any application level exception that might occur and display it as a form element error.
						$form->setError( $e->getMessage(), $_data );
					}

					if ( isset( $_data['#name'] ) && $form->hasError( $_data['#name'] ) ) {
						continue;
					}

					// Copy the value to be used in subsequent validation steps
					$_value =& $value[ $key ];

					if ( empty( $form->settings['#skip_validate'] )
						&& ! empty( $_data['#element_validate'] )
					) {
						// Process custom validations if any
						foreach ( $_data['#element_validate'] as $callback ) {
							try {
								$this->_application->System_Util_callUserFuncArray( $callback, array( $form, &$_value, $_data ) );
							} catch ( Exception\IException $e ) {
								$form->setError( $e->getMessage(), $_data );
							}
						}
					}
				}
			}
			if ( ! empty( $value ) && ! empty( $data['#remove_empty'] ) ) {
				$value = array_filter( $value );
			}
			return;
		}

		// Process child fields for non-tree fieldsets
		foreach ( array_keys( $data['#children'] ) as $weight ) {
			if ( ! is_int( $weight ) ) {
				continue;
			}

			foreach ( array_keys( $data['#children'][ $weight ] ) as $key ) {
				$_data =& $data['#children'][ $weight ][ $key ];
				if ( ! empty( $_data['#disabled'] )
					|| in_array( $_data['#type'], array( 'addmore' ) )
					|| $form->isInvisibleField( $_data )
				) {
					unset( $form->values[ $key ] );
					continue;
				}

				// Since the name of element does not belong to the group name hierarchy, fetch the element's value from the global scope.
				if ( ! isset( $form->values[ $key ] ) ) {
					$form->values[ $key ] = null;
				}

				// Send form submit notification to the element.
				try {
					$this->_application->Form_Fields_impl( $_data['#type'], $form->settings['#build_id'] )->formFieldSubmit( $form->values[ $key ], $_data, $form );
				} catch ( Exception\IException $e ) {
					// Catch exception that might occur and display it as a form element error.
					$form->setError( $e->getMessage(), $_data );
				}

				if ( $form->hasError( $key ) ) {
					continue;
				}

				// Copy the value to be used in subsequent validation steps
				$_value =& $form->values[ $key ];

				// Process custom validations if any
				foreach ( $_data['#element_validate'] as $callback ) {
					try {
						$this->_application->System_Util_callUserFuncArray( $callback, array( $form, &$_value, $_data ) );
					} catch ( Exception\IException $e ) {
						$form->setError( $e->getMessage(), $_data );
					}
				}
			}
		}
	}

	public function formFieldCleanup( array &$data, Form $form ) {
		$form->cleanupChildFields( $data );
	}

	public function formFieldRender( array &$data, Form $form ) {
		$form->renderChildFields( $data );
	}
}
