<?php
namespace Jet_Menu\Render;

use Jet_Menu\Endpoints\Get_Elementor_Template_Content;
class Manager {

	/**
	 * A reference to an instance of this class.
	 *
	 * @since 1.0.0
	 * @var   object
	 */
	private static $instance = null;

	/**
	 * @var null
	 */
	public $location_manager = null;

	/**
	 * @var array
	 */
	private $preloaded_menu_ids = array();

	/**
	 * @var array
	 */
	private $preloaded_template_ids = array();

	/**
	 * Returns the instance.
	 *
	 * @since  1.0.0
	 * @return object
	 */
	public static function get_instance() {

		// If the single instance hasn't been set, set it now.
		if ( null == self::$instance ) {
			self::$instance = new self;
		}

		return self::$instance;
	}

	/**
	 * Load files
	 */
	public function load_files() {

		$location_path = jet_menu_tools()->is_nextgen_mode() ? 'includes/render' : 'includes/render/legacy';

		require jet_menu()->plugin_path( "{$location_path}/location.php" );

		require jet_menu()->plugin_path( 'includes/render/base-render.php' );
		require jet_menu()->plugin_path( 'includes/render/render-modules/mega-menu-render.php' );
		require jet_menu()->plugin_path( 'includes/render/render-modules/mobile-menu-render.php' );
		require jet_menu()->plugin_path( 'includes/render/render-modules/block-editor-template-render.php' );
		require jet_menu()->plugin_path( 'includes/render/render-modules/elementor-template-render.php' );

		if ( jet_menu_tools()->is_nextgen_mode() ) {
			require jet_menu()->plugin_path( 'includes/render/walkers/mega-menu-walker.php' );
			require jet_menu()->plugin_path( 'includes/render/walkers/vertical-menu-walker.php' );
		} else {
			require jet_menu()->plugin_path( 'includes/render/walkers/legacy/main-menu-walker.php' );
			require jet_menu()->plugin_path( 'includes/render/walkers/legacy/vertical-menu-walker.php' );
		}
	}

	/**
	 * [generate_menu_raw_data description]
	 * @param  string  $menu_slug [description]
	 * @param  boolean $is_return [description]
	 * @return [type]             [description]
	 */
	public function generate_menu_raw_data( $menu_id = false, $with_render_data = false ) {

		if ( ! $menu_id ) {
			return false;
		}

		$menu_items = $this->get_menu_items_object_data( $menu_id );

		if ( $menu_items ) {
			_wp_menu_item_classes_by_context( $menu_items );
		}

		$items = array();

		if ( ! empty( $menu_items ) ) {
			foreach ( $menu_items as $key => $item ) {
				$item_id = $item->ID;
				$item_settings = jet_menu()->settings_manager->get_item_settings( $item_id );
				$item_content_type = isset( $item_settings['content_type'] ) ? $item_settings['content_type'] : 'default';
				$signature = '';
	
				switch ( $item_content_type ) {
					case 'default':
						$mega_template_id = get_post_meta( $item_id, 'jet-menu-item-block-editor', true );
						break;
					case 'elementor':
						$mega_template_id = get_post_meta( $item_id, 'jet-menu-item', true );
	
						break;
				}
	
				$template_id              = ( isset( $item_settings['enabled'] ) && filter_var( $item_settings['enabled'], FILTER_VALIDATE_BOOLEAN ) ) ? (int) $mega_template_id : false;
				$mega_render_data         = false;
				$mega_content_has_content = true;
				$use_ajax                 = false;
	
				if ( $template_id ) {
					switch ( $item_content_type ) {
						case 'default':
							$mega_template_dependencies = get_post_meta( $template_id, '_is_deps_ready', true );
							$is_content = ! $use_ajax || empty( $mega_template_dependencies ) ? true : false;
	
							$render_instance = new \Jet_Menu\Render\Block_Editor_Content_Render( [
								'template_id' => $template_id,
								'with_css'   => true,
								'is_content' => $is_content
							] );
	
							break;
						case 'elementor':
							$mega_template_dependencies = get_post_meta( $template_id, '_is_deps_ready', true );
							$is_content = ! $use_ajax || empty( $mega_template_dependencies ) ? true : false;
	
							$render_instance = new \Jet_Menu\Render\Elementor_Content_Render( [
								'template_id' => $template_id,
								'with_css'   => true,
								'is_content' => $is_content
							] );
	
							break;
					}
	
					if ( $is_content ) {
						update_post_meta( $template_id, '_is_deps_ready', 'true' );
					}
	
					if ( $render_instance &&  $with_render_data ) {
						$mega_render_data = $render_instance->get_render_data();
						$mega_render_data = apply_filters( 'jet-plugins/render/render-data', $mega_render_data, $template_id, $item_content_type );

						if ( ! $this->has_mega_template_content( $mega_render_data ) ) {
							$template_id              = false;
							$mega_render_data         = false;
							$mega_content_has_content = false;
						}
					}
				}
	
				$hide_item_text = ! empty( $item_settings['hide_item_text'] ) ? filter_var( $item_settings['hide_item_text'], FILTER_VALIDATE_BOOLEAN ) : false;
				$icon_type = isset( $item_settings['menu_icon_type'] ) ? $item_settings['menu_icon_type'] : 'icon';
	
	
				if ( jet_menu_tools()->is_nextgen_mode() ) {
					$icon_type = 'svg';
				}
	
				switch ( $icon_type ) {
					case 'icon':
						$item_icon = ! empty( $item_settings['menu_icon'] ) ? jet_menu_tools()->get_icon_html( $item_settings['menu_icon'], '' ) : '';
						break;
	
					case 'svg':
						$item_icon = ! empty( $item_settings['menu_svg'] ) ? jet_menu_tools()->get_svg_html( $item_settings['menu_svg'], false ) : '';
						break;
				}
	
				$badge_content_type = isset( $item_settings['menu_badge_type'] ) ? $item_settings['menu_badge_type'] : 'text';
	
				switch ( $badge_content_type ) {
					case 'text':
						$badge_content = isset( $item_settings[ 'menu_badge' ] ) ? $item_settings[ 'menu_badge' ] : false;
	
						break;
					case 'svg':
						$badge_content = ! empty( $item_settings['badge_svg'] ) ? jet_menu_tools()->get_svg_html( $item_settings['badge_svg'], false ) : false;;
	
						break;
				}

				if ( $template_id ) {
					$api_instance = new Get_Elementor_Template_Content();
					$signature    = $api_instance->generate_signature( $template_id );
				}

				$items[] = array (
					'id'                     => 'item-' . $item_id,
					'name'                   => $item->title,
					'attrTitle'              => ! empty( $item->attr_title ) ? $item->attr_title : false,
					'description'            => $item->description,
					'url'                    => $item->url,
					'target'                 => ! empty( $item->target ) ? $item->target : false,
					'xfn'                    => ! empty( $item->xfn ) ? $item->xfn : false,
					'itemParent'             => ! empty( $item->menu_item_parent ) ? 'item-' . $item->menu_item_parent : false,
					'itemId'                 => $item_id,
					'megaTemplateId'         => $template_id,
					'megaContent'            => $mega_render_data,
					'megaContentType'        => $item_content_type,
					'megaContentHasContent'  => $mega_content_has_content,
					'open'                   => false,
					'badgeContent'           => $badge_content,
					'itemIcon'               => $item_icon,
					'hideItemText'           => $hide_item_text,
					'classes'                => $item->classes,
					'signature'              => $signature,
				);
			}
		}

		if ( ! empty( $items ) ) {
			$items = $this->buildItemsTree( $items, false );
		}

		$menu_data = array(
			'items' => $items,
		);

		return $menu_data;
	}

	/**
	 * [buildItemsTree description]
	 * @param  array   &$items   [description]
	 * @param  integer $parentId [description]
	 * @return [type]            [description]
	 */
	public function buildItemsTree( array &$items, $parentId = false ) {

		$branch = [];

		foreach ( $items as &$item ) {

			if ( $item['itemParent'] === $parentId ) {
				$children = $this->buildItemsTree( $items, $item['id'] );

				if ( $children && !$item['megaTemplateId'] ) {
					$item['children'] = $children;
				}

				$branch[ $item['id'] ] = $item;

				unset( $item );
			}
		}

		return $branch;

	}

	/**
	 * Check if rendered mega template data contains content that should keep item as submenu trigger.
	 *
	 * @param array|false $render_data Rendered template data.
	 *
	 * @return bool
	 */
	protected function has_mega_template_content( $render_data ) {

		$content = isset( $render_data['content'] ) ? $render_data['content'] : '';

		return jet_menu_tools()->has_meaningful_content( $content );
	}

	/**
	 * [get_menu_items_object_data description]
	 * @param  boolean $menu_id [description]
	 * @return [type]           [description]
	 */
	public function get_menu_items_object_data( $menu_id = false ) {

		if ( ! $menu_id ) {
			return false;
		}

		$menu = wp_get_nav_menu_object( $menu_id );

		$menu_items = wp_get_nav_menu_items( $menu );

		if ( ! $menu_items ) {
			return false;
		}

		return $menu_items;
	}

	/**
	 * Get menu ID for current location
	 *
	 * @param  [type] $location [description]
	 * @return [type]           [description]
	 */
	public function get_menu_id( $location = null ) {
		$locations = get_nav_menu_locations();

		return isset( $locations[ $location ] ) ? $locations[ $location ] : false;
	}

	/**
	 * Enqueue Elementor mega template styles before the page head is printed.
	 *
	 * Elementor widget style dependencies are discovered while rendering template content.
	 * When a mega menu is rendered after wp_head, the dependencies are printed late.
	 * Preloading keeps them in the head.
	 */
	public function enqueue_mega_template_assets() {

		if ( is_admin() || ! jet_menu_tools()->has_elementor() || ! class_exists( '\Elementor\Plugin' ) ) {
			return;
		}

		$enabled = apply_filters( 'jet-menu/elementor-template-assets/preload-enabled', true );

		if ( ! filter_var( $enabled, FILTER_VALIDATE_BOOLEAN ) ) {
			return;
		}

		$preload_data = $this->get_frontend_asset_preload_data();

		foreach ( $preload_data['menu_ids'] as $menu_id ) {
			$this->enqueue_menu_mega_template_assets( $menu_id );
		}

		foreach ( $preload_data['template_ids'] as $template_id ) {
			$this->enqueue_elementor_template_assets( $template_id );
		}

	}

	/**
	 * @return array
	 */
	private function get_frontend_asset_preload_data() {

		$preload_data = $this->merge_asset_preload_data(
			$this->get_location_asset_preload_data(),
			$this->get_theme_core_location_asset_preload_data(),
			$this->get_current_elementor_asset_preload_data()
		);

		$preload_data['menu_ids'] = $this->sanitize_ids(
			apply_filters( 'jet-menu/elementor-template-assets/menu-ids', $preload_data['menu_ids'] )
		);

		$preload_data['template_ids'] = $this->sanitize_ids(
			apply_filters( 'jet-menu/elementor-template-assets/template-ids', $preload_data['template_ids'] )
		);

		return $preload_data;
	}

	/**
	 * @return array
	 */
	private function get_empty_asset_preload_data() {
		return array(
			'menu_ids'     => array(),
			'template_ids' => array(),
		);
	}

	/**
	 * @param array ...$data_sets
	 *
	 * @return array
	 */
	private function merge_asset_preload_data( ...$data_sets ) {

		$merged = $this->get_empty_asset_preload_data();

		foreach ( $data_sets as $data ) {
			if ( ! is_array( $data ) ) {
				continue;
			}

			$merged['menu_ids']     = array_merge( $merged['menu_ids'], $data['menu_ids'] ?? array() );
			$merged['template_ids'] = array_merge( $merged['template_ids'], $data['template_ids'] ?? array() );
		}

		$merged['menu_ids']     = $this->sanitize_ids( $merged['menu_ids'] );
		$merged['template_ids'] = $this->sanitize_ids( $merged['template_ids'] );

		return $merged;
	}

	/**
	 * @param array $ids
	 *
	 * @return array
	 */
	private function sanitize_ids( $ids = array() ) {

		if ( ! is_array( $ids ) ) {
			return array();
		}

		$ids = array_filter( array_map( 'absint', $ids ) );

		return array_values( array_unique( $ids ) );
	}

	/**
	 * @return array
	 */
	private function get_location_asset_preload_data() {

		$locations = get_nav_menu_locations();
		$data      = $this->get_empty_asset_preload_data();

		if ( empty( $locations ) ) {
			return $data;
		}

		foreach ( $locations as $location => $menu_id ) {
			$settings = jet_menu()->settings_manager->get_settings( $menu_id );

			if ( empty( $settings[ $location ]['enabled'] ) || 'true' !== $settings[ $location ]['enabled'] ) {
				continue;
			}

			$data['menu_ids'][] = $menu_id;
		}

		if ( $this->is_location_mobile_render_for_asset_preload() ) {
			$data['template_ids'] = array_merge(
				$data['template_ids'],
				$this->get_location_mobile_template_ids_for_asset_preload()
			);
		}

		return $data;
	}

	/**
	 * @return array
	 */
	private function get_location_mobile_template_ids_for_asset_preload() {
		return array(
			jet_menu()->settings_manager->options_manager->get_option( 'jet-menu-mobile-header-template', 0 ),
			jet_menu()->settings_manager->options_manager->get_option( 'jet-menu-mobile-before-template', 0 ),
			jet_menu()->settings_manager->options_manager->get_option( 'jet-menu-mobile-after-template', 0 ),
		);
	}

	/**
	 * @return bool
	 */
	private function is_location_mobile_render_for_asset_preload() {

		$use_mobile_device_render = jet_menu()->settings_manager->options_manager->get_option( 'jet-mega-menu-use-mobile-render', false );
		$use_mobile_device_render = filter_var( $use_mobile_device_render, FILTER_VALIDATE_BOOLEAN );

		if ( ! $use_mobile_device_render ) {
			return false;
		}

		$current_device = jet_menu_tools()->get_current_device();

		if ( 'desktop' === $current_device ) {
			return false;
		}

		$device_for_mobile_render = jet_menu()->settings_manager->options_manager->get_option( 'jet-mega-menu-mobile-device', 'mobile' );

		if ( 'tablet-mobile' === $device_for_mobile_render && ( 'tablet' === $current_device || 'mobile' === $current_device ) ) {
			return true;
		}

		return 'mobile' === $device_for_mobile_render && 'mobile' === $current_device;
	}

	/**
	 * @return array
	 */
	private function get_theme_core_location_asset_preload_data() {

		if ( ! defined( 'JET_THEME_CORE_VERSION' ) || ! function_exists( 'jet_theme_core' ) ) {
			return $this->get_empty_asset_preload_data();
		}

		$theme_core = jet_theme_core();

		if ( empty( $theme_core->locations ) || empty( $theme_core->template_conditions_manager ) || empty( $theme_core->templates ) ) {
			return $this->get_empty_asset_preload_data();
		}

		$locations = apply_filters( 'jet-menu/elementor-template-assets/theme-core-locations', array( 'header', 'footer' ) );

		if ( empty( $locations ) || ! is_array( $locations ) ) {
			return $this->get_empty_asset_preload_data();
		}

		$data = $this->get_theme_core_theme_builder_asset_preload_data( $theme_core, $locations );

		foreach ( $locations as $location ) {
			$structure = $theme_core->locations->get_structure_for_location( $location );

			if ( ! $structure ) {
				continue;
			}

			$template_id = $theme_core->template_conditions_manager->find_matched_conditions( $structure->get_id() );
			$template_id = is_array( $template_id ) ? reset( $template_id ) : $template_id;
			$template_id = absint( $template_id );

			if ( ! $template_id || 'elementor' !== $theme_core->templates->get_template_content_type( $template_id ) ) {
				continue;
			}

			$data = $this->merge_asset_preload_data(
				$data,
				$this->get_elementor_document_asset_preload_data( $template_id )
			);
		}

		return $data;
	}

	/**
	 * @param object $theme_core
	 * @param array  $locations
	 *
	 * @return array
	 */
	private function get_theme_core_theme_builder_asset_preload_data( $theme_core, $locations = array() ) {

		if ( empty( $theme_core->theme_builder->frontend_manager ) || ! method_exists( $theme_core->theme_builder->frontend_manager, 'get_matched_page_template_layouts' ) ) {
			return $this->get_empty_asset_preload_data();
		}

		$layout = $theme_core->theme_builder->frontend_manager->get_matched_page_template_layouts();

		if ( empty( $layout ) || ! is_array( $layout ) ) {
			return $this->get_empty_asset_preload_data();
		}

		$data = $this->get_empty_asset_preload_data();

		foreach ( $locations as $location ) {
			if ( empty( $layout[ $location ]['id'] ) ) {
				continue;
			}

			$template_id = absint( $layout[ $location ]['id'] );

			if ( ! $template_id || 'elementor' !== $theme_core->templates->get_template_content_type( $template_id ) ) {
				continue;
			}

			$data = $this->merge_asset_preload_data(
				$data,
				$this->get_elementor_document_asset_preload_data( $template_id )
			);
		}

		return $data;
	}

	/**
	 * @return array
	 */
	private function get_current_elementor_asset_preload_data() {

		$post_ids = array_filter( array_unique( array(
			get_the_ID(),
			get_queried_object_id(),
		) ) );

		$data = $this->get_empty_asset_preload_data();

		foreach ( $post_ids as $post_id ) {
			$data = $this->merge_asset_preload_data(
				$data,
				$this->get_elementor_document_asset_preload_data( $post_id )
			);
		}

		return $data;
	}

	/**
	 * @param int $post_id
	 *
	 * @return array
	 */
	private function get_elementor_document_asset_preload_data( $post_id = 0 ) {

		$post_id = absint( $post_id );

		if ( ! $post_id ) {
			return $this->get_empty_asset_preload_data();
		}

		$document = \Elementor\Plugin::$instance->documents->get( $post_id );

		if ( ! $document ) {
			return $this->get_empty_asset_preload_data();
		}

		$elements_data = $document->get_elements_raw_data();

		if ( empty( $elements_data ) ) {
			return $this->get_empty_asset_preload_data();
		}

		return $this->find_elementor_document_asset_preload_data( $elements_data );
	}

	/**
	 * @param array $elements_data
	 *
	 * @return array
	 */
	private function find_elementor_document_asset_preload_data( $elements_data = array() ) {

		$data = $this->get_empty_asset_preload_data();

		foreach ( $elements_data as $element_data ) {
			if ( ! is_array( $element_data ) ) {
				continue;
			}

			if ( 'widget' === ( $element_data['elType'] ?? '' ) ) {
				$settings    = isset( $element_data['settings'] ) && is_array( $element_data['settings'] ) ? $element_data['settings'] : array();
				$widget_type = $element_data['widgetType'] ?? '';

				if ( 'jet-mega-menu' === $widget_type && ! empty( $settings['menu'] ) ) {
					$data['menu_ids'][] = $settings['menu'];
					$data['menu_ids'][] = $settings['mobile-menu'] ?? 0;

					$data['template_ids'] = array_merge(
						$data['template_ids'],
						$this->get_elementor_widget_template_ids(
							$settings,
							array(
								'mobile-item-header-template',
								'mobile-item-before-template',
								'mobile-item-after-template',
							)
						)
					);
				}

				if ( 'jet-mobile-menu' === $widget_type && ! empty( $settings['menu'] ) ) {
					$data['menu_ids'][] = $settings['menu'];
					$data['menu_ids'][] = $settings['mobile_menu'] ?? 0;

					$data['template_ids'] = array_merge(
						$data['template_ids'],
						$this->get_elementor_widget_template_ids(
							$settings,
							array(
								'item_header_template',
								'item_before_template',
								'item_after_template',
							)
						)
					);
				}

				if ( 'jet-custom-menu' === $widget_type && ! empty( $settings['menu'] ) ) {
					$data['menu_ids'][] = $settings['menu'];
				}
			}

			if ( ! empty( $element_data['elements'] ) && is_array( $element_data['elements'] ) ) {
				$data = $this->merge_asset_preload_data(
					$data,
					$this->find_elementor_document_asset_preload_data( $element_data['elements'] )
				);
			}
		}

		return $data;
	}

	/**
	 * @param array $settings
	 * @param array $setting_keys
	 *
	 * @return array
	 */
	private function get_elementor_widget_template_ids( $settings = array(), $setting_keys = array() ) {

		$template_ids = array();

		foreach ( $setting_keys as $setting_key ) {
			if ( ! empty( $settings[ $setting_key ] ) ) {
				$template_ids[] = $settings[ $setting_key ];
			}
		}

		return $template_ids;
	}

	/**
	 * @param int $menu_id
	 */
	private function enqueue_menu_mega_template_assets( $menu_id = 0 ) {

		$menu_id = absint( $menu_id );

		if ( ! $menu_id || in_array( $menu_id, $this->preloaded_menu_ids, true ) ) {
			return;
		}

		$this->preloaded_menu_ids[] = $menu_id;

		$menu_items = $this->get_menu_items_object_data( $menu_id );

		if ( empty( $menu_items ) ) {
			return;
		}

		foreach ( $menu_items as $item ) {
			$item_settings = jet_menu()->settings_manager->get_menu_item_settings( $item->ID );

			if ( empty( $item_settings['enabled'] ) || ! filter_var( $item_settings['enabled'], FILTER_VALIDATE_BOOLEAN ) ) {
				continue;
			}

			if ( 'elementor' !== ( $item_settings['content_type'] ?? '' ) ) {
				continue;
			}

			$template_id = get_post_meta( $item->ID, 'jet-menu-item', true );

			$this->enqueue_elementor_template_assets( $template_id );
		}
	}

	/**
	 * @param int $template_id
	 */
	private function enqueue_elementor_template_assets( $template_id = 0 ) {

		$template_id = absint( $template_id );

		if ( ! $template_id || in_array( $template_id, $this->preloaded_template_ids, true ) ) {
			return;
		}

		$this->preloaded_template_ids[] = $template_id;

		if ( class_exists( '\Elementor\Core\Files\CSS\Post' ) ) {
			$post_css = new \Elementor\Core\Files\CSS\Post( $template_id );
			$post_css->enqueue();
		}

		$this->enqueue_cached_template_style_dependencies( $template_id );
		$this->enqueue_elementor_document_widget_styles( $template_id );
	}

	/**
	 * @param int $template_id
	 */
	private function enqueue_cached_template_style_dependencies( $template_id = 0 ) {

		$styles_data = get_post_meta( $template_id, '_is_style_deps', true );

		if ( empty( $styles_data['depends'] ) || ! is_array( $styles_data['depends'] ) ) {
			return;
		}

		foreach ( $styles_data['depends'] as $style_data ) {
			if ( empty( $style_data['handle'] ) ) {
				continue;
			}

			$handle = $style_data['handle'];

			if ( ! wp_style_is( $handle, 'registered' ) && ! empty( $style_data['obj'] ) && $style_data['obj'] instanceof \_WP_Dependency ) {
				wp_styles()->registered[ $handle ] = $style_data['obj'];
			}

			wp_enqueue_style( $handle );
		}
	}

	/**
	 * @param int $template_id
	 */
	private function enqueue_elementor_document_widget_styles( $template_id = 0 ) {

		$document = \Elementor\Plugin::$instance->documents->get( $template_id );

		if ( ! $document ) {
			return;
		}

		$elements_data = $document->get_elements_raw_data();

		if ( empty( $elements_data ) ) {
			return;
		}

		$this->enqueue_elementor_elements_widget_styles( $elements_data );
	}

	/**
	 * @param array $elements_data
	 */
	private function enqueue_elementor_elements_widget_styles( $elements_data = array() ) {

		foreach ( $elements_data as $element_data ) {
			if ( ! is_array( $element_data ) ) {
				continue;
			}

			if ( 'widget' === ( $element_data['elType'] ?? '' ) ) {
				$widget = false;

				try {
					$widget = \Elementor\Plugin::$instance->elements_manager->create_element_instance( $element_data );
				} catch ( \Throwable $exception ) {
					$widget = false;
				}

				if ( $widget && method_exists( $widget, 'get_style_depends' ) ) {
					foreach ( (array) $widget->get_style_depends() as $handle ) {
						if ( $handle ) {
							wp_enqueue_style( $handle );
						}
					}
				}
			}

			if ( ! empty( $element_data['elements'] ) && is_array( $element_data['elements'] ) ) {
				$this->enqueue_elementor_elements_widget_styles( $element_data['elements'] );
			}
		}
	}

	/**
	 * Constructor for the class
	 */
	public function __construct() {

		$this->load_files();

		$this->location_manager = new Location();

		add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_mega_template_assets' ), 20 );

	}
}
