<?php

/**
 * Created by PhpStorm.
 * User: marco
 * Date: 06/07/18
 * Time: 14.05
 *
 * Class o66TheModel
 *
 *           _____ _____________         __  ___          __     __________
 *    ____  / ___// ___/_  __/ /_  ___  /  |/  /___  ____/ /__  / / ____/ /___ ___________
 *   / __ \/ __ \/ __ \ / / / __ \/ _ \/ /|_/ / __ \/ __  / _ \/ / /   / / __ `/ ___/ ___/
 *  / /_/ / /_/ / /_/ // / / / / /  __/ /  / / /_/ / /_/ /  __/ / /___/ / /_/ (__  |__  )
 *  \____/\____/\____//_/ /_/ /_/\___/_/  /_/\____/\__,_/\___/_/\____/_/\__,_/____/____/
 *
 *
 */
class o66TheModel {
	private static $is_initialized = false;
	private static $jedi = 'Darth Vader: ';
	private static $site_ID = 0;
	private static $site_parent_ID = 0;
	private static $site_model_type = '';
	private static $site_name = '';
	private static $site_content = '';

	//the site and the root model
	private static $the_site_model = null;
	private static $the_root_model = null;

	//Static arrays. They guard all the data in the software
	//GOOD OLD RELATIONAL id->fields LINKED WITH OneModel ID
	//TODO ADD model->view->connection
	private static $the_model_flat_array = array();
	private static $model_id_count = 0;
	private static $the_model_fields_array = array();
	private static $the_parent_array = null;
	private static $the_children_array = null;


	//Static arrays to cache temp data and model internal sessions;
	private static $the_model_cache = array();

	/**
	 *  Singleton Constructor
	 */
	private static function initialize() {

		//Prevents re-init !important
		if ( self::$is_initialized ) {
			return;
		}

		//we need the global $post from wordpress values,
		//  and we'll use it in the root and the site models.
		global $post;
		$site_title       = get_bloginfo( 'name' );
		$site_description = get_bloginfo( 'description' );
		$site_url         = get_site_url();

		//we need to prepare the model array. And we need rebels to remember who is in charge.
		self::$jedi            .= ' He will join us or die, Master!';
		self::$site_ID         = 0;
		self::$site_parent_ID  = 0;
		self::$site_name       = o66_site_settings::site_slug;
		self::$site_content    = '<h1>' . $site_title . '</h1><p>' . $site_description . '</p>';
		self::$site_model_type = o66_model_types::site;

		//init cache
		self::$the_model_cache = array();

		//index etcs
		//TODO fare switch per archives e custom post types archives
		if ( ! is_home() ) {
			$root_post_object = $post;

			$root_model_ID         = 1;
			$root_model_name       = $root_post_object->post_name;
			$root_model_content    = '<h1>' . $root_post_object->post_title . '</h1><p>' . $root_post_object->post_content . '</p>';
			$root_model_model_type = o66_model_types::page; //it's a string but better to use constants
		} else {
			$home_id = get_option( 'page_for_posts' );

			$root_post_object = get_post( $home_id );

			$root_model_ID         = 1;
			$root_model_name       = $root_post_object->post_name;
			$root_model_content    = '<h1>' . $root_post_object->post_title . '</h1><p>' . $root_post_object->post_content . '</p>';
			$root_model_model_type = o66_model_types::index_post; //it's a string but better to use constants
		}

		//create the 2 OneModel Instance for the first generation of the model array
		// ID => 0 is the site oneModel
		// ID => 1 is the root oneModel. It's the parent of all children used in the page/document/whatever we
		// want to render with the view.
		self::$the_site_model       = new OneModel( self::$site_ID, self::$site_name, self::$site_content, self::$site_model_type );
		self::$the_root_model       = new OneModel( $root_model_ID, $root_model_name, $root_model_content, $root_model_model_type );
		self::$the_model_flat_array = array(

			0 => self::$the_site_model->getOneModelArray(),
			1 => self::$the_site_model->getOneModelArray()

		);

		//prepare array
		//  add the root model array into the site model, then put a link relationship children array
		self::$the_parent_array = array(
			self::$site_ID => null,
			$root_model_ID => self::$site_ID
		);

		self::$the_children_array = array(
			self::$site_ID => [ $root_model_ID ],
			$root_model_ID => []
		);


		//store array and set initial id count
		//self::$the_model_array = $site_model_array;
		self::$model_id_count = 1;

		//prepare the fields array.
		//  It stores all not strictly related model logic fields, like title and content, wp custom fields
		//  the fields are directed by model_type
		//	const model_ID = 'model_ID';
		//	const parent_model_ID = 'parent_model_ID';
		//	const model_children = 'model_children';
		//	const model_siblings = 'model_siblings';

		self::$the_model_fields_array = array(
			//the site node
			0 => array(
				//	o66_model_fields_keys::wp_id            => null,
				//	o66_model_fields_keys::wp_parent_id     => null,
				o66_model_fields_keys::model_ID => 0,
				o66_model_fields_keys::model_children => array(1),
				o66_model_fields_keys::title     => $site_title,
				o66_model_fields_keys::content   => $site_description,
				o66_model_fields_keys::summary   => $site_description,
				o66_model_fields_keys::permalink => $site_url,

				//o66_model_fields_keys::wp_menu_position => null,
				//o66_model_fields_keys::wp_post_type     => null

			),
			//the root node
			1 => array(
				o66_model_fields_keys::model_ID => $root_model_ID,
				o66_model_fields_keys::parent_model_ID => 0,
				o66_model_fields_keys::wp_id            => $root_post_object->ID,
				o66_model_fields_keys::wp_parent_id     => $root_post_object->post_parent,
				o66_model_fields_keys::title            => $root_post_object->post_title,
				//	o66_model_fields_keys::content          => $root_post_object->post_content,
				o66_model_fields_keys::content          => apply_filters( 'the_content', $root_post_object->post_content ),
				o66_model_fields_keys::summary          => $root_post_object->post_excerpt,
				o66_model_fields_keys::permalink        => get_the_permalink( $root_post_object->ID ),
				o66_model_fields_keys::wp_menu_position => $root_post_object->menu_order,
				o66_model_fields_keys::wp_post_type     => $root_post_object->post_type,
				o66_model_fields_keys::wp_date          => $root_post_object->post_date

			),

		);
		//var_dump(self::$the_model_fields_array);

		//TODO better singleton?
		self::$is_initialized = true;
	}

	/**
	 * @return array
	 */
	public static function getTheChildrenArray() {
		self::initialize();

		return self::$the_children_array;
	}


	/**
	 * @return OneModel
	 */
	public static function getTheSiteModel() {
		self::initialize();

		return self::$the_site_model;
	}

	/**
	 * @return OneModel
	 */
	public static function getTheRootModel() {
		self::initialize();

		return self::$the_root_model;
	}


	/**
	 * @return array
	 */
	public static function getTheModelFlatArray(): array {
		self::initialize();

		return self::$the_model_flat_array;
	}


	/**
	 * @return array
	 */
	public static function getTheModelFieldsArray(): array {
		self::initialize();

		return self::$the_model_fields_array;
	}



	/**
	 * Local Class Cache and Internal Session's stuff.
	 */

	/**
	 * @return array
	 */
	public static function getTheModelCache(): array {
		self::initialize();

		return self::$the_model_cache;
	}

	/**
	 * @param array $the_model_cache
	 */
	private static function setTheModelCache( array $the_model_cache ) {
		self::initialize();
		self::$the_model_cache = $the_model_cache;
	}

	public static function resetTheModelCache() {
		self::initialize();
		self::$the_model_cache = array();
	}

	/**
	 * @param $key string Array's Key to Edit
	 * @param $item mixed value to store
	 */
	public static function setModelCacheItem( $key, $item ) {
		self::initialize();
		if ( array_key_exists( $key, self::$the_model_cache ) ) {
			self::$the_model_cache[ $key ] = $item;
		} else {
			self::$the_model_cache[ $key ] = $item;
		}
	}

	/**
	 * @param $key string Array's Key to Get
	 *
	 * @return bool|mixed
	 */
	public static function getModelCacheItem( $key ) {
		self::initialize();
		if ( array_key_exists( $key, self::$the_model_cache ) ) {
			$item = self::$the_model_cache[ $key ];
		} else {
			$item = false;
		}

		return $item;
	}

	//THE GLOBAL ID COUNT

	/**
	 * @return int
	 */
	public static function getModelIdCount(): int {
		self::initialize();

		return self::$model_id_count;
	}

	/**
	 * @param int $model_id_count
	 */
	public static function setModelIdCount( int $model_id_count ) {
		self::initialize();
		self::$model_id_count = $model_id_count;
	}

	/**
	 * Raise the internal Count by one and get the new ID/COUNT
	 * @return int
	 */
	public static function incrementModelCountAngGetIt(): int {
		self::initialize();
		$count = self::$model_id_count;
		$count ++;
		self::$model_id_count = $count;

		return $count;
	}

	/**
	 * METHODS and UTILITIES
	 */

	/**
	 * @param object $wp_post_object
	 * @param bool $attach_fields
	 *
	 * @return OneModel
	 */
	public static function createOneModelFromWpPostObject( $wp_post_object, $attach_fields = true ) {
		self::initialize();
		//$last_model_count = self::getModelIdCount();
		$this_model_count = self::incrementModelCountAngGetIt();
		//self::setModelIdCount( $this_model_count );

		$this_model_wp_post_type = $wp_post_object->post_type;
		$this_model_ID           = $this_model_count;
		$this_model_name         = $wp_post_object->post_name;
		$this_model_content      = '<h1>' . $wp_post_object->post_title . '</h1><p>' . $wp_post_object->post_content . '</p>';
		$this_model_model_type   = $this_model_wp_post_type; //it's a string but better to use constants


		//title and peramlink exception
		$field_permalink = get_the_permalink( $wp_post_object->ID );
		$field_title     = $wp_post_object->post_title;
		if ( $this_model_wp_post_type === o66_model_types::wp_menu_item ) {
			//$field_title = get_the_title($wp_post_object->ID);
			$field_title     = $wp_post_object->title;
			$field_permalink = $wp_post_object->url;

		}

		$this_model = new OneModel( $this_model_ID, $this_model_name, $this_model_content, $this_model_model_type );


		self::$the_model_flat_array[ $this_model_count ] = $this_model->getOneModelArray();

		if ( $attach_fields ) {


			self::$the_model_fields_array[ $this_model_ID ] = array(
				o66_model_fields_keys::model_ID => $this_model_ID,
				o66_model_fields_keys::wp_id            => $wp_post_object->ID,
				o66_model_fields_keys::wp_parent_id     => $wp_post_object->post_parent,
				o66_model_fields_keys::title            => $field_title,
				//o66_model_fields_keys::content          => $wp_post_object->post_content,
				o66_model_fields_keys::content          => apply_filters( 'the_content', $wp_post_object->post_content ),
				o66_model_fields_keys::summary          => $wp_post_object->post_excerpt,
				o66_model_fields_keys::permalink        => $field_permalink,
				o66_model_fields_keys::wp_menu_position => $wp_post_object->menu_order,
				o66_model_fields_keys::wp_post_type     => $wp_post_object->post_type,
				o66_model_fields_keys::wp_date          => $wp_post_object->post_date,
			);

		}

		//self::$the_parent_array[$this_model_ID] = $this_model_parent_ID;

		return $this_model;
	}

	/**
	 * @param OneModel $one_model
	 * @param integer $parent_id_to_append
	 */
	public static function appendOneModelToTheModel( $one_model, $parent_id_to_append = 1 ) {
		self::initialize();

		$one_model_array = $one_model->getOneModelArray();
		$one_model_ID    = $one_model_array['ID'];
		//update the parent array and parent field in the fields array
		self::$the_model_fields_array[ $one_model_ID ]['model_parent_ID'] = $parent_id_to_append;
		self::$the_parent_array[ $one_model_ID ] = $parent_id_to_append;

		//update the children array and children field in fields array
		if ( ! isset( self::$the_children_array[ $parent_id_to_append ] ) ) {

			self::$the_children_array[ $parent_id_to_append ] = array( $one_model_ID );
			self::$the_model_fields_array[ $parent_id_to_append ][o66_model_fields_keys::model_children][] = $one_model_ID;



		} else {
			array_push( self::$the_children_array[ $parent_id_to_append ], $one_model_ID );
			self::$the_model_fields_array[ $parent_id_to_append ][o66_model_fields_keys::model_children][] = $one_model_ID;
			//self::$the_model_fields_array[ $parent_id_to_append ]['ninetto'] = 'aveva figli';
			//array_push( self::$the_model_fields_array[ $parent_id_to_append ]['model_children'], $one_model_ID );
		}



	}

	/**
	 * @param string $menu_location
	 *
	 * @return OneModel
	 */
	public static function createOneModelFromWpMenu( $menu_location ) {
		self::initialize();
		$menu_model_ID      = self::incrementModelCountAngGetIt();
		$menu_model_name    = $menu_location;
		$menu_model_content = $menu_location . ' menu items array';
		$menu_model_type    = o66_model_types::nav;

		$menu_model = new OneModel( $menu_model_ID, $menu_model_name, $menu_model_content, $menu_model_type );

		self::$the_model_flat_array[ $menu_model_ID ] = $menu_model->getOneModelArray();

		self::$the_model_fields_array[ $menu_model_ID ] = array(
			//	o66_model_fields_keys::wp_id            => null,
			//	o66_model_fields_keys::wp_parent_id     => null,
			o66_model_fields_keys::title     => $menu_model_name,
			o66_model_fields_keys::content   => $menu_model_content,
			o66_model_fields_keys::summary   => $menu_model_content,
			o66_model_fields_keys::permalink => '#' . $menu_model_name,
			//o66_model_fields_keys::wp_menu_position => null,
			//o66_model_fields_keys::wp_post_type     => null

		);

		$locations = get_nav_menu_locations();

		//TODO decidere come stabilire primary
		$menu = get_term( $locations[ $menu_location ], 'nav_menu' );

		$menu_items = wp_get_nav_menu_items( $menu->term_id );

		foreach ( $menu_items as $menu_item ) {
			//prent_r($menu_item);
			$this_menu_item_model = o66TheModel::createOneModelFromWpPostObject( $menu_item );
			$this_menu_item_array = $this_menu_item_model->getOneModelArray();
			//var_dump($menu_item->menu_item_parent);
			if ( intval( $menu_item->menu_item_parent ) !== 0 ) {
				$fields_array = self::$the_model_fields_array;
				$flat_array   = self::$the_model_flat_array;

				foreach ( $fields_array as $field_key => $fields_item ) {
					if ( $flat_array[ $field_key ]['model_type'] === o66_model_types::wp_menu_item ) {
						if ( $field_key !== 0 ) {
							if ( isset( $fields_item[ o66_model_fields_keys::wp_id ] ) ) {
								$this_field_item_ID = $fields_item[ o66_model_fields_keys::wp_id ];
								if ( $this_field_item_ID === intval( $menu_item->menu_item_parent ) ) {

									o66TheModel::appendOneModelToTheModel( $this_menu_item_model, $field_key );
								}
							}
						}
					}


				}

			} else {
				o66TheModel::appendOneModelToTheModel( $this_menu_item_model, $menu_model->getID() );
			}
			//prent_r($this_menu_item_array);


		}


		return $menu_model;

	}

	/**
	 * Return The Tree Model
	 * @return array
	 */
	public static function getTheTreeModelArray() {
		self::initialize();

		//ref
		//https://stackoverflow.com/questions/8840319/build-a-tree-from-a-flat-array-in-php
		//https://wordpress.stackexchange.com/questions/170033/convert-output-of-nav-menu-items-into-a-tree-like-multidimensional-array
		//get all the arrays needed to bake the final model ready to render

		$parents_array = self::$the_parent_array;

		function prepare_tree( $models_parent_array ) {

			$simple_tree = array();
			//clean array and assign relationship
			foreach ( $models_parent_array as $child_model => $this_child_parent ) {
				if ( $child_model !== $this_child_parent ) {
					if ( isset( $this_child_parent ) ) {
						$simple_tree[ $child_model ]['ID'] = $child_model;
						//	$simple_tree[ $this_child_parent ]['children'][] = $child_model;
						$simple_tree[ $child_model ]['parent_ID'] = $this_child_parent;
					}
				}
			}
			//TODO VERIFICARE CONTE E LIMITS, RIMUOVERE LIMIT
			$limit       = 25;
			$limit_count = 0;

			function build_tree( &$simple_tree, $parent_id = 0, $limit, &$count ) {
				$branch = array();
				$count ++;
				if ( $count === $limit ) {
					return [ 0 => 'overflow' ];
				}

				foreach ( $simple_tree as $model_tree ) {

					if ( $model_tree['parent_ID'] === $parent_id ) {
						//echo 'itero  ' . $model_tree['parent_ID'] . ' con parent id passato: ' . $parent_id.'<br>';
						$children = build_tree( $simple_tree, $model_tree['ID'], $limit, $count );
						if ( $children ) {
							$model_tree['children'] = $children;
						}
						$branch[ $model_tree['ID'] ] = $model_tree;
						unset( $simple_tree[ $model_tree['ID'] ] );
					}
				}

				return $branch;
			}

			$return_array = build_tree( $simple_tree, 0, $limit, $limit_count );

			return $return_array;
		}

		$tree = prepare_tree( $parents_array );

		return $tree;
	}
}