/**
  * get_matching_events function
  *
  * Get events that match with the arguments provided.
  *
  * @param int | bool          $start      Events start before this (GMT) time
  * @param int | bool          $end        Events end before this (GMT) time
  * @param array $filter       Array of filters for the events returned.
  *                            ['cat_ids']   => non-associatative array of category IDs
  *                            ['tag_ids']   => non-associatative array of tag IDs
  *                            ['post_ids']  => non-associatative array of post IDs
  *
  * @return array Matching events #'
  **/
 function get_matching_events($start = false, $end = false, $filter = array())
 {
     global $wpdb, $ai1ec_calendar_helper, $ai1ec_localization_helper;
     // holds event_categories sql
     $c_sql = '';
     $c_where_sql = '';
     // holds event_tags sql
     $t_sql = '';
     $t_where_sql = '';
     // holds posts sql
     $p_where_sql = '';
     // holds start sql
     $start_where_sql = '';
     // holds end sql
     $end_where_sql = '';
     // hold escape values
     $args = array();
     // =============================
     // = Generating start date sql =
     // =============================
     if ($start !== false) {
         $start_where_sql = "AND (e.start >= %s OR e.recurrence_rules != '')";
         $args[] = Ai1ec_Time_Utility::to_mysql_date($start);
     }
     // ===========================
     // = Generating end date sql =
     // ===========================
     if ($end !== false) {
         $end_where_sql = "AND (e.end <= %s OR e.recurrence_rules != '')";
         $args[] = Ai1ec_Time_Utility::to_mysql_date($end);
     }
     $wpml_join_particle = $ai1ec_localization_helper->get_wpml_table_join();
     $wpml_where_particle = $ai1ec_localization_helper->get_wpml_table_where();
     // Get the Join (filter_join) and Where (filter_where) statements based on $filter elements specified
     $ai1ec_calendar_helper->_get_filter_sql($filter);
     $query = $wpdb->prepare("SELECT *, e.post_id, e.start as start, e.end as end, e.allday, e.recurrence_rules, e.exception_rules,\n\t\t\t\te.recurrence_dates, e.exception_dates, e.venue, e.country, e.address, e.city, e.province, e.postal_code,\n\t\t\t\te.show_map, e.contact_name, e.contact_phone, e.contact_email, e.cost, e.ical_feed_url, e.ical_source_url,\n\t\t\t\te.ical_organizer, e.ical_contact, e.ical_uid " . "FROM {$wpdb->posts} " . "INNER JOIN {$wpdb->prefix}ai1ec_events AS e ON e.post_id = ID " . $wpml_join_particle . $filter['filter_join'] . "WHERE post_type = '" . AI1EC_POST_TYPE . "' " . "AND post_status = 'publish' " . $wpml_where_particle . $filter['filter_where'] . $start_where_sql . $end_where_sql, $args);
     $events = $wpdb->get_results($query, ARRAY_A);
     foreach ($events as &$event) {
         $event['start'] = Ai1ec_Time_Utility::from_mysql_date($event['start']);
         $event['end'] = Ai1ec_Time_Utility::from_mysql_date($event['end']);
         try {
             $event = new Ai1ec_Event($event);
         } catch (Ai1ec_Event_Not_Found $n) {
             unset($event);
             // The event is not found, continue to the next event
             continue;
         }
         // if there are recurrence rules, include the event, else...
         if (empty($event->recurrence_rules)) {
             // if start time is set, and event start time is before the range
             // it, continue to the next event
             if ($start !== false && $event->start < $start) {
                 unset($event);
                 continue;
             }
             // if end time is set, and event end time is after
             // it, continue to the next event
             if ($end !== false && $ev->end < $end) {
                 unset($event);
                 continue;
             }
         }
     }
     return $events;
 }
 /**
  * __construct function
  *
  * Create new event object, using provided data for initialization.
  *
  * @param int|array $data  Look up post with id $data, or initialize fields
  *                         with flat associative array $data containing both
  *                         post and event fields returned by join query
  *
  * @return void
  **/
 function __construct($data = null, $instance = false)
 {
     global $wpdb;
     if ($data == null) {
         return;
     }
     // ===========
     // = Post ID =
     // ===========
     if (is_numeric($data)) {
         // ============================
         // = Fetch post from database =
         // ============================
         $post = get_post($data);
         if (!$post || $post->post_status == 'auto-draft') {
             throw new Ai1ec_Event_Not_Found("Post with ID '{$data}' could not be retrieved from the database.");
         }
         $left_join = "";
         $select_sql = "e.post_id, e.recurrence_rules, e.exception_rules, " . "e.allday, e.instant_event, e.recurrence_dates, e.exception_dates, " . "e.venue, e.country, e.address, e.city, e.province, e.postal_code, " . "e.show_map, e.contact_name, e.contact_phone, e.contact_email, " . "e.contact_url, e.cost, e.ticket_url, e.ical_feed_url, " . "e.ical_source_url, e.ical_organizer, e.ical_contact, e.ical_uid, " . "e.longitude, e.latitude, e.show_coordinates, e.facebook_eid, " . "e.facebook_status, e.facebook_user, " . "GROUP_CONCAT( ttc.term_id ) AS categories, " . "GROUP_CONCAT( ttt.term_id ) AS tags ";
         if ($instance != false && is_numeric($instance)) {
             $select_sql .= ", IF( aei.start IS NOT NULL, aei.start, e.start ) as start," . "  IF( aei.start IS NOT NULL, aei.end,   e.end )   as end ";
             $instance = (int) $instance;
             $this->instance_id = $instance;
             $left_join = "LEFT JOIN {$wpdb->prefix}ai1ec_event_instances aei ON aei.id = {$instance} AND e.post_id = aei.post_id ";
         } else {
             $select_sql .= ", e.start as start, e.end as end, e.allday ";
         }
         // =============================
         // = Fetch event from database =
         // =============================
         $query = $wpdb->prepare("SELECT {$select_sql}" . "FROM {$wpdb->prefix}ai1ec_events e " . "LEFT JOIN {$wpdb->term_relationships} tr ON e.post_id = tr.object_id " . "LEFT JOIN {$wpdb->term_taxonomy} ttc ON tr.term_taxonomy_id = ttc.term_taxonomy_id AND ttc.taxonomy = 'events_categories' " . "LEFT JOIN {$wpdb->term_taxonomy} ttt ON tr.term_taxonomy_id = ttt.term_taxonomy_id AND ttt.taxonomy = 'events_tags' " . "{$left_join}" . "WHERE e.post_id = %d " . "GROUP BY e.post_id", $data);
         $event = $wpdb->get_row($query);
         if ($event === null || $event->post_id === null) {
             throw new Ai1ec_Event_Not_Found("Event with ID '{$data}' could not be retrieved from the database.");
         }
         $event->start = Ai1ec_Time_Utility::from_mysql_date($event->start);
         $event->end = Ai1ec_Time_Utility::from_mysql_date($event->end);
         // ===========================
         // = Assign post to property =
         // ===========================
         $this->post = $post;
         // ==========================
         // = Assign values to $this =
         // ==========================
         foreach ($this as $property => $value) {
             if ($property != 'post') {
                 if (isset($event->{$property})) {
                     $this->{$property} = $event->{$property};
                 }
             }
         }
     } elseif (is_array($data)) {
         // =======================================================
         // = Assign each event field the value from the database =
         // =======================================================
         foreach ($this as $property => $value) {
             if ($property != 'post' && array_key_exists($property, $data)) {
                 $this->{$property} = $data[$property];
                 unset($data[$property]);
             }
         }
         if (isset($data['post'])) {
             $this->post = (object) $data['post'];
         } else {
             // ========================================
             // = Remaining fields are the post fields =
             // ========================================
             $this->post = (object) $data;
         }
     } else {
         throw new Ai1ec_Invalid_Argument("Argument to constructor must be integer, array or null, not '{$data}'.");
     }
 }
 /**
  * _limit_result_set function
  *
  * Slice given number of events from list, with exception when all
  * events from last day shall be included.
  *
  * @param array $events   List of events to slice
  * @param int   $limit    Number of events to slice-off
  * @param bool  $last_day Set to true to include all events from last day ignoring {$limit}
  *
  * @return array Sliced events list
  */
 protected function _limit_result_set(array $events, $limit, $last_day)
 {
     global $ai1ec_events_helper;
     $limited_events = array();
     $start_day_previous = 0;
     foreach ($events as $event) {
         $start_day = date('Y-m-d', Ai1ec_Time_Utility::from_mysql_date($event['start']));
         --$limit;
         // $limit = $limit - 1;
         if ($limit < 0) {
             if (true === $last_day) {
                 if ($start_day != $start_day_previous) {
                     break;
                 }
             } else {
                 break;
             }
         }
         $limited_events[] = $event;
         $start_day_previous = $start_day;
     }
     return $limited_events;
 }