<?php
/**
 * Class representing vTimezones.
 *
 * $Horde: framework/iCalendar/iCalendar/vtimezone.php,v 1.8.10.9 2008/07/03 08:42:58 jan Exp $
 *
 * Copyright 2003-2008 The Horde Project (http://www.horde.org/)
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Mike Cochrane <mike@graftonhall.co.nz>
 * @since   Horde 3.0
 * @package Horde_iCalendar
 * @changes	2010/02/26 Joerg Lehrke <jlehrke@noc.de>: Add RDATE support (for KDE 4.x)
 */
class Horde_iCalendar_vtimezone extends Horde_iCalendar {

    function getType()
    {
        return 'vTimeZone';
    }

    function exportvCalendar()
    {
        return parent::_exportvData('VTIMEZONE');
    }

    /**
     * Parse child components of the vTimezone component. Returns an
     * array with the exact time of the time change as well as the
     * 'from' and 'to' offsets around the change. Time is arbitrarily
     * based on UTC for comparison.
     */
    function parseChild(&$child, $year)
    {
        // Make sure 'time' key is first for sort().
        $result['time'] = 0;
        $rrule_interval = 0; // 0 undefined, 1 yearly, 12 monthly

        $t = $child->getAttribute('TZOFFSETFROM');
        if (is_a($t, 'PEAR_Error')) {
            return false;
        }
        $result['from'] = ($t['hour'] * 60 * 60 + $t['minute'] * 60) * ($t['ahead'] ? 1 : -1);

        $t = $child->getAttribute('TZOFFSETTO');
        if (is_a($t, 'PEAR_Error')) {
            return false;
        }
        $result['to'] = ($t['hour'] * 60 * 60 + $t['minute'] * 60) * ($t['ahead'] ? 1 : -1);

        $switch_time = $child->getAttribute('DTSTART');
        if (is_a($switch_time, 'PEAR_Error')) {
            return false;
        }

        $rdates = $child->getAttribute('RDATE');
        if (!is_a($rdates, 'PEAR_Error')) {
	        foreach ($rdates as $rdate) {
		        $switch_time = $switch_time['value'];
		        $switch_year = date("Y", $switch_time);
		        if ($switch_year == $year) {
			        $t = getdate($switch_time);
			        $result['time'] = @gmmktime($t['hours'], $t['minutes'], $t['seconds'],
				        $t['mon'], $t['mday'], $t['year']);
			        return $result;
		        }
	        }
        }

        $rrules = $child->getAttribute('RRULE');
        if (is_a($rrules, 'PEAR_Error')) {
            if (!is_int($switch_time)) {
                return false;
            }
            // Convert this timestamp from local time to UTC for
            // comparison (All dates are compared as if they are UTC).
            $t = getdate($switch_time);
            $result['time'] = @gmmktime($t['hours'], $t['minutes'], $t['seconds'],
                                        $t['mon'], $t['mday'], $t['year']);
            return $result;
        }

        $switch_year = date("Y", $switch_time);
        if ($switch_year > $year) {
	        return false;
        }

        $rrules = explode(';', $rrules);
        foreach ($rrules as $rrule) {
            $t = explode('=', $rrule);
            switch ($t[0]) {
            case 'FREQ':
            	switch($t[1]) {
            		case 'YEARLY':
            			if ($rrule_interval == 12) {
                    		return false;
                		}
                		$rrule_interval = 1;
                		break;
                	case 'MONTHLY':
                		if ($rrule_interval == 1) {
                    		return false;
                		}
                		$rrule_interval = 12;
                		break;
                	default:
                		return false;
            	}
                break;

            case 'INTERVAL':
                if ($rrule_interval && $t[1] != $rrule_interval) {
                    return false;
                }
                $rrule_interval = intval($t[1]);
                if ($rrule_interval != 1 && $rrule_interval != 12) {
                	return false;
                }
                break;

            case 'COUNT':
	            if ($switch_year + intval($t[1]) < intval($year)) {
		            return false;
	            }
	            break;

            case 'BYMONTH':
                $month = intval($t[1]);
                break;

            case 'BYDAY':
                $len = strspn($t[1], '1234567890-+');
                if ($len == 0) {
                    return false;
                }
                $weekday = substr($t[1], $len);
                $weekdays = array(
                    'SU' => 0,
                    'MO' => 1,
                    'TU' => 2,
                    'WE' => 3,
                    'TH' => 4,
                    'FR' => 5,
                    'SA' => 6
                );
                $weekday = $weekdays[$weekday];
                $which = intval(substr($t[1], 0, $len));
                break;

            case 'UNTIL':
                if (intval($year) > intval(substr($t[1], 0, 4))) {
                    return false;
                }
                break;
            }
        }

		if ($rrule_interval == 12) {
			$month = date("n", $switch_time);
		}

        if (empty($month) || !isset($weekday)) {
            return false;
        }

        if (is_int($switch_time)) {
            // Was stored as localtime.
            $switch_time = strftime('%H:%M:%S', $switch_time);
            $switch_time = explode(':', $switch_time);
        } else {
            $switch_time = explode('T', $switch_time);
            if (count($switch_time) != 2) {
                return false;
            }
            $switch_time[0] = substr($switch_time[1], 0, 2);
            $switch_time[2] = substr($switch_time[1], 4, 2);
            $switch_time[1] = substr($switch_time[1], 2, 2);
        }

        // Get the timestamp for the first day of $month.
        $when = gmmktime($switch_time[0], $switch_time[1], $switch_time[2],
                         $month, 1, $year);
        // Get the day of the week for the first day of $month.
        $first_of_month_weekday = intval(gmstrftime('%w', $when));

        // Go to the first $weekday before first day of $month.
        if ($weekday >= $first_of_month_weekday) {
            $weekday -= 7;
        }
        $when -= ($first_of_month_weekday - $weekday) * 60 * 60 * 24;

        // If going backwards go to the first $weekday after last day
        // of $month.
        if ($which < 0) {
            do {
                $when += 60*60*24*7;
            } while (intval(gmstrftime('%m', $when)) == $month);
        }

        // Calculate $weekday number $which.
        $when += $which * 60 * 60 * 24 * 7;

        $result['time'] = $when;

        return $result;
    }

}

/**
 * @package Horde_iCalendar
 */
class Horde_iCalendar_standard extends Horde_iCalendar {

    function getType()
    {
        return 'standard';
    }

    function parsevCalendar($data)
    {
        parent::parsevCalendar($data, 'STANDARD');
    }

    function exportvCalendar()
    {
        return parent::_exportvData('STANDARD');
    }

}

/**
 * @package Horde_iCalendar
 */
class Horde_iCalendar_daylight extends Horde_iCalendar {

    function getType()
    {
        return 'daylight';
    }

    function parsevCalendar($data)
    {
        parent::parsevCalendar($data, 'DAYLIGHT');
    }

    function exportvCalendar()
    {
        return parent::_exportvData('DAYLIGHT');
    }

}
