<?php

/**
 * Calendar Widget class
 *
 * A widget that displays the next X upcoming events (similar to Agenda view).
 */
class Ai1ec_View_Admin_Widget extends Ai1ec_Embeddable {

    /**
     * @var boolean
     */
    protected $_css_loaded = false;

    /**
     * @return string
     */
    public function get_id() {
        return 'ai1ec_agenda_widget';
    }

    /**
     * Register the widget class.
     */
    public static function register_widget() {
        register_widget( 'Ai1ec_View_Admin_Widget' );
    }

    /**
     * Constructor for widget.
     */
    public function __construct() {

        parent::__construct(
            $this->get_id(),
            __( 'Upcoming Events', AI1EC_PLUGIN_NAME ),
            array(
                'description' => __( 'All-in-One Event Calendar: Lists upcoming events in Agenda view', AI1EC_PLUGIN_NAME ),
                'class' => 'ai1ec-agenda-widget',
            )
        );
    }

    /* (non-PHPdoc)
     * @see Ai1ec_Embeddable::register_javascript_widget()
     */
    public function register_javascript_widget( $id_base ) {
        $this->_registry->get( 'controller.javascript-widget' )
            ->add_widget( $id_base, 'view.calendar.widget' );
    }
    /* (non-PHPdoc)
     * @see Ai1ec_Embeddable::get_defaults()
     */
    public function get_defaults() {
        return array(
            'title'                  => __( 'Upcoming Events', AI1EC_PLUGIN_NAME ),
            'events_seek_type'       => 'events',
            'events_per_page'        => 10,
            'days_per_page'          => 10,
            'show_subscribe_buttons' => true,
            'show_calendar_button'   => true,
            'hide_on_calendar_page'  => true,
            'limit_by_cat'           => false,
            'limit_by_tag'           => false,
            'cat_ids'                => array(),
            'tag_ids'                => array(),
            'link_for_days'          => true,
        );
    }

    /* (non-PHPdoc)
     * @see Ai1ec_Embeddable::get_configurable_for_widget_creation()
     */
    public function get_configurable_for_widget_creation() {
        $defaults = $this->get_js_widget_configurable_defaults();
        return array(
            'events_seek_type' => array(
                'renderer' => array(
                    'class'   => 'select',
                    'label'   => __(
                        'Choose how to limit the upcoming events',
                        AI1EC_PLUGIN_NAME
                    ),
                    'options' => array(
                        array(
                            'text'  => __(
                                'Events',
                                AI1EC_PLUGIN_NAME
                            ),
                            'value' => 'events'
                        ),
                        array(
                            'text'  => __(
                                'Days',
                                AI1EC_PLUGIN_NAME
                            ),
                            'value' => 'days'
                        ),
                    ),
                ),
                'value' => $defaults['events_seek_type']
            ),
            'events_per_page' => array(
                'renderer' => array(
                    'class'     => 'input',
                    'label'     => Ai1ec_I18n::__( 'Number of events to show' ),
                    'type'      => 'append',
                    'append'    => 'events',
                ),
                'value'  => $defaults['events_per_page'],
            ),
            'days_per_page' => array(
                'renderer' => array(
                    'class'     => 'input',
                    'label'     => Ai1ec_I18n::__( 'Number of days to show' ),
                    'type'      => 'append',
                    'append'    => 'days',
                ),
                'value'  => $defaults['days_per_page'],
            ),
            'upcoming_widgets_default_tags_categories' => array(
                'renderer' => array(
                    'class' => 'tags-categories',
                    'label' => __(
                        'Show events filtered for the following tags/categories',
                        AI1EC_PLUGIN_NAME
                    ),
                    'help'  => __(
                        'To clear, hold &#8984;/<abbr class="initialism">CTRL</abbr> and click selection.',
                        AI1EC_PLUGIN_NAME
                    )
                ),
                'value' => array(
                    'categories' => array(),
                    'tags'       => array(),
                ),
            ),
            'show_subscribe_buttons' => array(
                'renderer' => array(
                    'class'     => 'checkbox',
                    'label'     => Ai1ec_I18n::__( 'Show the subscribe button in the widget' ),
                ),
                'value'  => $defaults['show_subscribe_buttons'],
            ),
        );
    }

    /* (non-PHPdoc)
     * @see Ai1ec_Calendar_View_Abstract::get_name()
    */
    public function get_name() {
        return 'Upcoming Events';
    }

    /**
     * The icon class associated with the widget.
     *
     * @return string
     */
    public function get_icon() {
        return 'ai1ec-fa ai1ec-fa-clock-o';
    }

    /**
     * Form function.
     *
     * Renders the widget's configuration form for the Manage Widgets page.
     *
     * @param  array $instance The data array for the widget instance being configured.
     * @return void
     */
    public function form( $instance ) {
        $default = $this->get_defaults();
        $instance = wp_parse_args( (array) $instance, $default );

        // Get available cats, tags, events to allow user to limit widget to certain categories
        $events_categories = get_terms( 'events_categories', array( 'orderby' => 'name', "hide_empty" => false ) );
        $events_tags       = get_terms( 'events_tags', array( 'orderby' => 'name', "hide_empty" => false ) );

        // Generate unique IDs and NAMEs of all needed form fields
        $fields = array(
            'title'                  => array('value'   => $instance['title']),
            'events_seek_type'       => array('value'   => $instance['events_seek_type']),
            'events_per_page'        => array('value'   => $instance['events_per_page']),
            'days_per_page'          => array('value'   => $instance['days_per_page']),
            'show_subscribe_buttons' => array('value'   => $instance['show_subscribe_buttons']),
            'show_calendar_button'   => array('value'   => $instance['show_calendar_button']),
            'hide_on_calendar_page'  => array('value'   => $instance['hide_on_calendar_page']),
            'limit_by_cat'           => array('value'   => $instance['limit_by_cat']),
            'limit_by_tag'           => array('value'   => $instance['limit_by_tag']),
            'cat_ids'          => array(
                                              'value'   => (array)$instance['cat_ids'],
                                              'options' => $events_categories
                                             ),
            'tag_ids'          => array(
                                              'value'   => (array)$instance['tag_ids'],
                                              'options' => $events_tags
                                             ),
        );
        foreach ( $fields as $field => $data ) {
            $fields[$field]['id']    = $this->get_field_id( $field );
            $fields[$field]['name']  = $this->get_field_name( $field );
            $fields[$field]['value'] = $data['value'];
            if ( isset($data['options']) ) {
                $fields[$field]['options'] = $data['options'];
            }
        }

        // Display theme
        $this->_registry->get( 'theme.loader' )->get_file(
            'agenda-widget-form.php',
            $fields,
            true
        )->render();
    }

    /**
     * Update function.
     *
     * Called when a user submits the widget configuration form.
     * The data should be validated and returned.
     *
     * @param  array $new_instance The new data that was submitted.
     * @param  array $old_instance The widget's old data.
     * @return array               The new data to save for this widget instance.
     */
    public function update( $new_instance, $old_instance ) {
        // Save existing data as a base to modify with new data
        $instance = $old_instance;
        $instance['title']                  = strip_tags( $new_instance['title'] );
        $instance['events_per_page']        = Ai1ec_Primitive_Int::index(
            $new_instance['events_per_page'],
            1,
            1
        );
        $instance['days_per_page']          = Ai1ec_Primitive_Int::index(
            $new_instance['days_per_page'],
            1,
            1
        );
        $instance['events_seek_type']       = $this->_valid_seek_type(
            $new_instance['events_seek_type']
        );
        $instance['show_subscribe_buttons'] = isset( $new_instance['show_subscribe_buttons'] ) ? true : false;
        $instance['show_calendar_button']   = isset( $new_instance['show_calendar_button'] ) ? true : false;
        $instance['hide_on_calendar_page']  = isset( $new_instance['hide_on_calendar_page'] ) ? true : false;

        // For limits, set the limit to False if no IDs were selected, or set the respective IDs to empty if "limit by" was unchecked
        $instance['limit_by_cat'] = false;
        $instance['cat_ids'] = array();
        if ( isset( $new_instance['cat_ids'] ) && $new_instance['cat_ids'] != false ) {
            $instance['limit_by_cat'] = true;
        }
        if ( isset( $new_instance['limit_by_cat'] ) && $new_instance['limit_by_cat'] != false ) {
            $instance['limit_by_cat'] = true;
        }
        if ( isset( $new_instance['cat_ids'] ) && $instance['limit_by_cat'] === true ) {
            $instance['cat_ids'] = $new_instance['cat_ids'];
        }

        $instance['limit_by_tag'] = false;
        $instance['tag_ids'] = array();
        if ( isset( $new_instance['tag_ids'] ) && $new_instance['tag_ids'] != false ) {
            $instance['limit_by_tag'] = true;
        }
        if ( isset( $new_instance['limit_by_tag'] ) && $new_instance['limit_by_tag'] != false ) {
            $instance['limit_by_tag'] = true;
        }
        if ( isset( $new_instance['tag_ids'] ) && $instance['limit_by_tag'] === true ) {
            $instance['tag_ids'] = $new_instance['tag_ids'];
        }

        return $instance;
    }

    /* (non-PHPdoc)
     * @see Ai1ec_Embeddable::add_js()
     */
    public function add_js() {
        $this->_registry->get( 'controller.javascript' )->add_link_to_render_js(
            Ai1ec_Javascript_Controller::LOAD_ONLY_FRONTEND_SCRIPTS,
            false
        );
    }

    /* (non-PHPdoc)
     * @see Ai1ec_Embeddable::get_content()
     */
    public function get_content( array $args_for_widget, $remote = false ) {
        $agenda     = $this->_registry->get(
            'view.calendar.view.agenda',
            $this->_registry->get( 'http.request.parser' )
        );
        $time       = $this->_registry->get( 'date.time' );
        $search     = $this->_registry->get( 'model.search' );
        $settings   = $this->_registry->get( 'model.settings' );
        $html       = $this->_registry->get( 'factory.html' );

        $is_calendar_page = is_page( $settings->get( 'calendar_page_id' ) );
        if ( $args_for_widget['hide_on_calendar_page'] &&
            $is_calendar_page ) {
            return;
        }

        // Add params to the subscribe_url for filtering by Limits (category, tag)
        $subscribe_filter  = '';
        if ( ! is_array( $args_for_widget['cat_ids'] ) ) {
            $args_for_widget['cat_ids'] = explode( ',', $args_for_widget['cat_ids'] );
        }

        if ( ! is_array( $args_for_widget['tag_ids'] ) ) {
            $args_for_widget['tag_ids'] = explode( ',', $args_for_widget['tag_ids'] );
        }
        $subscribe_filter .= $args_for_widget['cat_ids'] ? '&ai1ec_cat_ids=' . join( ',', $args_for_widget['cat_ids'] ) : '';
        $subscribe_filter .= $args_for_widget['tag_ids'] ? '&ai1ec_tag_ids=' . join( ',', $args_for_widget['tag_ids'] ) : '';

        // Get localized time
        $timestamp = $time->format_to_gmt();

        // Set $limit to the specified category/tag
        $limit = array(
            'cat_ids'    => $args_for_widget['cat_ids'],
            'tag_ids'    => $args_for_widget['tag_ids'],
        );
        $limit = apply_filters( 'ai1ec_add_filters_upcoming_widget', $limit );

        // Get events, then classify into date array
        // JB: apply seek check here
        $seek_days  = ( 'days' === $args_for_widget['events_seek_type'] );
        $seek_count = $args_for_widget['events_per_page'];
        $last_day   = false;
        if ( $seek_days ) {
            $seek_count = $args_for_widget['days_per_page'] * 5;
            $last_day   = strtotime(
                '+' . $args_for_widget['days_per_page'] . ' days'
            );
        }

        $event_results = $search->get_events_relative_to(
            $timestamp,
            $seek_count,
            0,
            $limit
        );
        if ( $seek_days ) {
            foreach ( $event_results['events'] as $ek => $event ) {
                if ( $event->get( 'start' )->format() >= $last_day ) {
                    unset( $event_results['events'][$ek] );
                }
            }
        }

        $dates                    = $agenda->get_agenda_like_date_array( $event_results['events'] );


        $args_for_widget['dates']                     = $dates;
        // load CSS just once for all widgets.
        // Do not load it on the calendar page as it's already loaded.
        if ( false === $this->_css_loaded && ! $is_calendar_page ) {
            if ( true === $remote ) {
                $args_for_widget['css'] = $this->_registry->get( 'css.frontend' )->get_compiled_css();
            }
            $this->_css_loaded = true;
        }
        $args_for_widget['show_location_in_title']    = $settings->get( 'show_location_in_title' );
        $args_for_widget['show_year_in_agenda_dates'] = $settings->get( 'show_year_in_agenda_dates' );
        $args_for_widget['calendar_url']              = $html->create_href_helper_instance( $limit )->generate_href();
        $args_for_widget['subscribe_url']             = AI1EC_EXPORT_URL . $subscribe_filter;
        $args_for_widget['subscribe_url_no_html']     = AI1EC_EXPORT_URL . '&no_html=true' . $subscribe_filter;
        $args_for_widget['text_upcoming_events']      = __( 'There are no upcoming events.', AI1EC_PLUGIN_NAME );
        $args_for_widget['text_all_day']              = __( 'all-day', AI1EC_PLUGIN_NAME );
        $args_for_widget['text_view_calendar']        = __( 'View Calendar', AI1EC_PLUGIN_NAME );
        $args_for_widget['text_edit']                 = __( 'Edit', AI1EC_PLUGIN_NAME );
        $args_for_widget['text_venue_separator']      = __( '@ %s', AI1EC_PLUGIN_NAME );
        $args_for_widget['text_subscribe_label']      = __( 'Add', AI1EC_PLUGIN_NAME );
        $args_for_widget['subscribe_buttons_text']    = $this->_registry
            ->get( 'view.calendar.subscribe-button' )
            ->get_labels();
        // Display theme
        return $this->_registry->get( 'theme.loader' )->get_file(
            'agenda-widget.twig',
            $args_for_widget
        )->get_content();
    }

    /* (non-PHPdoc)
     * @see Ai1ec_Embeddable::get_js_widget_configurable_defaults()
     */
    public function get_js_widget_configurable_defaults() {
        $def = $this->get_defaults();
        unset( $def['title'] );
        unset( $def['link_for_days'] );
        return $def;
    }

    /* (non-PHPdoc)
     * @see Ai1ec_Embeddable::javascript_widget()
     */
    public function javascript_widget( $args ) {
        $args['show_calendar_button'] = false;
        $args['link_for_days']        = false;
        return parent::javascript_widget( $args );
    }

    /* (non-PHPdoc)
     * @see Ai1ec_Embeddable::check_requirements()
     */
    public function check_requirements() {
        return null;
    }

    /**
     * _valid_seek_type method.
     *
     * Return valid seek type for given user input (selection).
     *
     * @param  string $value User selection for seek type
     * @return string        Seek type to use
     */
    protected function _valid_seek_type( $value ) {
        static $list = array( 'events', 'days' );
        if ( ! in_array( $value, $list ) ) {
            return (string)reset( $list );
        }
        return $value;
    }

}
