<?php

/**
 * Class for adding to and working with all data
 * related to the current page.
 *
 * @since 1.0
 */
final class FLPageData {

	/**
	 * An array of groups for grouping related properties.
	 *
	 * @since 1.0
	 * @access private
	 * @var array $groups
	 */
	static private $groups;

	/**
	 * An array of all registered properties stored by object
	 * type such as post, archive, and 404.
	 *
	 * @since 1.0
	 * @access private
	 * @var array $properties
	 */
	static private $properties;

	/**
	 * Cached values that have been retrieved.
	 *
	 * @since 1.0
	 * @access private
	 * @var array $values
	 */
	static private $values;

	/**
	 * A settings object for the current property value
	 * that is being pulled. Mainly useful for hooks
	 * that need to pass settings around.
	 *
	 * @since 1.0
	 * @access private
	 * @var object $settings
	 */
	static private $settings = null;

	/**
	 * Cached value for the is_archive method.
	 *
	 * @since 1.0
	 * @access private
	 * @var bool $is_archive
	 */
	static private $is_archive = null;

	/**
	 * Initialize page data.
	 *
	 * @since 1.0
	 * @return void
	 */
	static public function init() {
		self::init_defaults();
		add_action( 'wp', __CLASS__ . '::init_properties', 1 );
	}

	static private $id_form = [];

	/**
	 * Initialize default values for internal vars.
	 *
	 * @since 1.0
	 * @return void
	 */
	static public function init_defaults() {
		self::$groups = array(
			'general'  => array(
				'label'  => __( 'General', 'fl-builder' ),
				'render' => true,
			),
			'archives' => array(
				'label'  => __( 'Archives', 'fl-builder' ),
				'render' => true,
			),
			'posts'    => array(
				'label'  => __( 'Posts', 'fl-builder' ),
				'render' => true,
			),
			'comments' => array(
				'label'  => __( 'Comments', 'fl-builder' ),
				'render' => true,
			),
			'author'   => array(
				'label'  => __( 'Author', 'fl-builder' ),
				'render' => true,
			),
			'site'     => array(
				'label'  => __( 'Site', 'fl-builder' ),
				'render' => true,
			),
			'user'     => array(
				'label'  => __( 'User', 'fl-builder' ),
				'render' => true,
			),
			'advanced' => array(
				'label'  => __( 'Advanced', 'fl-builder' ),
				'render' => true,
			),
			'term'     => array(
				'label'  => __( 'Term', 'fl-builder' ),
				'render' => true,
			),
		);

		self::$properties = array(
			'archive' => array(),
			'post'    => array(),
			'site'    => array(),
		);

		self::$values = array(
			'archive' => array(),
			'post'    => array(),
			'site'    => array(),
		);

		self::$id_form = array(
			'type'        => 'text',
			'label'       => __( 'ID', 'fl-builder' ),
			'default'     => '',
			'description' => __( 'Which Post ID will the system use for this data connection? Leave Blank to use the current page/post', 'fl-builder' ),
		);
	}

	/**
	 * Loads page data properties.
	 *
	 * @since 1.0
	 * @return void
	 */
	static public function init_properties() {
		// Let devs hook into page data
		/**
		 * Fires to allow registering custom page data properties for dynamic content fields.
		 */
		do_action( 'fl_page_data_add_properties' );
	}

	/**
	 * Adds a property group for grouping related properties.
	 *
	 * @since 1.0
	 * @param string $key
	 * @param array  $data
	 * @return void
	 */
	static public function add_group( $key, $data = array() ) {
		if ( isset( self::$groups[ $key ] ) ) {
			return;
		}

		self::$groups[ $key ] = array_merge( array(
			'label'  => $key,
			'render' => true,
		), $data );
	}

	/**
	 * Returns the groups array.
	 *
	 * @since 1.0
	 * @return array
	 */
	static public function get_groups() {
		return self::$groups;
	}

	/**
	 * Adds a property and associated data for archives.
	 *
	 * @since 1.0
	 * @param string $key
	 * @param array  $data
	 * @return void
	 */
	static public function add_archive_property( $key, $data = array() ) {
		self::add_property( 'archive', $key, $data );
	}

	/**
	 * Adds a property and associated data for posts.
	 *
	 * @since 1.0
	 * @param string $key
	 * @param array  $data
	 * @return void
	 */
	static public function add_post_property( $key, $data = array() ) {
		$data = array_merge( array(
			'post_type' => 'all',
		), $data );

		self::add_property( 'post', $key, $data );

		/**
		 * Auto insert 'post_id' setting (#3452)
		 */
		// self::add_post_property_settings_fields( $key, [ 'post_id' => self::$id_form ] );
	}

	/**
	 * Adds a property and associated data for term.
	 *
	 * @since 1.0
	 * @param string $key
	 * @param array  $data
	 * @return void
	 */
	static public function add_term_property( $key, $data = array() ) {
		$data = array_merge( array(
			'post_type' => 'all',
		), $data );
		self::add_property( 'term', $key, $data );
	}

	/**
	 * Adds a property and associated data for the site.
	 *
	 * @since 1.0
	 * @param string $key
	 * @param array  $data
	 * @return void
	 */
	static public function add_site_property( $key, $data = array() ) {
		self::add_property( 'site', $key, $data );
	}

	/**
	 * Adds a property and associated data.
	 *
	 * @since 1.0
	 * @param string $object
	 * @param string $key
	 * @param array  $data
	 * @return void
	 */
	static public function add_property( $object, $key, $data = array() ) {
		if ( isset( self::$properties[ $object ][ $key ] ) ) {
			return;
		}

		self::$properties[ $object ][ $key ] = array_merge( array(
			'object' => $object,
			'key'    => $key,
			'group'  => 'general',
			'label'  => $key,
			'type'   => 'string',
			'form'   => false,
			'getter' => function () {
				return ''; },
		), $data );
	}

	/**
	 * Returns data for a single property.
	 *
	 * @since 1.0
	 * @param string $object
	 * @param string $key
	 * @return array
	 */
	static public function get_property( $object, $key ) {
		return isset( self::$properties[ $object ][ $key ] ) ? self::$properties[ $object ][ $key ] : false;
	}

	/**
	 * Returns the properties array.
	 *
	 * @since 1.0
	 * @return array
	 */
	static public function get_properties() {
		return self::$properties;
	}

	/**
	 * Returns the value for a property.
	 *
	 * @since 1.0
	 * @param string $object The type of object to return a value for.
	 * @param string $key The property key.
	 * @param object $settings A settings object to pass to the getter.
	 * @return mixed
	 */
	static public function get_value( $object, $key, $settings = null ) {
		global $post;
		$original_post = $post;
		$property      = self::get_property( $object, $key );

		// Property or getter doesn't exist, return an empty string.
		if ( ! $property || ! is_callable( $property['getter'] ) ) {
			return '';
		}

		if ( isset( $settings->post_id ) ) {
			$post = get_post( $settings->post_id );
		}

		// Get the value.
		if ( $property['form'] ) {
			$defaults       = FLBuilderModel::get_settings_form_defaults( $property['form']['id'] );
			$settings       = ! $settings ? new stdClass() : $settings;
			$settings       = (object) array_merge( (array) $defaults, (array) $settings );
			self::$settings = $settings;
			$value          = call_user_func( $property['getter'], $settings, $property );
			self::$settings = null;
		} else {
			$value = call_user_func( $property['getter'] );
		}

		// Cache the value.
		self::$values[ $object ][ $key ] = $value;

		$post = $original_post;

		// Return the value after running it through the expression function
		return FLThemeBuilderFieldConnections::general_compare( $settings, $value );
	}

	/**
	 * Returns an array of all property values.
	 *
	 * @since 1.0
	 * @param string $object The type of object to return values for.
	 * @return array
	 */
	static public function get_values( $object = null ) {
		// Return all values if we don't have an object.
		if ( ! $object ) {

			foreach ( self::$properties as $object => $properties ) {
				self::$values[ $object ] = self::get_values( $object );
			}

			return self::$values;
		}

		// Return the values for a single object.
		foreach ( self::$properties[ $object ] as $key => $data ) {

			if ( ! isset( self::$values[ $object ][ $key ] ) ) {
				self::$values[ $object ][ $key ] = self::get_value( $object, $key );
			}
		}

		return self::$values[ $object ];
	}

	/**
	 * Returns the settings for the current value that
	 * is being pulled, if settings are set.
	 *
	 * @since 1.0
	 * @return object
	 */
	static public function get_current_settings() {
		return self::$settings;
	}

	/**
	 * Adds a settings form associated with an archive property.
	 *
	 * @since 1.0
	 * @param string $key
	 * @param array  $data
	 * @return void
	 */
	static public function add_archive_property_settings_form( $key, $data = array() ) {
		self::add_property_settings_form( 'archive', $key, $data );
	}

	/**
	 * Adds a settings form associated with a post property.
	 *
	 * @since 1.0
	 * @param string $key
	 * @param array  $data
	 * @return void
	 */
	static public function add_post_property_settings_form( $key, $data = array() ) {
		self::add_property_settings_form( 'post', $key, $data );
	}

	/**
	 * Adds a settings form associated with a site property.
	 *
	 * @since 1.0
	 * @param string $key
	 * @param array  $data
	 * @return void
	 */
	static public function add_site_property_settings_form( $key, $data = array() ) {
		self::add_property_settings_form( 'site', $key, $data );
	}

	/**
	 * Adds a settings form associated with a term property.
	 *
	 * @since 1.0
	 * @param string $key
	 * @param array  $data
	 * @return void
	 */
	static public function add_term_property_settings_form( $key, $data = array() ) {
		self::add_property_settings_form( 'term', $key, $data );
	}

	/**
	 * Adds a settings form associated with a property.
	 *
	 * @since 1.0
	 * @param string $object
	 * @param string $key
	 * @param array  $data
	 * @return void
	 */
	static public function add_property_settings_form( $object, $key, $data = array() ) {
		$property = self::get_property( $object, $key );
		$form_id  = 'page-data-' . $object . '-' . $key;
		$form     = array(
			'title' => $property['label'],
			'tabs'  => isset( $data['tabs'] ) ? $data['tabs'] : $data,
		);

		self::$properties[ $object ][ $key ]['form'] = array(
			'id'  => $form_id,
			'css' => isset( $data['tabs'] ) && isset( $data['css'] ) ? $data['css'] : null,
			'js'  => isset( $data['tabs'] ) && isset( $data['js'] ) ? $data['js'] : null,
		);
		/**
		 * Auto insert 'post_id' setting (#3452)
		 */
		// if ( 'post' === $object ) {
		//  if ( is_array( $form['tabs']['general']['sections']['general']['fields'] ) ) {
		//      $form['tabs']['general']['sections']['general']['fields']['post_id'] = self::$id_form;
		//  } else {
		//      $form['tabs']['general']['sections']['general']['fields'] = [ 'post_id' => self::$id_form ];
		//  }
		// }

		FLBuilderModel::register_settings_form( $form_id, $form );
	}

	/**
	 * Adds a settings fields to a form associated with a archive property.
	 *
	 * @since 1.0
	 * @param string $key
	 * @param array  $data
	 * @return void
	 */
	static public function add_archive_property_settings_fields( $key, $data = array() ) {
		self::add_property_settings_fields( 'archive', $key, $data );
	}

	/**
	 * Adds a settings fields to a form associated with a post property.
	 *
	 * @since 1.0
	 * @param string $key
	 * @param array  $data
	 * @return void
	 */
	static public function add_post_property_settings_fields( $key, $data = array() ) {
		self::add_property_settings_fields( 'post', $key, $data );
	}

	/**
	 * Adds a settings fields to a form associated with a site property.
	 *
	 * @since 1.0
	 * @param string $key
	 * @param array  $data
	 * @return void
	 */
	static public function add_site_property_settings_fields( $key, $data = array() ) {
		self::add_property_settings_fields( 'site', $key, $data );
	}

	/**
	 * Adds a settings fields to a form associated with a term property.
	 *
	 * @since 1.0
	 * @param string $key
	 * @param array  $data
	 * @return void
	 */
	static public function add_term_property_settings_fields( $key, $data = array() ) {
		self::add_property_settings_fields( 'term', $key, $data );
	}

	/**
	 * Adds a settings fields to a form associated with a property.
	 * This makes it easy to add settings fields without all of the
	 * necessary form config as most properties won't need many fields.
	 *
	 * @since 1.0
	 * @param string $object
	 * @param string $key
	 * @param array  $data
	 * @return void
	 */
	static public function add_property_settings_fields( $object, $key, $data = array() ) {
		self::add_property_settings_form( $object, $key, array(
			'css'  => isset( $data['fields'] ) && isset( $data['css'] ) ? $data['css'] : null,
			'js'   => isset( $data['fields'] ) && isset( $data['js'] ) ? $data['js'] : null,
			'tabs' => array(
				'general' => array(
					'title'    => '',
					'sections' => array(
						'general' => array(
							'title'  => '',
							'fields' => isset( $data['fields'] ) ? $data['fields'] : $data,
						),
					),
				),
			),
		) );
	}

	/**
	 * Checks to see if the current page is an archive page
	 * or a theme layout set to override one.
	 *
	 * @since 1.0
	 * @return bool
	 */
	static public function is_archive() {
		global $wp_the_query;
		global $post;

		if ( null !== self::$is_archive ) {
			return self::$is_archive;
		}

		self::$is_archive = false;

		if ( is_archive() ) {
			self::$is_archive = true;
		} elseif ( is_object( $wp_the_query->post ) && 'fl-theme-layout' == $wp_the_query->post->post_type ) {

			$layout_type = get_post_meta( $wp_the_query->post->ID, '_fl_theme_layout_type', true );

			if ( 'archive' == $layout_type ) {
				self::$is_archive = true;
			}
		} elseif ( is_singular() && $post->post_type != $wp_the_query->post->post_type ) {
			self::$is_archive = true;
		}

		return self::$is_archive;
	}
}

FLPageData::init();
