<?php

/**
 * Class Forminator_Render_Form
 *
 * @since 1.0
 */
abstract class Forminator_Render_Form {

	/**
	 * Model data
	 *
	 * @var Forminator_Base_Form_Model
	 */
	public $model = null;

	/**
	 * Checks if is admin
	 *
	 * @var bool
	 */
	protected $is_admin = false;

	/**
	 * Track Views
	 *
	 * @var bool
	 */
	protected $track_views = true;

	/**
	 * Mapper form with its instance, handling multiple same form rendered
	 *
	 * @var array
	 */
	protected static $render_ids = array();

	/**
	 * Checks if is preview
	 *
	 * @var bool
	 */
	protected $is_preview = false;

	/**
	 * Action to be used to load ajax
	 *
	 * @var string
	 */
	protected $ajax_load_action = 'forminator_load_module';

	/**
	 * Last submitted data
	 * useful when rendering via ajax and need older data for markup
	 *
	 * @since 1.6.1
	 * @var array
	 */
	protected $last_submitted_data = array();

	/**
	 * Original wp http referer
	 * When ajax load enabled wp_http_referer is replaced with admin-ajax
	 * This var will make it persistent for next render
	 *
	 * @since 1.6.2
	 * @var string
	 */
	protected $_wp_http_referer = '';

	/**
	 * Original page_id
	 * When ajax load enabled page_id cant be found
	 * This var will make it persistent for next render
	 *
	 * @since 1.6.2
	 * @var int
	 */
	protected $_page_id = 0;

	/**
	 * Forminator_Render_Form constructor.
	 *
	 * @since 1.0
	 */
	public function __construct() {
		$this->is_admin = is_admin();
		$this->init();
	}

	/**
	 * Init method
	 *
	 * @since 1.0
	 */
	public function init() {
	}

	/**
	 * Display form method
	 * Must be implemented by class that extend it
	 *
	 * @since 1.0
	 *
	 * @param      $id
	 * @param bool $is_preview
	 *
	 * @return mixed
	 */
	abstract public function display( $id, $is_preview = false );

	/**
	 * Generate render_id for current form
	 * represented as integer, start from 0
	 *
	 * @param $id
	 */
	public function generate_render_id( $id ) {
		// set render_id for mapping Front End with its form.
		if ( ! isset( self::$render_ids[ $id ] ) ) {
			self::$render_ids[ $id ] = 0;
		} else {
			self::$render_ids[ $id ] ++;
		}
	}

	/**
	 * Render form markup
	 *
	 * @since 1.0
	 *
	 * @param      $id
	 * @param bool $hide If true, display: none will be added on the form markup and later removed with JS
	 * @param bool $is_preview
	 */
	public function render( $id, $hide = true, $is_preview = false ) {
		$form_type        = $this->get_form_type();
		$form_fields      = $this->get_fields();
		$form_settings    = $this->get_form_settings();
		$post_id          = $this->get_post_id();
		$this->is_preview = $is_preview;

		do_action( 'forminator_before_form_render', $id, $form_type, $post_id, $form_fields, $form_settings );

		$this->get_form( $id, true, $hide );

		do_action( 'forminator_after_form_render', $id, $form_type, $post_id, $form_fields, $form_settings );
	}

	/**
	 * Return form markup
	 *
	 * @since 1.0
	 *
	 * @param      $id
	 * @param bool $render
	 *
	 * @return mixed|void
	 */
	public function get_form( $id, $render = true, $hide = true ) {

		$html          = '';
		$forminator_ui = '';

		$data_design   = '';
		$data_grid     = '';

		$form_type         = $this->get_form_type();
		$form_fields       = $this->get_fields();
		$form_settings     = $this->get_form_settings();
		$form_design       = $this->get_form_design();
		$form_enctype      = $this->form_enctype();
		$extra_classes     = $this->form_extra_classes();
		$track_views       = $this->can_track_views();
		$fields_type_class = $this->get_fields_type_class();
		$design_class      = $this->get_form_design_class();

		// If rendered on Preview, the array is empty and sometimes PHP notices show up
		if ( $this->is_admin && ( empty( self::$render_ids ) || ! $id ) ) {
			self::$render_ids[ $id ] = 0;
		}

		$render_id = self::$render_ids[ $id ];

		$forminator_ui = 'forminator-ui ';

		if ( 'quiz' === $form_type ) {
			$data_design = 'data-design="' . $this->get_quiz_theme() . '"';
		} else {
			$data_design = 'data-design="' . $this->get_form_design() . '"';
		}

		if ( 'custom-form' === $form_type ) {
			$data_grid = 'data-grid="' . $this->get_fields_style() . '"';
		}

		// Markup Loader.
		$loader = sprintf(
			'<div class="%sforminator-%s forminator-%s-%s %s %s %s" data-forminator-render="%s" data-form="forminator-module-%s"><br/></div>',
			$forminator_ui,
			$form_type,
			$form_type,
			$id,
			$design_class,
			$fields_type_class,
			$extra_classes,
			$render_id,
			$id
		);

		// To-Do: Remove when live preview for Poll & Quiz implemented
		if( "custom-form" !== $form_type ) {
			$loader = '';
		}

		$quiz_type      = '';
		$quiz_spacing   = '';
		$quiz_columns   = '';
		$quiz_alignment = '';
		$aria_live      = '';

		if ( 'quiz' === $form_type ) {
			$quiz_type      = 'data-quiz="knowledge"'; // TODO: Get correct quiz type: knowledge or nowrong.
			$aria_live      = 'aria-live="polite"'; // Listen to live changes on form.
			$quiz_spacing   = 'data-spacing="default"';
			$quiz_alignment = 'data-alignment="left"';

			if ( isset( $form_settings['quiz-spacing'] ) && ! empty( $form_settings['quiz-spacing'] ) ) {
				$quiz_spacing = 'data-spacing="' . $form_settings['quiz-spacing'] . '"';
			}

			if ( isset( $form_settings['quiz-alignment'] ) && ! empty( $form_settings['quiz-alignment'] ) ) {
				$quiz_alignment = 'data-alignment="' . $form_settings['quiz-alignment'] . '"';
			} else {

				if ( false !== strpos( $form_design, 'grid' ) ) {
					$quiz_alignment = 'data-alignment="center"';
				}
			}

			if ( isset( $form_settings['visual_style'] ) && 'grid' === $form_settings['visual_style'] ) {
				if ( isset( $form_settings['quiz-grid-cols'] ) ) {
					$quiz_columns = 'data-columns="' . $form_settings['quiz-grid-cols'] . '"';
				} else {
					$quiz_columns = 'data-columns="3"';
				}
			}
		}

		$has_lead = isset( $form_settings['hasLeads'] ) ? $form_settings['hasLeads'] : false;

		$html .= $loader;

		$hidden = $hide ? 'style="display: none;"' : '';

		if ( $this->is_preview || is_admin() ) {
			$hidden = '';
		}

		if ( 'quiz' === $form_type && $has_lead ) {
			$html .= $this->render_form_header();
		}

		$html .= sprintf(
			'<form
				id="forminator-module-%s"
				class="%sforminator-%s forminator-%s-%s %s %s %s"
				action=""
				method="post"
				data-forminator-render="%s"
				data-form-id="%s"
				%s
				%s
				%s
				%s
				%s
				%s
				%s
				%s
				%s
			>',
			$id,
			$forminator_ui,
			$form_type,
			$form_type,
			$id,
			$design_class,
			$fields_type_class,
			$extra_classes,
			$render_id,
			$id,
			$quiz_type,
			$data_design,
			$quiz_spacing,
			$quiz_columns,
			$quiz_alignment,
			$data_grid,
			$form_enctype,
			$aria_live,
			$hidden
		);

		if ( ! $has_lead ) {
		    $html .= $this->render_form_header();
		}

		$html .= $this->render_fields( false );

		$html .= $this->referer_url_field( false );

		$defender_data = forminator_defender_compatibility();
		if ( $defender_data['is_activated'] ) {
			$html .= $this->render_form_authentication();
		}

		$html .= $this->get_submit( $id, false );

		$html .= sprintf( '</form>' );

		if ( 'custom-form' === $form_type ) {
			$html .= $this->render_skip_form_content();
		}

		if ( $track_views ) {
			$form_view = Forminator_Form_Views_Model::get_instance();
			$post_id   = $this->get_post_id();
			if ( ! $this->is_admin ) {
				$form_view->save_view( $id, $post_id, '' );
			}
		}

		if ( $render ) {
			echo apply_filters( 'forminator_render_form_markup', $html, $form_fields, $form_type, $form_settings, $form_design, $render_id ); // WPCS: XSS ok.
		} else {
			/** @noinspection PhpInconsistentReturnPointsInspection */
			return apply_filters( 'forminator_render_form_markup', $html, $form_fields, $form_type, $form_settings, $form_design, $render_id );
		}
	}

	/**
	 * Return form placeholder markup
	 *
	 * @since 1.6.1
	 *
	 * @param      $id
	 * @param bool $render
	 *
	 * @return mixed|void
	 */
	public function get_form_placeholder( $id, $render = true ) {
		$html      = '';
		$form_type = $this->get_form_type();
		//if rendered on Preview, the array is empty and sometimes PHP notices show up
		if ( $this->is_admin && ( empty( self::$render_ids ) || ! $id ) ) {
			self::$render_ids[ $id ] = 0;
		}

		$render_id = self::$render_ids[ $id ];

		$html .= sprintf(
			'<form id="forminator-module-%s" class="forminator-%s forminator-%s-%s" action="" method="post" data-forminator-render="%s">',
			$id,
			$form_type,
			$form_type,
			$id,
			$render_id
		);

			$html .= $this->render_form_header();

		$html .= '</form>';

		if ( $render ) {
			echo apply_filters( 'forminator_render_form_placeholder_markup', $html, $form_type, $render_id ); // WPCS: XSS ok.
		} else {
			/** @noinspection PhpInconsistentReturnPointsInspection */
			return apply_filters( 'forminator_render_form_placeholder_markup', $html, $form_type, $render_id );
		}
	}

	/**
	 * Get Additional CSS class to be aplied based on fields style (enclosed or not)
	 *
	 * @since 1.0.5
	 * @return string
	 */
	public function get_fields_type_class() {
		$form_type    = $this->get_form_type();
		$fields_style = $this->get_fields_style();
		if ( 'custom-form' === $form_type ) {
			if ( 'open' !== $fields_style ) {
				$fields_type = 'forminator-' . $fields_style;
			} else {
				$fields_type = '';
			}
		} else {
			$fields_type = '';
		}

		/**
		 * Filter CSS of fields_type that will be added on user
		 *
		 * @since 1.0.5
		 *
		 * @param string $fields_type  current fields type CSS class that aplied
		 * @param string $form_type    (custom-form / poll / quiz)
		 * @param string $fields_style (enclosed ?)
		 */
		return apply_filters( 'forminator_render_fields_type_class', $fields_type, $form_type, $fields_style );
	}

	/**
	 * Get Additional CSS class to be aplied based on get_form_design
	 *
	 * @since 1.0.5
	 * @return string
	 */
	public function get_form_design_class() {

		$form_type   = $this->get_form_type();
		$form_design = $this->get_form_design();

		if ( 'quiz' === $form_type ) {
			$design_class = 'forminator-quiz--' . $form_design;
		} else {

			if ( 'clean' === $form_design ) {
				$design_class = '';
			} else {
				$design_class = 'forminator-design--' . $form_design;
			}
		}

		/**
		 * Filter design CSS class that will be aplied on <form
		 *
		 * @since 1.0.5
		 *
		 * @param string $design_class current design CSS class applied
		 * @param string $form_design  (clean/material, etc)
		 */
		return apply_filters( 'forminator_render_form_design_class', $design_class, $form_design );

	}

	/**
	 * Return form submit button markup
	 *
	 * @since 1.0
	 *
	 * @param      $form_id
	 * @param bool $render
	 *
	 * @return mixed|void
	 */
	public function get_submit( $form_id, $render = true ) {
		$nonce     = $this->nonce_field( 'forminator_submit_form', 'forminator_nonce' );
		$post_id   = $this->get_post_id();
		$html      = $this->get_button_markup();
		$form_type = $this->get_form_type();
		$html      .= $nonce;
		$html      .= sprintf( '<input type="hidden" name="form_id" value="%s">', $form_id );
		$html      .= sprintf( '<input type="hidden" name="page_id" value="%s">', $post_id );
		if ( isset( self::$render_ids[ $form_id ] ) ) {
			$html .= sprintf( '<input type="hidden" name="render_id" value="%s">', self::$render_ids[ $form_id ] );
		}

		if ( $this->is_preview ) {
			$html .= sprintf( '<input type="hidden" name="action" value="%s">', "forminator_submit_preview_form_" . $form_type );
		} else {
			$html .= sprintf( '<input type="hidden" name="action" value="%s">', "forminator_submit_form_" . $form_type );
		}
		if ( $render ) {
			echo apply_filters( 'forminator_render_form_submit_markup', $html, $form_id, $post_id, $nonce ); // WPCS: XSS ok.
		} else {
			/** @noinspection PhpInconsistentReturnPointsInspection */
			return apply_filters( 'forminator_render_form_submit_markup', $html, $form_id, $post_id, $nonce );
		}
	}

	/**
	 * Return button markup
	 *
	 * @since 1.0
	 * @return mixed
	 */
	public function get_button_markup() {

		$html   = '';
		$button = $this->get_submit_button_text();

		$html   = '<div class="forminator-row">';

			$html   .= '<div class="forminator-col">';

				$html   .= '<div id="submit" class="forminator-field">';

					$html .= '<button class="forminator-button forminator-button-submit">';

						if ( 'material' === $this->get_form_design() ) {

							$html .= sprintf( '<span>%s</span>', $button );

							$html .= '<span aria-hidden="true"></span>';

						} else {

							$html .= $button;

						}

					$html .= '</button>';

				$html .= '</div>';

			$html .= '</div>';

		$html .= '</div>';

		return apply_filters( 'forminator_render_button_markup', $html, $button );
	}

	/**
	 * Submit button text
	 *
	 * @since 1.0
	 * @return string
	 */
	public function get_submit_button_text() {
		return __( "Submit", Forminator::DOMAIN );
	}

	/**
	 * Return form fields
	 *
	 * @since 1.0
	 * @return array
	 */
	public function get_fields() {
		// That function will be overwritten by form class
		return array();
	}


	/**
	 * Return form fields markup
	 *
	 * @since 1.0
	 *
	 * @param bool $render
	 *
	 * @return mixed|void
	 */
	public function render_fields( $render = true ) {
		$html = '';

		$fields = $this->get_fields();
		foreach ( $fields as $key => $field ) {
			do_action( 'forminator_before_field_render', $field );

			// Render before field markup
			$html .= $this->render_field_before( $field );

			// Render field
			$html .= $this->render_field( $field );

			do_action( 'forminator_after_field_render', $field );

			// Render after field markup
			$html .= $this->render_field_after( $field );
		}

		if ( $render ) {
			echo wp_kses_post( $html ); // WPCS: XSS ok.
		} else {
			/** @noinspection PhpInconsistentReturnPointsInspection */
			return apply_filters( 'forminator_render_fields_markup', $html, $fields );
		}
	}

	/**
	 * Return referer url field markup
	 *
	 * @since ?
	 *
	 * @param bool $render
	 *
	 * @return string|void
	 */
	public function referer_url_field( $render = true ) {
		$referer_url = "";
		if( isset( $_REQUEST['extra'] ) && is_array( $_REQUEST['extra'] ) && isset( $_REQUEST['extra']['referer_url'] ) ) {
			$referer_url = sanitize_text_field($_REQUEST['extra']['referer_url']);
		} elseif ( isset ( $_SERVER['HTTP_REFERER'] ) ) {
			$referer_url = $_SERVER['HTTP_REFERER'];
		}

		$html = sprintf( '<input type="hidden" name="referer_url" value="%s" />', esc_attr( $referer_url ) );

		if ( $render ) {
			echo wp_kses_post( $html ); // WPCS: XSS ok.
		} else {
			return $html;
		}
	}

	/**
	 * Return field classes
	 *
	 * @since 1.0
	 *
	 * @param $field
	 *
	 * @return string
	 */
	public function get_classes(
		/** @noinspection PhpUnusedParameterInspection */
		$field
	) {
		return 'forminator-field';
	}

	/**
	 * Return markup before field
	 *
	 * @since 1.0
	 *
	 * @param $field
	 *
	 * @return mixed
	 */
	public function render_field_before( $field ) {
		$class = $this->get_classes( $field );
		$html  = sprintf( '<div class="%s">', $class );

		return apply_filters( 'forminator_before_field_markup', $html, $class );
	}

	/**
	 * Return markup after field
	 *
	 * @since 1.0
	 *
	 * @param $field
	 *
	 * @return mixed
	 */
	public function render_field_after( $field ) {
		$html = sprintf( '</div>' );

		return apply_filters( 'forminator_after_field_markup', $html, $field );
	}

	/**
	 * Return sanitized form data
	 *
	 * @since 1.0
	 *
	 * @param $content
	 *
	 * @return mixed
	 */
	public function sanitize_output( $content ) {
		return htmlentities( $content, ENT_QUOTES );
	}

	/**
	 * Return field markup
	 *
	 * @since 1.0
	 *
	 * @param $field
	 *
	 * @return mixed|void
	 */
	public function render_field( $field ) {
	}

	/**
	 * Return form settings
	 *
	 * @since 1.0
	 * @return array
	 */
	public function get_form_settings() {
		return array();
	}

	/**
	 * Return field ID
	 *
	 * @since 1.0
	 *
	 * @param $field
	 *
	 * @return string
	 */
	public function get_field_id( $field ) {
		return isset( $field['element_id'] ) ? $field['element_id'] : '0';
	}

	/**
	 * Return post ID
	 *
	 * @since 1.0
	 * @return int|string|bool
	 */
	public function get_post_id() {
		$post_id = $this->_page_id;
		if ( empty( $post_id ) ) {
			$post_id = get_post() ? get_the_ID() : '0';
		}

		return $post_id;
	}

	/**
	 * Return form type
	 *
	 * @since 1.0
	 * @return string
	 */
	public function get_form_type() {
		return '';
	}

	/**
	 * Return form design
	 *
	 * @since 1.0
	 * @return string
	 */
	public function get_form_design() {
		return '';
	}

	/**
	 * Return fields style
	 *
	 * @since 1.0
	 * @return string
	 */
	public function get_fields_style() {
		return '';
	}

	/**
	 * Render form header
	 *
	 * @since 1.0
	 */
	public function render_form_header() {
		return '';
	}

	/**
	 * Render form header
	 *
	 * @since 1.0
	 */
	public function render_form_authentication() {
		return '';
	}

	/**
	 * Form enctype
	 *
	 * @since 1.0
	 * @return string
	 */
	public function form_enctype() {
		return '';
	}

	/**
	 * Form extra classes
	 *
	 * @since 1.0
	 */
	public function form_extra_classes() {
		return '';
	}

	/**
	 * Check if can track views
	 *
	 * @since 1.0
	 * @return bool
	 */
	public function can_track_views() {
		return $this->track_views;
	}

	/**
	 * Define cache constants
	 *
	 * @since 1.6.1
	 */
	public function maybe_define_cache_constants() {
		if ( $this->model instanceof Forminator_Base_Form_Model ) {
			if ( $this->model->is_use_donotcachepage_constant() ) {
				if ( ! defined( 'DONOTCACHEPAGE' ) ) {
					define( 'DONOTCACHEPAGE', 1 );
				}
			}
		}
	}

	/**
	 * Get load ajax status
	 *
	 * @since 1.6.1
	 *
	 * @param $force
	 *
	 * @return bool
	 */
	public function is_ajax_load( $force = false ) {

		// somehow, it could be incompatible model,
		// lets return as false when it happens
		if ( $this->model instanceof Forminator_Base_Form_Model ) {
			return $this->model->is_ajax_load( $force );
		}

		return false;

	}

	/**
	 * Script loader module via ajax
	 *
	 * @since 1.6.1
	 *
	 * @param       $is_preview
	 * @param array $preview_data
	 * @param array $lead_data
	 */
	public function ajax_loader( $is_preview, $preview_data = array(), $lead_data = array() ) {

		if ( ! $this->model instanceof Forminator_Base_Form_Model ) {
			return;
		}
		// Load module only via ajax
		if ( ! $this->is_ajax_load( $is_preview ) ) {
			return;
		}

		$id                        = $this->model->id;
		$this->last_submitted_data = $_POST; // wpcs csrf ok.
		//if rendered on Preview, the array is empty and sometimes PHP notices show up
		if ( $this->is_admin && ( empty( self::$render_ids ) || ! $id ) ) {
			self::$render_ids[ $id ] = 0;
		}

		$this->_wp_http_referer = esc_attr( wp_unslash( $_SERVER['REQUEST_URI'] ) );
		$this->_page_id         = $this->get_post_id();

		if ( ! isset( self::$render_ids[ $id ] ) ) {
			return;
		}

		$ajax_options = array(
			'action'           => $this->ajax_load_action,
			'type'             => $this->model->get_post_type(),
			'id'               => $id,
			'render_id'        => self::$render_ids[ $id ],
			'is_preview'       => $is_preview,
			'preview_data'     => $preview_data,
			'last_submit_data' => $this->last_submitted_data,
			'nonce'				 => wp_create_nonce( 'forminator_load_module' ),
			'extra'            => array(
				'_wp_http_referer' => wp_unslash( $_SERVER['REQUEST_URI'] ),
				'page_id'          => $this->get_post_id(),
				'referer_url'	=> '',//Original referer url where the user come from. This field will be set via JS
			),
		);

		if ( ! empty( $lead_data ) ) {
			$ajax_options['has_lead']  = $lead_data['has_lead'];
			$ajax_options['leads_id']  = $lead_data['leads_id'];
      }

		$front_loader_config = wp_json_encode(
			$ajax_options
		);

		$forminator_loader_script = '
		(function ($, document, window) {
				"use strict";
				(function () {
					$(document).ready(function () {
						if (window.elementorFrontend) {
							if (typeof elementorFrontend.hooks !== "undefined") {
								elementorFrontend.hooks.addAction("frontend/element_ready/global", function ( $scope ) {
									if ( $scope.find( "#forminator-module-' . $id . '" ).length > 0 ) {
										if (typeof ($.fn.forminatorLoader) !== \'undefined\') {
											var front_loader_config = ' . $front_loader_config . ';
											front_loader_config.extra.referer_url = document.referrer;
											$(\'#forminator-module-' . $id . '[data-forminator-render="' . self::$render_ids[ $id ] . '"]\')
												.forminatorLoader(front_loader_config);
										}
									}
								});
							}
						}

						if (typeof ($.fn.forminatorLoader) === \'undefined\') {
							console.log(\'forminator scripts not loaded\');
						} else {
							var front_loader_config = ' . $front_loader_config . ';
							front_loader_config.extra.referer_url = document.referrer;
							$(\'#forminator-module-' . $id . '[data-forminator-render="' . self::$render_ids[ $id ] . '"]\')
								.forminatorLoader(front_loader_config);
						}
					});
				})();
			}(jQuery, document, window));';

		// on real render use add_inline_script to avoid late initialization
		if ( ! $is_preview ) {
			wp_add_inline_script( 'forminator-front-scripts', $forminator_loader_script );
		} else {
			// we are on preview, and its ajax called, so scripts need to be output-ed rather than add it on enqueued script
			?>
			<script type="text/javascript">
				<?php echo $forminator_loader_script;// wpcs XSS ok, should be escaped prior ?>
			</script>
			<?php
		}
	}

	/**
	 * Ajax handler to load module
	 *
	 * @since 1.6.1
	 */
	public static function ajax_load_module() {
		if ( ! $_REQUEST['is_preview'] && ( ! isset( $_REQUEST['nonce'] ) || ! wp_verify_nonce( $_REQUEST['nonce'], 'forminator_load_module' ) ) ) {
			wp_send_json_error( new WP_Error( 'invalid_code' ) );
		}

		$response          = array();
		$data              = $_REQUEST;
		$id                = isset( $data['id'] ) ? $data['id'] : 0;
		$type              = isset( $data['type'] ) ? $data['type'] : 0;
		$is_preview        = isset( $data['is_preview'] ) ? $data['is_preview'] : 0;
		$preview_data      = isset( $data['preview_data'] ) ? $data['preview_data'] : 0;
		$last_submit_data  = isset( $data['last_submit_data'] ) ? $data['last_submit_data'] : array();
		$extra             = isset( $data['extra'] ) ? $data['extra'] : array();
		$has_lead          = isset( $data['has_lead'] ) ? $data['has_lead'] : false;
		$leads_id          = isset( $data['leads_id'] ) ? $data['leads_id'] : 0;
		$lead_preview_data = isset( $data['lead_preview_data'] ) ? $data['lead_preview_data'] : 0;

		$is_preview = filter_var( $is_preview, FILTER_VALIDATE_BOOLEAN );

		if ( empty( $id ) && ! $is_preview ) {
			wp_send_json_error( new WP_Error( 'invalid_id' ) );
		}

		if ( empty( $type ) ) {
			wp_send_json_error( new WP_Error( 'invalid_type' ) );
		}


		if ( ! empty( $preview_data ) ) {
			if ( ! is_array( $preview_data ) ) {
				$preview_data = stripslashes( $preview_data );
				$preview_data = json_decode( $preview_data, true );
			}
		}

		$view = null;
		if ( 'forminator_forms' === $type ) {
			$view = Forminator_CForm_Front::get_instance();
			if ( ! empty( $preview_data ) ) {
				$preview_data = forminator_data_to_model_form( $preview_data );
			}
		} elseif ( 'forminator_polls' === $type ) {
			if ( ! empty( $preview_data ) && is_array( $preview_data ) ) {
				$preview_data = forminator_data_to_model_poll( $preview_data );
			}
			$view = Forminator_Poll_Front::get_instance();
		} elseif ( 'forminator_quizzes' === $type ) {
			$view = Forminator_QForm_Front::get_instance();
			if ( ! empty( $preview_data ) && is_array( $preview_data ) ) {
				$preview_data = forminator_data_to_model_quiz( $preview_data );
			}
		}

		if ( ! $view instanceof Forminator_Render_Form ) {
			wp_send_json_error( new WP_Error( 'invalid_module' ) );
		}

		$response = $view->ajax_display( $id, $is_preview, $preview_data, true, $last_submit_data, $extra );

		if ( $has_lead ) {
			$lead_view = Forminator_CForm_Front::get_instance();
			$lead_wrapper_start = $is_preview ? $view->lead_wrapper_start() : '';
			$lead_wrapper_end   = $is_preview ? $view->lead_wrapper_end() : '';
			$lead_response = $lead_view->ajax_display( $leads_id, $is_preview, $lead_preview_data, true, $last_submit_data, $extra, $id );
			$response['html'] = $lead_wrapper_start . $response['html'] . $lead_response['html'] . $lead_wrapper_end;
			$response['styles']  = array_merge( $response['styles'], $lead_response['styles'] );
			$response['scripts'] = array_merge( $response['scripts'], $lead_response['scripts'] );
			$response['script']  = $response['script'] . $lead_response['script'];
			$response['style']  = $response['style'] . $lead_response['style'];
			$response['lead_options'] = $lead_response['options'];
        }

		wp_send_json_success( $response );
	}

	/**
	 * Display ajax render
	 *
	 * @since 1.6.1
	 * @since 1.6.2 add $extra args
	 *
	 * @param       $id
	 * @param bool  $is_preview
	 * @param bool  $data
	 * @param bool  $hide
	 * @param array $last_submit_data
	 * @param array $extra extra config to display
	 *
	 * @return array
	 */
	abstract public function ajax_display( $id, $is_preview = false, $data = false, $hide = true, $last_submit_data = array(), $extra = array() );

	/**
	 * Generate nonce field
	 * Respect _wp_http_referer to avoid being replaced when ajax load enabled
	 *
	 * @since 1.6.2
	 *
	 * @param $action
	 * @param $name
	 *
	 * @return string
	 */
	protected function nonce_field( $action, $name ) {
		$with_referer = ! empty( $this->_wp_http_referer ) ? false : true;
		$nonce        = wp_nonce_field( $action, $name, $with_referer, false );

		if ( ! $with_referer ) {
			$nonce .= sprintf( '<input type="hidden" name="_wp_http_referer" value="%s" />', esc_attr( $this->_wp_http_referer ) );
		}

		return $nonce;
	}
}