<?php
namespace TotalThemeCore\Vcex;

use WPEX_Post_Cards_Shortcode;

defined( 'ABSPATH' ) || exit;

class Post_Cards extends WPEX_Post_Cards_Shortcode {

	/**
	 * Output.
	 */
	protected $output = '';

	/**
	 * Associative array of shortcode attributes.
	 */
	protected $atts = [];

	/**
	 * Current query.
	 */
	protected $query = null;

	/**
	 * Check if we are currently loading more posts via ajax.
	 */
	protected $doing_loadmore = false;

	/**
	 * Constructor.
	 */
	public function __construct() {
		$this->doing_loadmore = vcex_doing_loadmore();
	}

	/**
	 * Render post cards
	 */
	public function render( $atts = [] ) {
		if ( ! vcex_maybe_display_shortcode( self::TAG, $atts )
			|| ! function_exists( 'vcex_build_wp_query' )
			|| ! function_exists( 'wpex_get_card' )
		) {
			return;
		}

		// Define shortcode output.
		$inner_output = '';

		// Store some original atts before parsing.
		$og_paged = ! empty( $atts['paged'] ) ? $atts['paged'] : null;
		$og_entry_count = ! empty( $atts['entry_count'] ) ? absint( $atts['entry_count'] ) : null;
		$running_count = ! empty( $atts['running_count'] ) ? absint( $atts['running_count'] ) : 0;

		// Parse shortcode atts.
		$this->atts = vcex_shortcode_atts( self::TAG, $atts, get_parent_class() );

		// Core vars.
		$is_auto_query = vcex_validate_att_boolean( 'auto_query', $this->atts );
		$display_type = $this->get_display_type();
		$grid_style = $this->get_grid_style();
		$grid_columns = $this->get_grid_columns();
		$grid_gap_class = $this->get_grid_gap_class();
		$grid_is_responsive = vcex_validate_att_boolean( 'grid_columns_responsive', $this->atts, true );

		// Featured card vars.
		$has_featured_card = $this->has_featured_card();
		$featured_card_location = $this->get_featured_card_location();
		$is_featured_card_top = $this->is_featured_card_top();
		$fc_bk = ! empty( $this->atts['featured_breakpoint'] ) ? $this->atts['featured_breakpoint'] : 'sm';

		// Ajax attributes (don't need to be parsed).
		if ( $this->has_loadmore() ) {
			$ajax_atts = $atts;
			if ( $is_auto_query ) {
				$ajax_atts['query_vars'] = $ajax_atts['query_vars'] ?? array();
			}
			$this->atts['paged'] = $og_paged;
		}

		// We can remove $atts from memory now.
		unset( $atts );

		// Parse featured card ID.
		if ( vcex_validate_att_boolean( 'featured_card', $this->atts ) ) {
			$this->atts['featured_post_id'] = $this->get_featured_post_id();
			if ( isset( $ajax_atts ) ) {
				$ajax_atts['featured_post_id'] = $this->atts['featured_post_id'];
			}
		}

		// Set current entry count.
		$entry_count = $og_entry_count ?? 0;

		// Define card args.
		$card_args = $this->get_card_args();

		// Query posts.
		$this->query = vcex_build_wp_query( $this->atts, self::TAG );

		// Check if we have posts or a featured post.
		if ( $this->query->have_posts() || ! empty( $this->atts['featured_post_id'] ) ) :

			/*-------------------------------------*/
			/* [ Inner Output Starts Here ]
			/*-------------------------------------*/

			// Add inner flex wrap
			if ( $has_featured_card && ! $is_featured_card_top ) {

				$inner_class = array(
					'wpex-post-cards-inner',
				);

				if ( ! $is_featured_card_top ) {
					$inner_class[] = 'wpex-' . $fc_bk . '-flex';
				}

				if ( 'right' === $featured_card_location ) {
					$inner_class[] = 'wpex-' . $fc_bk . '-flex-row-reverse';
				}

				$inner_output .= '<div class="' . esc_attr( implode( ' ', $inner_class ) ) . '">';
			}

			/*-------------------------------------*/
			/* [ Featured Card ]
			/*-------------------------------------*/
			if ( $has_featured_card ) {

				$fc_width = apply_filters( 'wpex_post_cards_featured_width', 50 );
				$fc_width = ! empty( $this->atts['featured_width'] ) ? $this->atts['featured_width'] : $fc_width;

				$featured_card_classes = array(
					'wpex-post-cards-featured',
				);

				// Featured card flex classes.
				if ( ! $is_featured_card_top ) {
					$featured_card_classes[] = 'wpex-' . $fc_bk . '-w-' . trim( absint( $fc_width ) );
					$featured_card_classes[] = 'wpex-' . $fc_bk . '-flex-shrink-0';
				}

				// Featured card bottom margin.
				if ( empty( $this->atts['featured_divider'] ) || ! $is_featured_card_top ) {
					$fc_margin = $this->atts['featured_margin'] ? absint( $this->atts['featured_margin'] ) : 30;
					$featured_card_classes[] = 'wpex-mb-' . $fc_margin;
				}

				// Featured card side margin.
				switch ( $featured_card_location ) {
					case 'left':
						$featured_card_classes[] = 'wpex-' . $fc_bk . '-mb-0';
						$featured_card_classes[] = 'wpex-' . $fc_bk . '-mr-' . $fc_margin;
						break;
					case 'right':
						$featured_card_classes[] = 'wpex-' . $fc_bk . '-mb-0';
						$featured_card_classes[] = 'wpex-' . $fc_bk . '-ml-' . $fc_margin;
						break;
				}

				if ( 'woocommerce' === $this->atts['card_style'] ) {
					$featured_card_classes[] = 'products';
				}

				// Featured card CSS.
				$featured_card_css = vcex_inline_style( array(
					'--wpex-card-media-max-width' => $this->atts['featured_media_max_width'] ?? null,
				) );

				// Display featured card.
				$inner_output .= '<div class="' . esc_attr( implode( ' ', $featured_card_classes ) ) . '"' . $featured_card_css . '>';

					if ( ! empty( $this->atts['featured_post_id'] ) ) {
						$featured_post_id = $this->atts['featured_post_id'];
						global $post;
						$post = get_post( $this->atts['featured_post_id'] );
						$inner_output .= wpex_get_card( $this->get_featured_card_args( $featured_post_id ) );
						wp_reset_postdata();
					} else {
						$count=0;
						while ( $this->query->have_posts() ) :
							$count++;
							if ( 2 === $count ) {
								break;
							}

							$this->query->the_post();

							$featured_post_id = get_the_ID();

							$inner_output .= wpex_get_card( $this->get_featured_card_args( $featured_post_id ) );

						endwhile;
					}

				$inner_output .= '</div>';

				if ( empty( $this->atts['featured_post_id'] ) ) {
					wp_reset_postdata();
					$this->query->rewind_posts();
				}

				if ( ! empty( $this->atts['featured_divider'] ) && $is_featured_card_top ) {
					$inner_output .= $this->featured_divider();
				}

			}

			/*-------------------------------------*/
			/* [ Entries start here ]
			/*-------------------------------------*/
			if ( $this->query->have_posts() ) :

				if ( $has_featured_card && ! $is_featured_card_top ) {
					$inner_output .= '<div class="wpex-post-cards-aside wpex-min-w-0 wpex-' . $fc_bk . '-flex-grow">';
				}

				$items_wrap_class = array();

				switch ( $display_type ) {

					case 'carousel':

						vcex_enqueue_carousel_scripts();

						// All carousels need a unique classname.
						$unique_classname = vcex_element_unique_classname();

						// Get carousel settings.
						$carousel_settings = vcex_get_carousel_settings( $this->atts, self::TAG, false );
						$carousel_css = vcex_get_carousel_inline_css( $unique_classname, $carousel_settings );

						$items_data['data-wpex-carousel'] = vcex_carousel_settings_to_json( $carousel_settings );
						$items_wrap_class[] = 'wpex-posts-card-carousel';
						$items_wrap_class[] = 'wpex-carousel';
						$items_wrap_class[] = 'owl-carousel';

						if ( $carousel_css ) {
							$items_wrap_class[] = $unique_classname;
							$items_wrap_class[] = 'wpex-carousel--render-onload';
							$this->output .= $carousel_css;
						}

						// Flex carousel.
						if ( empty( $this->atts['auto_height'] ) || 'false' === $this->atts['auto_height'] ) {
							$items_wrap_class[] = 'wpex-carousel--flex';
						}

						// No margins.
						if ( array_key_exists( 'items_margin', $this->atts ) && empty( absint( $this->atts['items_margin'] ) ) ) {
							$items_wrap_class[] = 'wpex-carousel--no-margins';
						}

						// Arrow style.
						$arrows_style = ! empty( $this->atts['arrows_style'] ) ? $this->atts['arrows_style'] : 'default';
						$items_wrap_class[] = 'arrwstyle-' . sanitize_html_class( $arrows_style );

						// Arrow position.
						if ( ! empty( $this->atts['arrows_position'] ) && 'default' != $this->atts['arrows_position'] ) {
							$items_wrap_class[] = 'arrwpos-' . sanitize_html_class( $this->atts['arrows_position'] );
						}

						break;

					case 'list':
						$items_wrap_class[] = 'wpex-post-cards-list';
						$items_wrap_class[] = 'wpex-grid';
						if ( $grid_gap_class ) {
							$items_wrap_class[] = $grid_gap_class;
						}
						if ( vcex_validate_att_boolean( 'alternate_flex_direction', $this->atts ) ) {
							$items_wrap_class[] = 'wpex-post-cards-list--alternate-flex-direction';
						}
						if ( vcex_validate_att_boolean( 'list_divider_remove_last', $this->atts ) ) {
							$items_wrap_class[] = 'wpex-last-divider-none';
						}
						break;

					case 'flex':
						$items_wrap_class[] = 'wpex-flex wpex-overflow-x-auto';
						if ( $grid_gap_class ) {
							$items_wrap_class[] = $grid_gap_class;
						}
						break;

					case 'grid':
					default:

						if ( 'css_grid' === $grid_style ) {
							$items_wrap_class[] = 'wpex-post-cards-grid wpex-grid';
							if ( $grid_is_responsive && ! empty( $this->atts['grid_columns_responsive_settings'] ) ) {
								$r_grid_columns = vcex_parse_multi_attribute( $this->atts['grid_columns_responsive_settings'] );
								if ( $r_grid_columns && is_array( $r_grid_columns ) ) {
									$r_grid_columns['d'] = $grid_columns;
									$grid_columns = $r_grid_columns;
								}
							}
							if ( $grid_is_responsive && function_exists( 'wpex_grid_columns_class' ) ) {
								$items_wrap_class[] = wpex_grid_columns_class( $grid_columns );
							} else {
								$items_wrap_class[] = 'wpex-grid-cols-' . sanitize_html_class( $grid_columns );
							}
						} else {
							$items_wrap_class[] = 'wpex-post-cards-grid';
							$items_wrap_class[] = 'wpex-row';
							$items_wrap_class[] = 'wpex-clr';
						}

						if ( 'masonry' === $grid_style ) {
							$items_wrap_class[] = 'wpex-masonry-grid';
							if ( function_exists( 'wpex_enqueue_masonry_scripts' ) ) {
								wpex_enqueue_masonry_scripts(); // uses theme masonry scripts.
							}
						}

						if ( $grid_gap_class ) {
							$items_wrap_class[] = $grid_gap_class;
						}

						break;

				} // end switch

				if ( 'woocommerce' === $this->atts['card_style'] ) {
					$items_wrap_class[] = 'products';
				}

				$inner_output .= '<div class="' . esc_attr( implode( ' ', $items_wrap_class ) ) . '"';

					// Add grid data attributes.
					if ( ! empty( $items_data ) ) {
						foreach ( $items_data as $key => $value ) {
							$inner_output .= ' ' . $key ."='" . esc_attr( $value ) . "'";
						}
					}

					// Add CSS to the items element.
					$grid_css_args = array();
					if ( ! empty( $this->atts['media_max_width'] ) ) {
						$grid_css_args['--wpex-card-media-max-width'] = $this->atts['media_max_width'];
					}
					if ( ! $grid_gap_class ) {
						switch( $display_type ) {
							case 'list':
								if ( ! empty( $this->atts['list_spacing'] ) ) {
									$grid_css_args['gap'] = $this->atts['list_spacing'];
								}
								break;
							case 'flex':
							case 'grid':
								if ( ! empty( $this->atts['grid_spacing'] ) ) {
									if ( 'css_grid' === $grid_style || 'flex' === $display_type ) {
										$grid_css_args['gap'] = $this->atts['grid_spacing'];
									} else {
										$grid_css_args['--wpex-row-gap'] = $this->atts['grid_spacing'];
									}
								}
								break;
						}
					}
					if ( $grid_css_args ) {
						$inner_output .= vcex_inline_style( $grid_css_args );
					}

				$inner_output .= '>';

					// Add first divider if enabled.
					if ( 'list' === $display_type
						&& ! vcex_validate_att_boolean( 'list_divider_remove_first', $this->atts, true )
						&& ! $this->doing_loadmore
					) {
						$inner_output .= $this->list_divider( $this->atts );
					}

					// The Loop
					while ( $this->query->have_posts() ) :

						$this->query->the_post(); // !!! Important !!!

						$post_id = get_the_ID();
						$post_type = get_post_type( $post_id );
						$card_args['post_id'] = $post_id;

						if ( ! empty( $featured_post_id ) && empty( $this->atts['featured_post_id'] ) && $post_id === $featured_post_id ) {
							continue;
						}

						$entry_count++;

						$running_count++;
						set_query_var( 'wpex_loop_running_count', absint( $running_count ) );

						$item_class = array(
							'wpex-post-cards-entry',
							'post-' . sanitize_html_class( $post_id ),
							'type-' . sanitize_html_class( $post_type ),
						);

						switch ( $display_type ) {
							case 'carousel':
								$item_class[] = 'wpex-carousel-slide';
								break;
							case 'list':
								// No classes for list style.
								break;
							case 'flex':
							case 'grid':
							default:

								if ( 'flex' === $display_type ) {
									$item_class[] = 'wpex-max-w-100';
									if ( ! empty( $this->atts['flex_basis'] ) ) {
										$item_class[] = 'wpex-flex-shrink-0';
									}
								}

								// Modern CSS grids.
								if ( 'css_grid' === $grid_style || 'flex' === $display_type ) {
									$item_class[] = 'wpex-flex';
									$item_class[] = 'wpex-flex-col';
									$item_class[] = 'wpex-flex-grow';
								}

								// Old school grids.
								else {
									if ( $grid_is_responsive ) {
										$item_class[] = 'col';
									} else {
										$item_class[] = 'nr-col';
									}

									$item_class[] = 'col-' . sanitize_html_class( $entry_count );

									if ( $grid_columns ) {
										$item_class[] = 'span_1_of_' . sanitize_html_class( $grid_columns );
									}

									if ( $grid_is_responsive ) {
										$rs = vcex_parse_multi_attribute( $this->atts['grid_columns_responsive_settings'] );
										foreach ( $rs as $key => $val ) {
											if ( $val ) {
												$item_class[] = 'span_1_of_' . sanitize_html_class( $val ) . '_' . sanitize_html_class( $key );
											}
										}
									}
								}

								if ( 'masonry' === $grid_style ) {
									$item_class[] = 'wpex-masonry-col';
								}

								break;
						}

					if ( function_exists( 'vcex_get_post_term_classes' ) ) {
						$terms = vcex_get_post_term_classes();
						if ( $terms && is_array( $terms ) ) {
							foreach ( $terms as $term_name ) {
								$item_class[] = $term_name;
							}
						}
					}

					/**
					 * Filters the Post Cards shortcode entry classes.
					 *
					 * @param array $class
					 * @param array $shortcode_atts
					 */
					$item_class = (array) apply_filters( 'wpex_post_cards_entry_class', $item_class, $this->atts );

					$entry_style = '';
					$entry_style_args = [];

					if ( 'flex' === $display_type ) {
						if ( ! empty( $this->atts['flex_basis'] ) ) {
							$entry_style_args['flex_basis'] = $this->atts['flex_basis'];
						}
					}

					if ( $entry_style_args ) {
						$entry_style = vcex_inline_style( $entry_style_args );
					}

					// Begin entry output.
					$inner_output .= '<div class="' . esc_attr( implode( ' ', array_unique( $item_class ) ) ) . '"' . $entry_style . '>';

						$inner_output .= wpex_get_card( $card_args );

					$inner_output .= '</div>';

					// List Divider.
					if ( 'list' === $display_type && ! empty( $this->atts['list_divider'] ) ) {
						$inner_output .= $this->list_divider( $this->atts );
					}

					// Reset entry count.
					if ( 'grid' === $display_type
						&& 'fit_rows' === $grid_style
						&& $entry_count === $grid_columns
					) {
						$entry_count = 0;
					}

					endwhile;

				// Update ajax vars after loop.
				if ( isset( $ajax_atts ) ) {
					$ajax_atts['entry_count'] = $entry_count; // Update counter
					$ajax_atts['running_count'] = $running_count; // Update running count
				}

				// Reset post data.
				wp_reset_postdata();

				// Remove running count.
				set_query_var( 'wpex_loop_running_count', null );

			// Close element that holds main (not featured) posts.
			$inner_output .= '</div>';

			/*-------------------------------------*/
			/* [ Pagination ]
			/*-------------------------------------*/
			if ( 'grid' === $display_type || 'list' === $display_type ) {
				$pagination_type = $this->get_pagination_type();
				switch( $pagination_type ) {
					case 'loadmore';
					case 'infinite_scroll';
						if ( ! empty( $this->query->max_num_pages ) ) {
							vcex_loadmore_scripts();
							$ajax_atts = $ajax_atts ?? array();
							$infinite_scroll = ( 'infinite_scroll' === $pagination_type ) ? true : false;
							$inner_output .= vcex_get_loadmore_button( self::TAG, $ajax_atts, $this->query, $infinite_scroll );
						}
						break;
					case 'numbered';
						$inner_output .= vcex_pagination( $this->query, false );
						break;
				}
			}

			// Close featured aside wrap.
			if ( $has_featured_card && ! $is_featured_card_top ) {
				$inner_output .= '</div>';
			}

			// Close featured flex wrap.
			if ( $has_featured_card && ! $is_featured_card_top ) {
				$inner_output .= '</div>';
			}

		endif; // has posts check.

		/*-------------------------------------*/
		/* [ Put inner_output inside wrap. ]
		/*-------------------------------------*/
		$this->output .= '<div';

				if ( ! empty( $this->atts['unique_id'] ) ) {
					$this->output .= ' id="' . esc_attr( $this->atts['unique_id'] ) . '"';
				}

				$this->output .= ' class="' . esc_attr( implode( ' ', $this->get_wrap_classes( $has_featured_card ) ) ) . '"';

				/*
				// @todo switch to this when adding ajax filter functionality.
				if ( isset( $ajax_atts ) ) {
					$this->output .= ' data-vcex-public-nonce="' . esc_attr( wp_create_nonce( 'vcex-public-nonce' ) ) . '"';
					$this->output .= ' data-vcex-settings="' . esc_attr( $this->get_json_data( $ajax_atts ) ) . '"';
				}*/

			$this->output .= '>';

		$this->output .= $inner_output;

		$this->output .= '</div>'; // close wrap

		// If no posts are found display message.
		else :

			$this->output .= vcex_no_posts_found_message( $this->atts );

		endif; // End post check.

		// @codingStandardsIgnoreLine
		echo $this->output;
	}

	/**
	 * Return array of wrap classes.
	 */
	protected function get_wrap_classes( $has_featured_card = false ) {
		$classes = array(
			'wpex-post-cards',
			'wpex-post-cards-' . sanitize_html_class( $this->atts['card_style'] ),
		);

		if ( $has_featured_card ) {
			$classes[] = 'wpex-post-cards-has-featured'; // @todo rename to use BEM
		}

		if ( 'woocommerce' === $this->atts['card_style'] ) {
			$classes[] = 'woocommerce';
		}

		if ( ! empty( $this->atts['bottom_margin'] ) ) {
			$classes[] = vcex_sanitize_margin_class( $this->atts['bottom_margin'], 'wpex-mb-' );
		}

		if ( ! empty( $this->atts['el_class'] ) ) {
			$classes[] = wp_strip_all_tags( $this->atts['el_class'] );
		}

		if ( ! empty( $this->atts['css_animation'] ) && vcex_validate_att_boolean( 'css_animation_sequential', $this->atts ) ) {
			$classes[] = 'wpb-animate-in-sequence';
		}

		return $classes;
	}

	/**
	 * Return json data used for ajax functions.
	 */
	protected function get_json_data( $ajax_atts = array() ) {
		return wp_json_encode( $ajax_atts, false );
	}

	/**
	 * Return card args based on shortcode atts.
	 */
	protected function get_card_args() {
		$args = array(
			'style' => $this->atts['card_style'],
		);

		if ( ! empty( $this->atts['template_id'] ) ) {
			$args['template_id'] = $this->atts['template_id'];
		}

		if ( ! empty( $this->atts['display_type'] ) ) {
			$args['display_type'] = $this->atts['display_type'];
		}

		if ( ! empty( $this->atts['link_type'] ) ) {
			$args['link_type'] = $this->atts['link_type'];
		}

		if ( ! empty( $this->atts['modal_title'] ) ) {
			$args['modal_title'] = $this->atts['modal_title'];
		}

		if ( ! empty( $this->atts['modal_template'] ) ) {
			$args['modal_template'] = $this->atts['modal_template'];
		}

		if ( ! empty( $this->atts['link_target'] ) ) {
			$args['link_target'] = $this->atts['link_target'];
		}

		if ( ! empty( $this->atts['link_rel'] ) ) {
			$args['link_rel'] = $this->atts['link_rel'];
		}

		if ( ! empty( $this->atts['title_font_size'] ) ) {
			$args['title_font_size'] = $this->atts['title_font_size'];
		}

		if ( ! empty( $this->atts['title_tag'] ) ) {
			$args['title_tag'] = $this->atts['title_tag'];
		}

		if ( ! empty( $this->atts['css_animation'] ) ) {
			$args['css_animation'] = $this->atts['css_animation'];
		}

		if ( isset( $this->atts['more_link_text'] ) && '' !== $this->atts['more_link_text'] ) {
			$args['more_link_text'] = $this->atts['more_link_text']; // allows "0" for disabling.
		}

		if ( ! empty( $this->atts['media_width'] ) ) {
			$args['media_width'] = $this->atts['media_width'];
		}

		if ( ! empty( $this->atts['media_breakpoint'] ) ) {
			$args['breakpoint'] = $this->atts['media_breakpoint'];
		}

		if ( empty( $this->atts['thumbnail_size'] ) || 'wpex_custom' === $this->atts['thumbnail_size'] ) {
			$args['thumbnail_size'] = array(
				$this->atts['thumbnail_width'],
				$this->atts['thumbnail_height'],
				$this->atts['thumbnail_crop'],
			);
		} else {
			$args['thumbnail_size'] = $this->atts['thumbnail_size'];
		}

		if ( ! empty( $this->atts['thumbnail_overlay_style'] ) ) {
			$args['thumbnail_overlay_style'] = $this->atts['thumbnail_overlay_style'];
		}

		if ( ! empty( $this->atts['thumbnail_overlay_button_text'] ) ) {
			$args['thumbnail_overlay_button_text'] = $this->atts['thumbnail_overlay_button_text'];
		}

		if ( ! empty( $this->atts['thumbnail_hover'] ) ) {
			$args['thumbnail_hover'] = $this->atts['thumbnail_hover'];
		}

		if ( ! empty( $this->atts['thumbnail_filter'] ) ) {
			$args['thumbnail_filter'] = $this->atts['thumbnail_filter'];
		}

		if ( ! empty( $this->atts['media_el_class'] ) ) {
			$args['media_el_class'] = $this->atts['media_el_class'];
		}

		if ( ! empty( $this->atts['card_el_class'] ) ) {
			$args['el_class'] = $this->atts['card_el_class'];
		}

		if ( isset( $this->atts['excerpt_length'] ) && '' !== $this->atts['excerpt_length'] ) {
			$args['excerpt_length'] = $this->atts['excerpt_length'];
		}

		if ( ! empty( $this->atts['alternate_flex_direction'] ) ) {
			$args['alternate_flex_direction'] = $this->atts['alternate_flex_direction'];
		}

		$allowed_media = $this->get_allowed_media();

		if ( ! is_null( $allowed_media ) ) {
			$args['allowed_media'] = $allowed_media;
		}

		return $args;
	}

	/**
	 * Check if loadmore is enabled.
	 */
	protected function has_loadmore() {
		if ( ( ! empty( $this->atts['pagination'] ) && in_array( $this->atts['pagination'], array( 'loadmore', 'infinite_scroll' ) ) ) || vcex_validate_att_boolean( 'pagination_loadmore', $this->atts ) ) {
			return true;
		}
		return false;
	}

	/**
	 * Check if featured card is enabled.
	 */
	protected function has_featured_card() {
		if ( $this->doing_loadmore ) {
			return false;
		}

		// Check if the featured card is enabled.
		$check = vcex_validate_att_boolean( 'featured_card', $this->atts );

		// Do not show featured card on paginated pages.
		if ( ( ! vcex_validate_att_boolean( 'featured_show_on_paged', $this->atts ) || $this->has_loadmore() ) && is_paged() ) {
			$check = false;
		}

		/**
		 * Filters whether the featured card is enabled or not.
		 *
		 * @param bool $check
		 */
		$check = (bool) apply_filters( 'wpex_post_cards_has_featured_card', $check, $this->atts );

		return $check;
	}

	/**
	 * Get supported media.
	 */
	protected function get_allowed_media() {
		if ( $this->is_elementor_widget() ) {
			return null;
		}
		if ( array_key_exists( 'allowed_media', $this->atts ) ) {
			if ( $this->atts['allowed_media'] && is_string( $this->atts['allowed_media'] ) ) {
				$this->atts['allowed_media'] = wp_parse_list( $this->atts['allowed_media'] );
			}
			foreach ( $this->atts['allowed_media'] as $k => $v ) {
				if ( ! in_array( $v, array( 'thumbnail', 'video' ) ) ) {
					unset( $this->atts['allowed_media'][$k] );
				}
			}
			return $this->atts['allowed_media'];
		}
	}

	/**
	 * Check if featured card is enabled.
	 */
	protected function has_featured_card_custom_query() {
		if ( $this->has_loadmore() || ! vcex_validate_att_boolean( 'featured_show_on_paged', $this->atts ) ) {
			return true;
		}
		return false;
	}

	/**
	 * Get featured card ID.
	 */
	protected function get_featured_post_id() {
		$featured_post_id = 0;

		if ( ! empty( $this->atts['featured_post_id'] ) ) {
			$post_id = $this->atts['featured_post_id'];
			$post = get_post( $post_id );
			if ( $post && 'publish' === $post->post_status ) {
				$featured_post_id = wpex_parse_obj_id( $post_id, get_post_type( $post ) );
			}
		} elseif ( $this->has_featured_card_custom_query() ) {
			$query_args = $this->atts;
			$query_args['posts_per_page'] = 1;
			$query_posts = vcex_build_wp_query( $query_args );
			if ( $query_posts->have_posts() && ! empty( $query_posts->posts[0] ) ) {
				$featured_post_id = $query_posts->posts[0]->ID ?? 0;
			}
		}

		/**
		 * Filters the featured card ID.
		 *
		 * @param int $featured_post_id
		 * @param array $shortcode attributes
		 */
		$featured_post_id = (int) apply_filters( 'wpex_post_cards_featured_post_id', $featured_post_id, $this->atts );

		return $featured_post_id;
	}

	/**
	 * Featured card args.
	 *
	 * @todo can we optimize this to use a loop instead?
	 */
	protected function get_featured_card_args( $post_id ) {
		$featured_card_style = ! empty( $this->atts['featured_style'] ) ? $this->atts['featured_style'] : $this->atts['card_style'];

		$args = array(
			'post_id'  => $post_id,
			'style'    => $featured_card_style,
			'featured' => true,
		);

		if ( ! empty( $this->atts['featured_template_id'] ) ) {
			$args['template_id'] = $this->atts['featured_template_id'];
		}

		if ( ! empty( $this->atts['featured_title_font_size'] ) ) {
			$args['title_font_size'] = $this->atts['featured_title_font_size'];
		}

		if ( ! empty( $this->atts['featured_title_tag'] ) ) {
			$args['title_tag'] = $this->atts['featured_title_tag'];
		}

		if ( empty( $this->atts['featured_thumbnail_size'] ) || 'wpex_custom' === $this->atts['featured_thumbnail_size'] ) {
			$args['thumbnail_size'] = array(
				$this->atts['featured_thumbnail_width'],
				$this->atts['featured_thumbnail_height'],
				$this->atts['featured_thumbnail_crop'],
			);
		} else {
			$args['thumbnail_size'] = $this->atts['featured_thumbnail_size'];
		}

		if ( isset( $this->atts['featured_more_link_text'] ) && '' !== $this->atts['featured_more_link_text'] ) {
			$args['more_link_text'] = $this->atts['featured_more_link_text']; // allows "0" for disabling.
		}

		if ( isset( $this->atts['featured_excerpt_length'] ) && '' !== $this->atts['featured_excerpt_length'] ) {
			$args['excerpt_length'] = $this->atts['featured_excerpt_length'];
		}

		if ( ! empty( $this->atts['thumbnail_overlay_style'] ) ) {
			$args['thumbnail_overlay_style'] = $this->atts['thumbnail_overlay_style'];
		}

		if ( ! empty( $this->atts['thumbnail_overlay_button_text'] ) ) {
			$args['thumbnail_overlay_button_text'] = $this->atts['thumbnail_overlay_button_text'];
		}

		if ( ! empty( $this->atts['thumbnail_hover'] ) ) {
			$args['thumbnail_hover'] = $this->atts['thumbnail_hover'];
		}

		if ( ! empty( $this->atts['thumbnail_filter'] ) ) {
			$args['thumbnail_filter'] = $this->atts['thumbnail_filter'];
		}

		if ( ! empty( $this->atts['media_el_class'] ) ) {
			$args['media_el_class'] = $this->atts['media_el_class'];
		}

		if ( ! empty( $this->atts['featured_el_class'] ) ) {
			$args['el_class'] = $this->atts['featured_el_class'];
		}

		if ( ! empty( $this->atts['modal_title'] ) ) {
			$args['modal_title'] = $this->atts['modal_title'];
		}

		if ( ! empty( $this->atts['modal_template'] ) ) {
			$args['modal_template'] = $this->atts['modal_template'];
		}

		if ( ! empty( $this->atts['link_type'] ) ) {
			$args['link_type'] = $this->atts['link_type'];
		}

		if ( ! empty( $this->atts['link_target'] ) ) {
			$args['link_target'] = $this->atts['link_target'];
		}

		if ( ! empty( $this->atts['link_rel'] ) ) {
			$args['link_rel'] = $this->atts['link_rel'];
		}

		if ( ! empty( $this->atts['featured_media_width'] ) ) {
			$args['media_width'] = $this->atts['featured_media_width'];
		}

		if ( ! empty( $this->atts['featured_media_breakpoint'] ) ) {
			$args['breakpoint'] = $this->atts['featured_media_breakpoint'];
		} elseif ( ! empty( $this->atts['media_breakpoint'] ) ) {
			$args['breakpoint'] = $this->atts['media_breakpoint'];
		}

		$allowed_media = $this->get_allowed_media();

		if ( ! is_null( $allowed_media ) ) {
			$args['allowed_media'] = $allowed_media;
		}

		/**
		 * Filters the wpex_post_card featured card args.
		 *
		 * @param array $args
		 * @param array $shortcode_attributes
		 */
		$args = (array) apply_filters( 'wpex_post_cards_featured_card_args', $args, $this->atts );

		return $args;
	}

	/**
	 * Get featured card location.
	 */
	protected function get_featured_card_location() {
		return $this->atts['featured_location'] ?? 'top';
	}

	/**
	 * Check if featured card is enabled.
	 */
	protected function is_featured_card_top() {
		$location = $this->get_featured_card_location();
		if ( 'left' === $location || 'right' === $location ) {
			return false;
		}
		return true;
	}

	/**
	 * Featured Card Divider
	 */
	protected function featured_divider() {
		$divider_class = array(
			'wpex-post-cards-featured-card-divider',
			'wpex-divider',
			'wpex-divider-' . sanitize_html_class( $this->atts['featured_divider'] ),
		);

		if ( ! empty( $this->atts['featured_divider_size'] ) ) {
			$divider_size = absint( $this->atts['featured_divider_size'] );
			if ( 1 === $divider_size ) {
				$divider_class[] = 'wpex-border-b';
			} else {
				$divider_class[] = 'wpex-border-b-' . $divider_size;
			}
		}

		$divider_style = '';

		if ( ! empty( $this->atts['featured_divider_color'] ) ) {
			$divider_style = vcex_inline_style( array(
				'border_color' => $this->atts['featured_divider_color'],
			) );
		}

		$spacing = ! empty( $this->atts['featured_divider_margin'] ) ? $this->atts['featured_divider_margin'] : 15;

		if ( ! empty( $this->atts['featured_margin'] ) ) {
			$divider_class[] = 'wpex-mt-' . sanitize_html_class( absint( $spacing ) );
			$divider_class[] = 'wpex-mb-' . sanitize_html_class( absint( $this->atts['featured_margin'] ) );
		} else {
			$divider_class[] = 'wpex-my-' . sanitize_html_class( absint( $spacing ) );
		}

		return '<div class="' . esc_attr( implode( ' ', $divider_class ) ) . '"' . $divider_style . '></div>';
	}

	/**
	 * List Divider.
	 */
	protected function list_divider() {
		$divider_class = array(
			'wpex-card-list-divider',
			'wpex-divider',
			'wpex-divider-' . sanitize_html_class( $this->atts['list_divider'] ),
		);

		$divider_class[] = 'wpex-my-0'; // remove default margin since we want to use gaps.

		if ( ! empty( $this->atts['list_divider_size'] ) ) {
			$divider_size = absint( $this->atts['list_divider_size'] );
			if ( 1 === $divider_size ) {
				$divider_class[] = 'wpex-border-b';
			} else {
				$divider_class[] = 'wpex-border-b-' . $divider_size;
			}
		}

		$divider_style = '';

		if ( ! empty( $this->atts['list_divider_color'] ) ) {
			$divider_style = vcex_inline_style( array(
				'border_color' => $this->atts['list_divider_color'],
			) );
		}

		return '<div class="' . esc_attr( implode( ' ', $divider_class ) ) . '"' . $divider_style . '></div>';
	}

	/**
	 * Get display type.
	 */
	protected function get_display_type() {
		return ! empty( $this->atts['display_type'] ) ? $this->atts['display_type'] : 'grid';
	}

	/**
	 * Get pagination type.
	 */
	protected function get_pagination_type() {
		$pagination_type = $this->atts['pagination'] ?? '';
		$is_auto_query = vcex_validate_att_boolean( 'auto_query', $this->atts );

		$allowed_choices = array(
			'loadmore',
			'numbered',
			'infinite_scroll',
		);

		// Allowed pagination styles.
		if ( ! in_array( $pagination_type, $allowed_choices ) ) {
			$pagination_type = '';
		}

		// Enable pagination for auto and custom queries.
		if ( ! $pagination_type && ( $is_auto_query || ( vcex_validate_att_boolean( 'custom_query', $this->atts ) && ! empty( $this->query->query['pagination'] ) ) ) ) {
				$pagination_type = 'numbered';
		}

		// relevanssi fix.
		if ( $pagination_type && $is_auto_query && function_exists( 'relevanssi_do_query' ) && is_search() ) {
			$pagination_type = 'numbered';
		}

		return $pagination_type;
	}

	/**
	 * Get grid style.
	 */
	protected function get_grid_style() {
		$allowed = array(
			'css_grid',
			'masonry',
			'fit_rows',
		);
		if ( ! empty( $this->atts['grid_style'] ) && in_array( $this->atts['grid_style'], $allowed ) ) {
			return $this->atts['grid_style'];
		}
		return 'fit_rows';
	}

	/**
	 * Get grid style.
	 */
	protected function get_grid_columns() {
		return ! empty( $this->atts['grid_columns'] ) ? absint( $this->atts['grid_columns'] ) : 1;
	}

	/**
	 * Get grid gap class.
	 */
	protected function get_grid_gap_class() {
		$display_type = $this->get_display_type();
		if ( 'carousel' === $display_type ) {
			return;
		}
		$gap        = '';
		$grid_style = $this->get_grid_style();
		switch ( $display_type ) {
			case 'list':
				$gap = ! empty( $this->atts['list_spacing'] ) ? $this->atts['list_spacing'] : '15';
				break;
			case 'flex':
			case 'grid':
				$default = '';
				if ( 'css_grid' === $grid_style || 'flex' === $display_type ) {
					$default = '20';  // css_grid needs a default gap.
				}
				$gap = ! empty( $this->atts['grid_spacing'] ) ? $this->atts['grid_spacing'] : $default;
				break;
		}
		if ( $gap ) {
			if ( 'list' === $display_type
				|| 'flex' === $display_type
				|| ( 'grid' === $display_type && 'css_grid' === $grid_style )
			) {
				$use_utl_class = true;
			} else {
				$use_utl_class = false;
			}
			if ( 'none' === $gap ) {
				if ( $use_utl_class ) {
					return 'wpex-gap-0';
				} else {
					return 'gap-none';
				}
			}
			if ( function_exists( 'wpex_column_gaps' )
				&& array_key_exists( str_replace( 'px', '', $gap ), wpex_column_gaps() )
			) {
				if ( $use_utl_class ) {
					return 'wpex-gap-' . absint( $gap );
				} else {
					return 'gap-' . absint( $gap );
				}
			}
		}
	}

	/**
	 * Check if displaying within an elementor widget.
	 */
	protected function is_elementor_widget() {
		if ( isset( $this->atts['is_elementor_widget'] ) && vcex_validate_boolean( $this->atts['is_elementor_widget'] ) ) {
			return true;
		}
	}

}