<?php

// Exit if accessed directly
if (!defined('ABSPATH')) {
    exit;
}

class WPSBC_Search_Widget_Outputter
{

    /**
     * The shortcode attributes
     *
     */
    private $args;

    /**
     * The start date
     *
     */
    private $start_date;

    /**
     * The end date
     *
     */
    private $end_date;

    /**
     * Variable that holds the errors
     *
     */
    private $has_error = false;

    /**
     * The error message
     *
     */
    private $error;

    /**
     * Constructor
     *
     */
    public function __construct($args, $start_date = null, $end_date = null)
    {

        /**
         * Set the attributes
         *
         */
        $this->args = $args;

        /**
         * Set the start date
         */
        if ($start_date === null && isset($_GET['wpsbc-search-start-date'])) {
            $this->start_date = sanitize_text_field($_GET['wpsbc-search-start-date']);
        } else {
            $this->start_date = $start_date;
        }

        /**
         * Set the end date
         *
         */
        if ($end_date === null && isset($_GET['wpsbc-search-end-date'])) {
            $this->end_date = sanitize_text_field($_GET['wpsbc-search-end-date']);
        } else {
            $this->end_date = $end_date;
        }

        /**
         * Check for errors
         *
         */
        $this->check_errors();
    }

    /**
     * Constructs and returns the HTML for the Search Widget
     *
     * @return string
     *
     */
    public function get_display()
    {
        // Add the shortcode attributes
        $search_widget_html_data = '';
        foreach ($this->args as $att => $val) {
            $search_widget_html_data .= 'data-' . $att . '="' . esc_attr($val) . '" ';
        }

        $output = '<div class="wpsbc-search-widget wpsbc-search-widget-' . $this->get_search_type() . '-date-search" ' . $search_widget_html_data . '>';

        // Get the form
        $output .= '<div class="wpsbc-search-widget-form-wrap">';
        $output .= $this->get_display_search_form();
        $output .= '</div>';

        // Get the errors
        $output .= $this->get_display_error();

        // Get the results
        $output .= '<div class="wpsbc-search-widget-results-wrap" data-label-previous="' . $this->get_search_widget_string('label_previous') . '" data-label-next="' . $this->get_search_widget_string('label_next') . '">';
        $output .= $this->get_search_results();
        $output .= '</div>';

        $output .= $this->get_widget_styles();

        $output .= '<div class="wpsbc-search-container-loaded" data-just-loaded="1"></div>';

        $output .= '</div>';

        return $output;
    }

    /**
     * Constructs the HTML for the form errors
     *
     * @return string
     *
     */
    private function get_display_error()
    {

        // Check if we have errors
        if ($this->has_error === false) {
            return false;
        }

        // If we do, return them
        return '<div class="wpsbc-search-widget-error-field">' . $this->error . '</div>';
    }

    /**
     * Constructs the HTML for the search form
     *
     * @return string
     *
     */
    private function get_display_search_form()
    {
        ob_start();
        include 'views/view-search-form.php';
        $output = ob_get_contents();
        ob_end_clean();
        return $output;
    }

    /**
     * Constructs the HTML for the 'no results found' message
     *
     * @return string
     *
     */
    private function get_display_no_results()
    {
        return '<p class="wpsbc-search-widget-no-results">' . $this->get_search_widget_string('no_results') . '</p>';
    }

    /**
     * Constructs the HTML for the search results
     *
     * @return string
     *
     */
    private function get_search_results()
    {
        // Check if the form was submitted
        if ($this->is_form_submitted() === false) {

            if ($this->args['show_results_on_load'] == 'no') {
                return false;
            }

            $available_calendars = $this->get_all_results_calendar_data();
        } else {
            if ($this->has_error === true) {
                return false;
            }
            // Get search data
            $available_calendars = $this->get_search_results_calendar_data();
        }

        // Search results title
        $output = '<h2>' . $this->get_search_widget_string('results_title') . '</h2>';

        // Check if there are results
        if (empty($available_calendars)) {
            $output .= $this->get_display_no_results();
            return $output;
        }

        $output .= '<div class="wpsbc-search-widget-results">';

        // Display results
        foreach ($available_calendars as $calendar) {
            $output .= $this->get_display_search_result($calendar);
        }

        $output .= '</div>';

        return $output;
    }

    /**
     * Constructs the HTML for each search result row
     *
     * @param array $data
     * @return string
     *
     */
    private function get_display_search_result($data)
    {

        $data['button_label'] = $this->get_search_widget_string('view_button_label');

        $link_attributes = apply_filters('wpbs_search_result_link_attributes', array(
            'href' => $data['link'],
            'title' => $data['calendar_name'],
        ), $data, $this);

        $link_attributes_html = $this->array_to_html_attributes($link_attributes);

        $output = '<div class="wpsbc-search-widget-result' . ($data['featured_image'] ? ' wpsbc-has-thumb' : '') . '">';
        

            if ($data['featured_image']) {
                if (empty($data['link'])) {
                    $output .= $data['featured_image'];
                } else {
                    $output .= '<a class="wpsbc-search-widget-result-link" ' . $link_attributes_html . '>' . $data['featured_image'] . '</a>';
                }
            }

            $output .= '<div class="wpsbc-search-widget-result-title">';
                if (empty($data['link'])) {
                    $output .= '<h3>' . $data['calendar_name'] . '</h3>';
                } else {
                    $output .= '<h3><a class="wpsbc-search-widget-result-link" ' . $link_attributes_html . '>' . $data['calendar_name'] . '</a></h3>';
                    
                }
            $output .= '</div>';

            if (!empty($data['link'])) {
            $output .= '<a class="wpsbc-search-widget-result-button" ' . $link_attributes_html . '>' . $data['button_label'] . '</a>';
        }

        $output .= '</div>';

        return apply_filters('wpsbc_search_results_html', $output, $data);
    }

    /**
     * Checks if the form was submitted
     *
     * @return bool
     *
     */
    private function is_form_submitted()
    {
        if ($this->start_date === null || $this->end_date === null) {
            return false;
        }
        return true;
    }

    /**
     * Returns all calendars in the correct format, used when show_resutls_on_load is true
     *
     */
    private function get_all_results_calendar_data()
    {
        $available_calendars = [];

        if (empty($this->args['calendars']) || $this->args['calendars'] == 'all') {
            // All calendars
            $args = array('status' => 'active', 'orderby' => 'name', 'order' => 'asc');
        } else {
            // Specific calendars
            $calendar_ids = array_filter(array_map('trim', explode(',', $this->args['calendars'])));
            $args = array(
                'include' => $calendar_ids,
                'orderby' => 'FIELD( id, ' . implode(',', $calendar_ids) . ')',
                'order' => '',
            );
        }

        $args = apply_filters('wpsbc_search_calendar_query_args', $args);

        $calendars = wpsbc_get_calendars($args);

        foreach ($calendars as $calendar) {


            $name = $calendar->get_name($this->args['language']);

            // Get calendar Links
            $calendar_link_post_id = WPSBC_Calendar_Overview_Outputter::get_calendar_link_post_id($calendar->get('id'), $this->args['language']);
            $calendar_link = WPSBC_Calendar_Overview_Outputter::get_calendar_link($calendar->get('id'), $this->args['language']);


            // Featured image
            $featured_image = false;
            if ($this->args['featured_image'] == 'yes' && $calendar_link_post_id && has_post_thumbnail($calendar_link_post_id)) {
                $featured_image = get_the_post_thumbnail($calendar_link_post_id, apply_filters('wpsbc_search_wiget_featured_image_size', 'large'));
            }

            $available_calendars[] = array('calendar_name' => $name, 'calendar_id' => $calendar->get('id'), 'link' => $calendar_link, 'post_id' => $calendar_link_post_id, 'featured_image' => $featured_image,);
        }

        return $available_calendars;
    }

    /**
     * Does the actual search for available dates in calendars
     *
     */
    private function get_search_results_calendar_data()
    {
        // Format start date
        $start_datetime = apply_filters('wpsbc_search_widget_start_date', DateTime::createFromFormat('Y-m-d', $this->start_date));
        $start_date = $start_datetime->format('Ymd');

        // Format end date
        if ($this->get_search_type() == 'multiple') {
            $end_datetime = apply_filters('wpsbc_search_widget_end_date', DateTime::createFromFormat('Y-m-d', $this->end_date));
            $end_date = $end_datetime->format('Ymd');
        } else {
            $end_datetime = clone $start_datetime;
            $end_date = $start_date;
        }

        // Add custom search dates with +/- 2 days to properly handle ical events
        $search_start_date = clone $start_datetime;
        $search_end_date = clone $end_datetime;

        $search_start_date->modify('-2 days');
        $search_end_date->modify('+2 days');

        $search_start_date->modify('-2 days');
        $search_end_date->modify('+2 days');

        $all_events = wpsbc_get_events(array('custom_query' => 'AND CONCAT(date_year,LPAD(date_month, 2, "0"),LPAD(date_day, 2, "0")) >= ' . $search_start_date->format('Ymd') . ' AND CONCAT(date_year,LPAD(date_month, 2, "0"),LPAD(date_day, 2, "0")) <= ' . $search_end_date->format('Ymd'))); // Calendar Events

        $all_groupped_events = [];
        foreach ($all_events as $event) {
            $all_groupped_events[$event->get('calendar_id')][] = $event;
        }

        // Empty array with calendars
        $available_calendars = [];

        if (empty($this->args['calendars']) || $this->args['calendars'] == 'all') {
            // All calendars
            $args = apply_filters('wpsbc_search_widget_results_calendar_args', array('status' => 'active', 'orderby' => 'name', 'order' => 'asc'));
            $calendars = wpsbc_get_calendars($args);
        } else {
            // Specific calendars
            $calendar_ids = array_filter(array_map('trim', explode(',', $this->args['calendars'])));
            $args = array(
                'include' => $calendar_ids,
                'orderby' => 'FIELD( id, ' . implode(',', $calendar_ids) . ')',
                'order' => '',
            );

            $calendars = wpsbc_get_calendars($args);
        }

        // Loop through calendars
        foreach ($calendars as $calendar) {

            //$is_available = wpsbc_check_if_date_range_is_bookable($calendar->get('id'), $start_date, $end_date);

            // Assume calendar dates are available
            $is_available = true;
            $default_legend_is_unbookable = false;

            // Get non bookable legend items
            $non_bookable_legend_items = array();
            $legend_items = wpsbc_get_legend_items(array('calendar_id' => $calendar->get('id')));

            $changeover_start = $changeover_end = false;

            foreach ($legend_items as $legend_item) {
                if (!$legend_item->get('is_bookable')) {
                    $non_bookable_legend_items[] = $legend_item->get('id');

                    if ($legend_item->get('is_default')) {
                        $default_legend_is_unbookable = true;
                    }
                }

                  if ($legend_item->get('changeover_type') == 'start') {
                    $changeover_start = $legend_item->get('id');
                }

                if ($legend_item->get('changeover_type') == 'end') {
                    $changeover_end = $legend_item->get('id');
                }
            }

            $changeover_start_found = $changeover_end_found = 0;

            // Loop through events
            $calendar_events = isset($all_groupped_events[$calendar->get('id')]) ? $all_groupped_events[$calendar->get('id')] : [];
            $ical_events = wpsbc_get_ical_feeds_as_events($calendar->get('id'), $calendar_events, $search_start_date->format('Ymd'), $search_end_date->format('Ymd')); // iCalendar Events

            $events = array_merge($calendar_events, $ical_events);

            $sorted_events = array();

            foreach ($events as $event) {
                $sorted_events[$event->get('date_year') . str_pad($event->get('date_month'), 2, '0', STR_PAD_LEFT) . str_pad($event->get('date_day'), 2, '0', STR_PAD_LEFT)] = $event;
            }

            ksort($sorted_events);

            $matching_events = 0;

            $interval = DateInterval::createFromDateString('1 day');
            $start = DateTime::createFromFormat('Ymd', $start_date);
            $end = DateTime::createFromFormat('Ymd', $end_date);
            $end->modify('+1 day');
            $period = new DatePeriod($start, $interval, $end);

            $selection = [];

            foreach ($period as $date) {
                $selection[$date->format('Ymd')] = false;
            }

            foreach ($sorted_events as $event_date => $event) {

                // If event date is outside search range, continue;
                if ($event_date < $start_date || $event_date > $end_date) {
                    continue;
                }

                $event_legend_item_id = $event->get('legend_item_id') == null ? -1 : $event->get('legend_item_id');

                $matching_events++;

                // Check if we found a default legend item and if it's unbookable
                if ($event_legend_item_id === -1 && $default_legend_is_unbookable) {
                    $is_available = false;
                    break;
                }

                // Check if the event found is not bookable
                if (in_array($event_legend_item_id, $non_bookable_legend_items)) {
                    $is_available = false;
                    break;
                }

                // Check for changeovers. The rule is that if a start changeover exists in an array, we shouln't an end changeover

                // We found a starting changeover date
                if ($event_legend_item_id == $changeover_start) {
                    $changeover_start_found++;
                }

                if ($event_legend_item_id == $changeover_end) {
                    $changeover_end_found++;
                }

                // Now if we find an ending changeover date and a starting changeover date was previously found, we mark the date as not available.
                if ($event_legend_item_id == $changeover_end && $changeover_start_found !== 0) {
                    $is_available = false;
                    break;
                }

                // If more than 2 starting changeovers are found, selection is invalid
                if ($changeover_start_found !== 0 && $changeover_start_found > 1) {
                    $is_available = false;
                    break;
                }

                // If more than 2 ending changeovers are found, selection is invalid
                if ($changeover_end_found !== 0 && $changeover_end_found > 1) {
                    $is_available = false;
                    break;
                }

                $selection[$event_date] = $event_legend_item_id;
            }

            foreach ($selection as $i => $legend_id) {

                if ($legend_id === false) {
                    continue;
                }

                // Check if the selection starts with a starting changeover
                if ($i == $start_date && $legend_id == $changeover_start) {
                    $is_available = false;
                    break;
                }

                // Check if the selection ends with an ending changeover.
                if ($i == $end_date && $legend_id == $changeover_end) {
                    $is_available = false;
                    break;
                }

                // Check if we have any changeover in the middle of our selection range
                if ($i != $start_date && $i != $end_date && ($legend_id == $changeover_start || $legend_id == $changeover_end)) {
                    $is_available = false;
                    break;
                }
            }

            // If no events are found but the default legend is unbookable, it means that the date is not available.
            if (($matching_events === 0 || $matching_events != count($selection)) && $default_legend_is_unbookable === true) {
                $is_available = false;
            }

            if ($is_available === true) {

                // Get calendar name
                $name = $calendar->get_name($this->args['language']);

                // Get calendar Link
                $link = WPSBC_Calendar_Overview_Outputter::get_calendar_link($calendar->get('id'), $this->args['language']);
                $calendar_link_post_id = WPSBC_Calendar_Overview_Outputter::get_calendar_link_post_id($calendar->get('id'), $this->args['language']);

                // Featured image
                $featured_image = false;
                if ($this->args['featured_image'] == 'yes' && $calendar_link_post_id && has_post_thumbnail($calendar_link_post_id)) {
                    $featured_image = get_the_post_thumbnail($calendar_link_post_id, apply_filters('wpsbc_search_wiget_featured_image_size', 'large'));
                }

                // Build array
                $available_calendars[] = array('calendar_name' => $name, 'calendar_id' => $calendar->get('id'), 'link' => $link, 'post_id' => $calendar_link_post_id, 'featured_image' => $featured_image);
            }
        }

        return $available_calendars;
    }

    /**
     * Check for form errors
     *
     */
    private function check_errors()
    {

        // If form wasn't submitted, there is nothing to check
        if ($this->is_form_submitted() === false) {
            return false;
        }

        // Check if a starting day was entered
        if (empty($this->start_date)) {
            $this->has_error = true;
            $this->error = $this->get_search_widget_string('no_start_date');
            return;
        }

        // Check if an ending day was entered
        if ($this->get_search_type() == 'multiple' && empty($this->end_date)) {
            $this->has_error = true;
            $this->error = $this->get_search_widget_string('no_end_date');
            return;
        }

        // Check if the starting day is valid
        if (DateTime::createFromFormat('Y-m-d', $this->start_date) === false) {
            $this->has_error = true;
            $this->error = $this->get_search_widget_string('invalid_start_date');
            return;
        }

        // Check if the ending day is valid
        if ($this->get_search_type() == 'multiple' && DateTime::createFromFormat('Y-m-d', $this->end_date) === false) {
            $this->has_error = true;
            $this->error = $this->get_search_widget_string('invalid_end_date');
            return;
        }
    }

    /**
     * Helper function to get custom or translated strings
     *
     */
    private function get_search_widget_string($key)
    {

        $settings = get_option('wpsbc_settings', array());

        // Check for translation
        if (!empty($settings['search_addon'][$key . '_translation_' . $this->args['language']])) {
            return $settings['search_addon'][$key . '_translation_' . $this->args['language']];
        }

        // Check for default
        if (!empty($settings['search_addon'][$key])) {
            return $settings['search_addon'][$key];
        }

        return wpsbc_search_widget_default_strings()[$key];
    }

    /**
     * Get the "selection_type" shortcode parameter.
     *
     */
    private function get_search_type()
    {

        if (isset($this->args['selection_type']) && $this->args['selection_type'] == 'single') {
            return 'single';
        }

        return 'multiple';
    }

    /**
     * Generates the styles for the form colors
     *
     * @return string
     *
     */
    protected function get_widget_styles()
    {

        $settings = get_option('wpsbc_settings', array());

        $colors = array(
            'button_background_color' => '#aaaaaa',
            'button_background_hover_color' => '#7f7f7f',
            'button_text_color' => '#ffffff',
            'button_text_hover_color' => '#ffffff',
        );

        foreach ($colors as $color_key => $color) {
            if (isset($settings['search_addon'][$color_key]) && !empty($settings['search_addon'][$color_key])) {
                $colors[$color_key] = $settings['search_addon'][$color_key];
            }
        }

        $output = '<style>';

        // Button
        $output .= '.wpsbc-search-widget .wpsbc-search-widget-form .wpsbc-search-widget-field button.wpsbc-search-widget-datepicker-submit, .wpsbc-search-widget .wpsbc-search-widget-form .wpsbc-search-widget-field input[type="submit"], .wpsbc-search-widget .wpsbc-search-widget-results-wrap .wpsbc-search-widget-result .wpsbc-search-widget-result-button {background-color: ' . $colors['button_background_color'] . ' !important; color: ' . $colors['button_text_color'] . ' !important; }';

        // Button Hover
        $output .= '.wpsbc-search-widget .wpsbc-search-widget-form .wpsbc-search-widget-field button.wpsbc-search-widget-datepicker-submit:hover, .wpsbc-search-widget .wpsbc-search-widget-form .wpsbc-search-widget-field input[type="submit"]:hover , .wpsbc-search-widget .wpsbc-search-widget-results-wrap .wpsbc-search-widget-result .wpsbc-search-widget-result-button:hover {background-color: ' . $colors['button_background_hover_color'] . ' !important;  color: ' . $colors['button_text_hover_color'] . ' !important; }';

        // Datepicker Selection
        $output .= '.ui-datepicker.wpsbc-datepicker td.ui-datepicker-current-day {background-color: ' . $colors['button_background_color'] . ' !important; }';
        $output .= '.ui-datepicker.wpsbc-datepicker td.ui-datepicker-current-day a { color: ' . $colors['button_text_color'] . ' !important; }';

        // Datepicker Hover
        $output .= '.ui-datepicker.wpsbc-datepicker td .ui-state-default.ui-state-hover {background-color: ' . $colors['button_background_hover_color'] . ' !important;  color: ' . $colors['button_text_hover_color'] . ' !important; }';

        $output .= '</style>';

        return $output;
    }

    /**
     * Convert array to html attributes
     *
     */
    private function array_to_html_attributes($array)
    {
        return implode(' ', array_map(
            function ($k, $v) {
                return $k . '="' . esc_attr($v) . '"';
            },
            array_keys($array),
            $array
        ));
    }
}
