<?php 
//define and clean up formats for display
$summary_format = str_replace ( ">", "&gt;", str_replace ( "<", "&lt;", em_get_option ( 'dbem_ical_description_format' ) ) );
$description_format = str_replace ( ">", "&gt;", str_replace ( "<", "&lt;", em_get_option ( 'dbem_ical_real_description_format') ) );
$location_format = str_replace ( ">", "&gt;", str_replace ( "<", "&lt;", em_get_option ( 'dbem_ical_location_format' ) ) );
$parsed_url = parse_url(get_bloginfo('url'));
$site_domain = preg_replace('/^www./', '', $parsed_url['host']);
$timezone_support = defined('EM_ICAL_TIMEZONE_SUPPORT') ? EM_ICAL_TIMEZONE_SUPPORT : true;

//figure out limits
$ical_limit = em_get_option('dbem_ical_limit');
$page_limit = $ical_limit > 50 || !$ical_limit ? 50:$ical_limit; //set a limit of 50 to output at a time, unless overall limit is lower
//get passed on $args and merge with defaults
$args = !empty($args) ? $args:array(); /* @var $args array */
$args = array_merge(array('limit'=>$page_limit, 'page'=>'1', 'owner'=>false, 'orderby'=>'event_start_date,event_start_time', 'scope' => em_get_option('dbem_ical_scope') ), $args);
$args = apply_filters('em_calendar_template_args',$args);
//get first round of events to show, we'll start adding more via the while loop
$EM_Events = EM_Events::get( $args );
$timezones = array();

//calendar header
$output_header = "BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//wp-events-plugin.com//".EM_VERSION."//EN";

//if timezone is supported, we output the blog timezone here
if( $timezone_support ){
	//get default blog timezone and output only if we're not in UTC or with manual offsets
	$blog_timezone = EM_DateTimeZone::create()->getName();
	if( !preg_match('/^UTC/', $blog_timezone) ){
		$output_header .= "
TZID:{$blog_timezone}
X-WR-TIMEZONE:{$blog_timezone}";
	}
}

echo preg_replace("/([^\r])\n/", "$1\r\n", $output_header);

//loop through events
$count = 0;
while ( count($EM_Events) > 0 ){
	foreach ( $EM_Events as $EM_Event ) {
		/* @var $EM_Event EM_Event */
	    if( $ical_limit != 0 && $count > $ical_limit ) break; //we've reached our maximum
	    //figure out the timezone of this event, or if it's an offset and add to list of timezones and date ranges to define in VTIMEZONE
	    $show_timezone = $timezone_support && !preg_match('/^UTC/', $EM_Event->get_timezone()->getName());
	    if( $show_timezone ){
	    	$timezone = $EM_Event->start()->getTimezone()->getName();
	    	if( empty($timezones[$timezone]) ){
	    		$timezones[$timezone] = array( $EM_Event->start()->getTimestamp(), $EM_Event->end()->getTimestamp() );
	    	}else{
	    		if( $timezones[$timezone][0] > $EM_Event->start()->getTimestamp() ) $timezones[$timezone][0] = $EM_Event->start()->getTimestamp();
	    		if( $timezones[$timezone][1] < $EM_Event->end()->getTimestamp() ) $timezones[$timezone][1] = $EM_Event->end()->getTimestamp();
	    	}
	    }
	    //calculate the times along with timezone offsets
		if($EM_Event->event_all_day){
			//we get local time since we're representing a date not a time
			$dateStart	= ';VALUE=DATE:'.$EM_Event->start()->format('Ymd'); //all day
			$dateEnd	= ';VALUE=DATE:'.$EM_Event->end()->copy()->add('P1D')->format('Ymd'); //add one day
		}else{
			//get date output with timezone and local time if timezone output is enabled, or UTC time if not and/or if offset is manual
			if( $show_timezone ){
				//show local time and define a timezone
				$dateStart	= ':'.$EM_Event->start()->format('Ymd\THis');
				$dateEnd = ':'.$EM_Event->end()->format('Ymd\THis');
			}else{
				//create a UTC equivalent time for all events irrespective of timezone
				$dateStart	= ':'.$EM_Event->start(true)->format('Ymd\THis\Z');
				$dateEnd = ':'.$EM_Event->end(true)->format('Ymd\THis\Z');
			}
		}
		if( $show_timezone ){
			$dateStart = ';TZID='.$timezone . $dateStart;
			$dateEnd = ';TZID='.$timezone . $dateEnd;
		}
		if( !empty($EM_Event->event_date_modified) && $EM_Event->event_date_modified != '0000-00-00 00:00:00' ){
			$dateModified =  get_gmt_from_date($EM_Event->event_date_modified, 'Ymd\THis\Z');
		}else{
		    $dateModified = get_gmt_from_date($EM_Event->post_modified, 'Ymd\THis\Z');
		}
		
		//formats
		$summary_raw = apply_filters('em_ical_output_content_summary', $EM_Event->output($summary_format,'ical'), $EM_Event, $args);
		$summary = em_mb_ical_wordwrap('SUMMARY:'.$summary_raw);
		$description_raw = apply_filters('em_ical_output_content_description', $EM_Event->output($description_format,'ical'), $EM_Event, $args);
		$description = em_mb_ical_wordwrap('DESCRIPTION:'. $description_raw);
		$url = 'URL:'.$EM_Event->get_permalink();
		$url = wordwrap($url, 74, "\n ", true);
		$location = $location_raw = $geo = $apple_geo = $apple_location = $apple_location_title = $apple_structured_location = $categories = false;
		if( $EM_Event->location_id ){
			$location_raw = $EM_Event->output($location_format, 'ical');
			if( $EM_Event->get_location()->location_latitude || $EM_Event->get_location()->location_longitude ){
				$geo = 'GEO:'.$EM_Event->get_location()->location_latitude.";".$EM_Event->get_location()->location_longitude;
			}
			if( !defined('EM_ICAL_APPLE_STRUCT') || !EM_ICAL_APPLE_STRUCT ){
				$apple_location = str_replace(';', '', html_entity_decode(str_replace('\;', ';', $EM_Event->output('#_LOCATIONFULLLINE, #_LOCATIONCOUNTRY', 'ical'))));
				$apple_location_title = str_replace('\;', '', html_entity_decode(str_replace('\;', ';', $EM_Event->output('#_LOCATIONNAME', 'ical'))));
				$apple_geo = !empty($geo) ? $EM_Event->get_location()->location_latitude.",".$EM_Event->get_location()->location_longitude:'0,0';
				$apple_structured_location = "X-APPLE-STRUCTURED-LOCATION;VALUE=URI;X-ADDRESS={$apple_location};X-APPLE-RADIUS=100;X-TITLE={$apple_location_title}:geo:{$apple_geo}";
				$apple_structured_location = str_replace('"', '\"', $apple_structured_location); //google chucks a wobbly with these on this line
				$apple_structured_location = em_mb_ical_wordwrap($apple_structured_location);
			}
		}elseif( $EM_Event->has_event_location() ){
			$ical_location = $EM_Event->get_event_location()->get_ical_location();
			if( $ical_location ){
				$location_raw = $ical_location;
			}
		}
		if( $location_raw ) {
			$location_raw = apply_filters('em_ical_output_content_location', $location_raw, $EM_Event, $args);
			$location = em_mb_ical_wordwrap('LOCATION:'. $location_raw);
		}
		$categories = array();
		foreach( $EM_Event->get_categories() as $EM_Category ){ /* @var EM_Category $EM_Category */
			$categories[] = $EM_Category->name;
		}
		$image = $EM_Event->get_image_url();
		
		//create a UID, make it unique and update independent
		$UID = $EM_Event->event_id . '@' . $site_domain;
		if( is_multisite() ) $UID = absint($EM_Event->blog_id) . '-' . $UID;
		$UID = wordwrap("UID:".$UID, 74, "\r\n ", true);
//output ical item		
$output = "\r\n"."BEGIN:VEVENT
{$UID}
DTSTART{$dateStart}
DTEND{$dateEnd}
DTSTAMP:{$dateModified}
{$url}
{$summary}";
//Description if available
if( $description ){
    $output .= "\r\n" . $description;
}
//add featured image if exists
if( $image ){
	$image = wordwrap("ATTACH;FMTTYPE=image/jpeg:".esc_url_raw($image), 74, "\n ", true);
	$output .= "\r\n" . $image;
}
//add categories if there are any
if( !empty($categories) ){
	$categories = wordwrap("CATEGORIES:".implode(',', $categories), 74, "\n ", true);
	$output .= "\r\n" . $categories;
}
//Location if there is one
if( $location ){
	$output .= "\r\n" . $location;
	//geo coordinates if they exist
	if( $geo ){
		$output .= "\r\n" . $geo;
	}
	//create apple-compatible feature for locations
	if( !empty($apple_structured_location) ){
		$output .= "\r\n" . $apple_structured_location;
	}
}
// add organizer if specifically requested via $args in locate_template
if( !empty($include_organizer) ){
	/*
	 * Currently unused via settings, can be activated by adding this line of code somewhere e.g. your theme functions.php file
	 * add_filter('em_locate_template_args_templates/ical.php', function($args){ $args['include_organizer'] = true; return $args; });
	 */
	$EM_Person = new EM_Person($EM_Event->get_owner());
	$output .= "\r\n" . 'ORGANIZER;CN="'. $EM_Person->get_name() .'":MAILTO:'. $EM_Person->user_email;
}
// allow for filters to intervene the output
$output = apply_filters('em_ical_event_output_content', $output, $EM_Event);
//end the event
$output .= "
END:VEVENT";

		//clean up new lines, rinse and repeat
		echo preg_replace("/([^\r])\n/", "$1\r\n", $output);
		$count++;
	}
	if( $ical_limit != 0 && $count >= $ical_limit ){ 
	    //we've reached our limit, or showing one event only
	    break;
	}else{
	    //get next page of results
	    $args['page']++;
		$EM_Events = EM_Events::get( $args );
	}
}

//Now we sort out timezones and add it to the top of the output
if( $timezone_support && !empty($timezones) ){
	$vtimezones = array();
	foreach( $timezones as $timezone => $timezone_range ){
		$vtimezones[$timezone] = array();
		$previous_offset = false;
		//get the range of transitions, with a year's cushion so we can calculate the TZOFFSETFROM value
		$EM_DateTimeZone = EM_DateTimeZone::create($timezone);
		$timezone_transitions = $EM_DateTimeZone->getTransitions($timezone_range[0] - YEAR_IN_SECONDS, $timezone_range[1] + YEAR_IN_SECONDS);
		do{
			$current_transition = current($timezone_transitions);
			$transition_key = key($timezone_transitions);
			$next_transition = next($timezone_transitions);
			//format the offset to a UTC-OFFSET
			$current_offset_sign = $current_transition['offset'] < 0 ? '-' : '+';
			$current_offset_hours = absint(floor($current_transition['offset'] / HOUR_IN_SECONDS));
			$current_offset_minute_seconds = absint($current_transition['offset']) - $current_offset_hours*HOUR_IN_SECONDS;
			$current_offset_minutes = $current_offset_minute_seconds == 0 ? 0 : absint($current_offset_minute_seconds / MINUTE_IN_SECONDS);
			$current_transition['offset'] = $current_offset_sign . str_pad($current_offset_hours, 2, "0", STR_PAD_LEFT) . str_pad($current_offset_minutes, 2, "0", STR_PAD_LEFT);
			//skip transitions before and after the event date range, assuming we have some in between
			if( !empty($next_transition) && $next_transition['ts'] < $timezone_range[0] ){
				//remember previous offset
				$previous_offset = $current_transition['offset'];
				continue;
			}
			if( $current_transition['ts'] > $timezone_range[1] ) break;
			//modify the transition array directly and add it to vtimezones array
			unset( $current_transition['time'] );
			$current_transition['isdst'] = $current_transition['isdst'] ? 'DAYLIGHT':'STANDARD';
			$EM_DateTime = new EM_DateTime($current_transition['ts'], $EM_DateTimeZone);
			$current_transition['ts'] = $EM_DateTime->format('Ymd\THis');
			$current_transition['offsetfrom'] = $previous_offset === false ? $current_transition['offset'] : $previous_offset;
			$vtimezones[$timezone][] = $current_transition;
			//remember previous offset
			$previous_offset = $current_transition['offset'];
		} while( $next_transition !== false );
	}
	foreach( $vtimezones as $timezone => $timezone_transitions ){
		$output = "
BEGIN:VTIMEZONE
TZID:{$timezone}
X-LIC-LOCATION:{$timezone}";
		foreach( $timezone_transitions as $transition ){
			$output .= "
BEGIN:{$transition['isdst']}
DTSTART:{$transition['ts']}
TZOFFSETFROM:{$transition['offsetfrom']}
TZOFFSETTO:{$transition['offset']}
TZNAME:{$transition['abbr']}
END:{$transition['isdst']}";
		}
		$output .= "
END:VTIMEZONE";
		echo preg_replace("/([^\r])\n/", "$1\r\n", $output);
	}
}

//calendar footer
echo "\r\n"."END:VCALENDAR";