<?php
namespace Jet_Engine\Elementor_Views\Components;

// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
	die;
}

/**
 * Define components registry class
 */
class Register {

	protected $category_registered = false;
	protected $current_widget = null;
	protected $current_component = null;

	public function __construct() {

		add_filter(
			'jet-engine/twig-views/editor/css-variables',
			[ $this, 'register_css_vars_for_component_controls' ]
		);

		add_filter( 'jet-engine/listings/document-id', [ $this, 'set_components_document_id' ] );

		add_action(
			'jet-engine/listings/components/register-component-elements',
			[ $this, 'add_widget_for_component' ]
		);

		add_action( 'jet-engine/listings/components/update-settings', [ $this, 'on_settings_update' ] );
		add_action( 'jet-engine/elementor-views/documents-registered', [ $this, 'register_document' ] );
		add_action( 'elementor/document/after_save', [ $this, 'save_component_meta' ], 10, 2 );
		add_action( 'jet-engine/elementor-views/dynamic-tags/register', [ $this, 'register_tags' ] );
		add_action( 'jet-engine/component/before-content', [ $this, 'print_block_preview_css' ] );

		add_action(
			'jet-engine/elementor-views/frontend/before-inline-css',
			[ $this, 'fix_ajax_component_inline_css' ], 0, 2
		);

		if ( ! $this->category_registered ) {
			add_action(
				'elementor/elements/categories_registered',
				[ $this, 'register_components_category' ]
			);
		}

		add_action(
			'jet-engine/component/before-content',
			[ $this, 'register_css_selectors_update' ]
		);

		add_action(
			'jet-engine/component/after-content',
			[ $this, 'unregister_css_selectors_update' ],
			99999
		);

		add_action(
			'jet-engine/elementor-views/components/current-widget',
			[ $this, 'set_current_widget' ]
		);
	}

	/**
	 * Current widget setter
	 *
	 * @param mixed $widget
	 * @return void
	 */
	public function set_current_widget( $widget ) {
		$this->current_widget = $widget;
	}

	/**
	 * Ensure CSS file is updated when component is rendered via AJAX request.
	 * @see https://github.com/Crocoblock/issues-tracker/issues/16542
	 *
	 * @param int $post_id Component/Listing to render CSS for.
	 * @param \Elementor\Core\Files\CSS\Post $css_file CSS file instance.
	 */
	public function fix_ajax_component_inline_css( $post_id, $css_file ) {

		static $posts_done = [];

		if ( ! jet_engine()->listings->components->is_component( $post_id ) ) {
			return;
		}

		if ( in_array( $post_id, $posts_done, true ) ) {
			return;
		}

		$css_file->update();
	}

	/**
	 * Apply unique CSS ID for dynamic CSS
	 *
	 * @return string|null
	 */
	public function apply_unique_css_id() {

		$current_widget = $this->current_widget;

		if ( ! $current_widget && $this->current_component ) {

			$stack_index = jet_engine()->listings->components->stack->get_component_stack_index(
				$this->current_component
			);

			if ( false !== $stack_index ) {

				$stack_meta = jet_engine()->listings->components->stack->get_stack_meta(
					$stack_index,
					'current_widget'
				);

				if ( $stack_meta ) {
					$current_widget = $stack_meta;
				}
			}
		}
		return $current_widget ? $current_widget->get_jet_instance_id() : null;
	}

	/**
	 * Apply component selector for dynamic CSS
	 *
	 * @param string $selector
	 * @param int    $data_id
	 * @param int    $rendered_id
	 * @return string
	 */
	public function apply_component_selector( $selector, $data_id, $rendered_id ) {

		/**
		 * Prevent applying selector if we're not in the context of the current component
		 * @see https://github.com/Crocoblock/issues-tracker/issues/17873
		 */
		if (
			$this->current_component
			&& (int) $rendered_id !== (int) $this->current_component->get_id() )
		{
			return $selector;
		}

		$current_widget = $this->current_widget;

		// Try to set $current_widget from stack if not set already
		// This case is actual when component contains another component
		if ( ! $current_widget && $this->current_component ) {

			$stack_index = jet_engine()->listings->components->stack->get_component_stack_index(
				$this->current_component
			);

			if ( false !== $stack_index ) {

				$stack_meta = jet_engine()->listings->components->stack->get_stack_meta(
					$stack_index,
					'current_widget'
				);

				if ( $stack_meta ) {
					$current_widget = $stack_meta;
				}
			}
		}

		if ( ! $current_widget && ! $this->current_component ) {
			return $selector;
		} elseif ( ! $current_widget && $this->current_component ) {
			return '.jet-listing-grid--' . $this->current_component->get_id();
		}

		return $current_widget->apply_component_selector( $selector );
	}

	/**
	 * Register hooks to update CSS selectors for dynamic styles generated by Elementor
	 *
	 * @param object $component
	 * @return void
	 */
	public function register_css_selectors_update( $component ) {

		$this->current_component = $component;

		$stack_index = jet_engine()->listings->components->stack->get_component_stack_index( $component );

		if ( false !== $stack_index && $this->current_widget ) {
			jet_engine()->listings->components->stack->set_stack_meta(
				$stack_index,
				'current_widget',
				$this->current_widget
			);
		}

		add_filter(
			'jet-engine/elementor-views/dynamic-tags/dynamic-css-unique-id',
			[ $this, 'apply_unique_css_id' ]
		);

		add_filter(
			'jet-engine/elementor-views/dynamic-css/unique-listing-selector',
			[ $this, 'apply_component_selector' ], 0, 3
		);

	}

	/**
	 * Unregister hooks to update CSS selectors for dynamic styles generated by Elementor
	 *
	 * @param object $component
	 * @return void
	 */
	public function unregister_css_selectors_update( $component ) {

		$stack_index = jet_engine()->listings->components->stack->get_component_stack_index( $component );

		if ( 0 === $stack_index ) {

			$this->current_component = null;

			remove_filter(
				'jet-engine/elementor-views/dynamic-tags/dynamic-css-unique-id',
				[ $this, 'apply_unique_css_id' ]
			);

			remove_filter(
				'jet-engine/elementor-views/dynamic-css/unique-listing-selector',
				[ $this, 'apply_component_selector' ], 0, 3
			);
		} else {
			$this->current_component = jet_engine()->listings->components->stack->get_parent( $stack_index );
		}
	}

	/**
	 * Print Elementor CSS for Elementor-created component in block editor
	 *
	 * @return [type] [description]
	 */
	public function print_block_preview_css( $component ) {

		if ( 'elementor' !== $component->get_render_view() ) {
			return;
		}

		// phpcs:disable
		if ( empty( $_REQUEST['is_component_preview'] ) ) {
			return;
		}
		// phpcs:enable

		\Elementor\Plugin::instance()->frontend->register_styles();
		\Elementor\Plugin::instance()->frontend->enqueue_styles();

		wp_print_styles();
	}

	/**
	 * Synch elementor controls with component settings
	 *
	 * @param  [type] $component [description]
	 * @return [type]            [description]
	 */
	public function on_settings_update( $component ) {

		if ( 'elementor' !== $component->get_render_view() ) {
			return;
		}

		$props = $component->get_props();
		$styles = $component->get_styles();

		if ( ! empty( $props ) ) {
			foreach ( $props as $i => $prop ) {
				if ( empty( $prop['_id'] ) && ! empty( $prop['id'] ) ) {
					$props[ $i ]['_id'] = $prop['id'];
				}

			}
		}

		if ( ! empty( $styles ) ) {
			foreach ( $styles as $i => $prop ) {
				if ( empty( $prop['_id'] ) && ! empty( $prop['id'] ) ) {
					$styles[ $i ]['_id'] = $prop['id'];
				}

			}
		}

		$page_settings = get_post_meta( $component->get_id(), '_elementor_page_settings', true );
		$listing_data  = get_post_meta( $component->get_id(), '_listing_data', true );

		if ( empty( $page_settings ) ) {
			$page_settings = [];
		}

		if ( empty( $listing_data ) ) {
			$listing_data = [];
		}

		$page_settings['component_controls_list']       = $props;
		$listing_data['component_controls_list']        = $props;
		$page_settings['component_style_controls_list'] = $styles;
		$listing_data['component_style_controls_list']  = $styles;

		update_post_meta( $component->get_id(), '_elementor_page_settings', $page_settings );
		update_post_meta( $component->get_id(), '_listing_data', $listing_data );

	}

	/**
	 * Regsiter CSS variables for component controls
	 * @return [type] [description]
	 */
	public function register_css_vars_for_component_controls( $css_vars = [] ) {

		$kit = \Elementor\Plugin::$instance->kits_manager->get_active_kit();

		if ( ! $kit ) {
			return $css_vars;
		}

		$system_colors = $kit->get_settings( 'system_colors' );
		$custom_colors = $kit->get_settings( 'custom_colors' );

		if ( ! empty( $system_colors ) ) {
			foreach ( $system_colors as $color ) {
				$css_vars[] = [
					'var'   => sprintf( 'var( --e-global-color-%1$s )', $color['_id'] ),
					'value' => $color['color'],
				];
			}
		}

		if ( ! empty( $custom_colors ) ) {
			foreach ( $custom_colors as $color ) {
				$css_vars[] = [
					'var'   => sprintf( 'var( --e-global-color-%1$s )', $color['_id'] ),
					'value' => $color['color'],
				];
			}
		}

		return $css_vars;
	}

	/**
	 * Register dynamic tags to get component prop
	 *
	 * @param  [type] $tags_module [description]
	 * @return [type]              [description]
	 */
	public function register_tags( $tags_module ) {

		require jet_engine()->plugin_path( 'includes/components/elementor-views/components/dynamic-tags/text-tag.php' );
		require jet_engine()->plugin_path( 'includes/components/elementor-views/components/dynamic-tags/image-tag.php' );
		require jet_engine()->plugin_path( 'includes/components/elementor-views/components/dynamic-tags/color-tag.php' );

		$tags_module->register( new Dynamic_Tags\Text_Tag() );
		$tags_module->register( new Dynamic_Tags\Image_Tag() );
		$tags_module->register( new Dynamic_Tags\Color_Tag() );

	}


	/**
	 * Save component meta into a separate meta field on elementor component document save
	 *
	 * @param  [type] $document [description]
	 * @param  [type] $data     [description]
	 * @return [type]           [description]
	 */
	public function save_component_meta( $document, $data ) {

		if ( $document->get_name() !== jet_engine()->listings->components->get_component_base_name() ) {
			return;
		}

		$component = jet_engine()->listings->components->get(
			jet_engine()->listings->components->get_component_base_name() . '-' . $document->get_main_id()
		);

		if ( $component && ! empty( $data['settings']['component_controls_list'] ) ) {
			$component->set_props( $data['settings']['component_controls_list'] );
		}

		if ( $component && ! empty( $data['settings']['component_style_controls_list'] ) ) {
			$component->set_styles( $data['settings']['component_style_controls_list'] );
		}

	}

	/**
	 * Regster component document
	 *
	 * @return [type] [description]
	 */
	public function register_document( $documents_manager ) {

		require jet_engine()->plugin_path( 'includes/components/elementor-views/components/component-document.php' );

		$documents_manager->register_document_type(
			jet_engine()->listings->components->get_component_base_name(),
			'\Jet_Engine\Elementor_Views\Components\Document'
		);

	}

	/**
	 * Set document ID for component
	 *
	 * @param [type] $id [description]
	 */
	public function set_components_document_id( $id ) {

		// phpcs:disable
		if ( ! empty( $_REQUEST['listing_view_type'] )
			&& 'elementor' === $_REQUEST['listing_view_type']
			&& ! empty( $_REQUEST['template_entry_type'] )
			&& 'component' === $_REQUEST['template_entry_type']
		) {
			return jet_engine()->listings->components->get_component_base_name();
		}
		// phpcs:enable

		return $id;
	}

	/**
	 * Register separate widget for each component
	 * @param [type] $component [description]
	 */
	public function add_widget_for_component( $component ) {

		if ( ! $component->is_view_supported( 'elementor' ) ) {
			return;
		}

		add_action( 'elementor/widgets/register', function( $widgets_manager ) use ( $component ) {

			if ( ! class_exists( '\Jet_Engine\Elementor_Views\Components\Base_Widget' ) ) {
				require jet_engine()->plugin_path( 'includes/components/elementor-views/components/base-widget.php' );
			}

			$component_widget = new Base_Widget( [], null, $component );

			$widgets_manager->register( $component_widget );

		} );

	}

	/**
	 * Register components category
	 *
	 * @param  [type] $elements_manager [description]
	 * @return [type]                   [description]
	 */
	public function register_components_category( $elements_manager ) {

		$elements_manager->add_category(
			jet_engine()->listings->components->components_category( 'slug' ),
			[
				'title' => jet_engine()->listings->components->components_category( 'name' ),
				'icon' => 'fa fa-plug',
			]
		);

		$this->category_registered = true;

	}

}
