<?php
/**
 * JEvents Component for Joomla 1.5.x
 *
 * @version     $Id: iCalRRule.php 1670 2010-01-15 07:58:14Z geraint $
 * @package     JEvents
 * @copyright   Copyright (C) 2008-2009 GWE Systems Ltd, 2006-2008 JEvents Project Group
 * @license     GNU/GPLv2, see http://www.gnu.org/licenses/gpl-2.0.html
 * @link        http://www.jevents.net
 */

// no direct access
defined( '_JEXEC' ) or die( 'Restricted access' );

class iCalRRule extends JTable  {

	/** @var int Primary key */
	var $rr_id					= null;

	/**
	 * This holds the raw data as an array 
	 *
	 * @var array
	 */
	var $data;
	var $freq;

	// array of exception dates
	var $_exdate = array();

	/**
	 * Null Constructor
	 */
	function iCalRRule( &$db ) {
		parent::__construct( '#__jevents_rrule', 'rr_id', $db );
	}

	/**
	 * Pseudo Constructor
	 *
	 * @param iCal Entry parsed from ICS file as an array $ice
	 * @return n/a
	 */
	function iCalRRuleFromDB($icalrowAsArray){
		$db	=& JFactory::getDBO();
		$temp = new iCalRRule($db);

		$temp->data = $icalrowAsArray;

		// Should really test count
		$temp->processField2("freq","YEARLY");
		$temp->processField2("count",999);
		$temp->processField2("rinterval",1);
		//interval ios a mysql reserved word
		$temp->_interval = $temp->rinterval;
		if ($temp->freq=="none"){
			$temp->processField2("until","");
		}
		else {
			$cfg = & JEVConfig::getInstance();
			// cap indefinate repeats if count is blank as well as until
			if (array_key_exists("COUNT",$temp->data)){
				$temp->processField2("until","");
			}
			else {
				$temp->processField2("until",mktime(23,59,59,12,12,$cfg->get("com_latestyear",2020)));
			}
		}
		$temp->processField2("untilraw","");
		$temp->processField2("bysecond","");
		$temp->processField2("byminute","");
		$temp->processField2("byhour","");
		$temp->processField2("byday","");
		$temp->processField2("bymonthday","");
		$temp->processField2("byyearday","");
		$temp->processField2("byweekno","");
		$temp->processField2("bymonth","");
		$temp->processField2("bysetpos","");
		$temp->processField2("wkst","");
		return $temp;
	}
	function processField2($field,$default){
		$this->$field = array_key_exists(strtolower($field),$this->data)?$this->data[strtolower($field)]:$default;
	}


	/**
	 * Pseudo Constructor
	 *
	 * @param iCal Entry parsed from ICS file as an array $ice
	 * @return n/a
	 */
	function iCalRRuleFromData($rrule){
		$db	=& JFactory::getDBO();
		$temp = new iCalRRule($db);

		$temp->data = $rrule;
		$temp->freq = $temp->data['FREQ'];

		// Should really test count
		$temp->processField("count",999);
		$temp->processField("interval",1);
		//interval ios a mysql reserved word
		$temp->rinterval = $temp->interval;
		$temp->_interval = $temp->interval;
		unset($temp->interval);
		if ($temp->freq=="none"){
			$temp->processField("until","");
		}
		else {
			// cap indefinate repeats if count is blank as well as until
			if (array_key_exists("COUNT",$temp->data)){
				$temp->processField("until","");
			}
			else {
				$cfg = & JEVConfig::getInstance();
				$temp->processField("until",mktime(23,59,59,12,12,$cfg->get("com_latestyear",2020)));
			}
		}
		$temp->processField("untilraw","");
		$temp->processField("bysecond","");
		$temp->processField("byminute","");
		$temp->processField("byhour","");
		$temp->processField("byday","");
		$temp->processField("bymonthday","");
		$temp->processField("byyearday","");
		$temp->processField("byweekno","");
		$temp->processField("bymonth","");
		$temp->processField("bysetpos","");
		$temp->processField("wkst","");
		return $temp;
	}

	function processField($field,$default){
		$this->$field = array_key_exists(strtoupper($field),$this->data)?$this->data[strtoupper($field)]:$default;
	}
	/**
	 * Creates a repeat if not an exception date returns 1 anyway
	 *
	 * @param unknown_type $start
	 * @param unknown_type $end
	 * @return unknown
	 */
	function _makeRepeat($start,$end){
		if (!isset($this->_repetitions)) $this->_repetitions = array();
		$db	=& JFactory::getDBO();
		$repeat = new iCalRepetition($db);
		$repeat->eventid = $this->eventid;
		// TODO CHECK THIS logic
		$repeat->startrepeat = strftime('%Y-%m-%d %H:%M:%S',$start);
		// iCal for whole day uses 00:00:00 on the next day JEvents uses 23:59:59 on the same day
		list ($h,$m,$s) = explode(":",strftime("%H:%M:%S",$end));
		if (($h+$m+$s)==0) {
			//			$repeat->endrepeat = strftime('%Y-%m-%d 23:59:59',($end-86400));
			$repeat->endrepeat = strftime('%Y-%m-%d 23:59:59',$end);
		}
		else {
			$repeat->endrepeat = strftime('%Y-%m-%d %H:%M:%S',$end);
		}

		$repeat->duplicatecheck = md5($repeat->eventid . $start );

		// Double check its not in the list of exception dates
		foreach ($this->_exdate as $exdate) {
			// compare based on YYYYMMDD since exceptions are(normally) whole day, "EXDATE;VALUE=DATE"
			if (strftime('%Y%m%d', $exdate) == strftime('%Y%m%d', $start))	{
				// return 0;
				// exceptions count
				return 1;
			}
		}
		$this->_repetitions[] = $repeat;
		return 1;
	}

	function _afterUntil($testDate){
		if (strlen($this->until)==0) return false;
		if (!isset($this->_untilMidnight)) {
			list ($d,$m,$y) = explode(":",strftime("%d:%m:%Y",$this->until));
			$this->_untilMidnight = mktime(23,59,59,$m,$d,$y);
		}
		if (strlen($this->until)>0 && $testDate>intval($this->_untilMidnight)) {
			return true;
		}
		else return false;
	}


	/**
	 * sort the by days string for negative values so that we start at the beginning of the month/start date
	 *
	 * @param unknown_type $days
	 * @param unknown_type $currentMonthStart
	 * @param unknown_type $dtstart
	 */
	function sortByDays(&$days,$currentMonthStart,$dtstart){
		if (count($days)==0) return;
		// only sort negative values
		if (strpos($days[0],"-")===false) return;

		static $weekdayMap=array("SU"=>0,"MO"=>1,"TU"=>2,"WE"=>3,"TH"=>4,"FR"=>5,"SA"=>6);
		static $weekdayReverseMap=array("SU","MO","TU","WE","TH","FR","SA");

		list ($currentMonth,$currentYear) = explode(":",strftime("%m:%Y",$currentMonthStart));
		list ($startMonth,$startYear) = explode(":",strftime("%m:%Y",$dtstart));
		if ($startMonth==$currentMonth && $startYear==$currentYear){
			$startdate = $dtstart;
		}
		else {
			$startdate = $currentMonthStart;
		}
		$startWD = strftime("%w",$startdate);

		$sorteddays = array();
		// start from week -6 and go forward (overkill I know)
		for ($w=-6;$w<0;$w++){
			// now loop over the week starting at the appropriate day fo the week
			for ($i=0;$i<7;$i++){
				$wd = ($startWD+$i)%7;
				$check = strval($w).$weekdayReverseMap[$wd];
				if (in_array($check,$days)) $sorteddays[]=$check;
			}
		}
		$days = $sorteddays;

	}

	/**
	 * Technically this is very complicated 
	 * see http://www.w3.org/2002/12/cal/rfc2445 ad search for "BYxxx rule parts modify the recurrence in some manner"
	 * 
	 * Priority to 'analysis' should therefore be (??)
	 * 
	 * FREQ=YEARLY
	 * BYMONTH
	 * BYWEEKNO
	 * BYYEARDAY
	 * FREQ=MONTHLY ??
	 * BYMONTHDAY
	 * FREQ=WEEKLY ??
	 * BYDAY
	 * FREQ=DAILY ??
	 * BYHOUR, BYMINUTE, BYSECOND
	 * BYSETPOS
	 * 
	 * INTERVAL always applies to FREQ
	 * 
	 * So if I go over step in freq units adding the BYMONTH to YEARLY etc.
	 * then restricting DAILY with BYMONTH (do an excessive loop in this situation and test if the rules hols
	 * until I get some better logic)
	 */


	/**
	 * Generates repetition from vevent & rrule data from scratch
	 * The result can then be saved to the database
	 */
	function getRepetitions($dtstart,$dtend,$duration,$recreate=false,$exdate=array()) {
		// put exdate into somewhere that I can get from _makerepeat
		$this->_exdate = $exdate;
		// TODO  "getRepetitions doesnt yet deal with Short months and 31st or leap years/week 53<br/>";
		if ($dtend==0 && $duration>0){
			$dtend=$dtstart+$duration;
		}
		if (!$recreate && isset($this->_repetitions)) return $this->_repetitions;
		$this->_repetitions = array();
		if (!isset($this->eventid)) {
			echo "no eventid set in generateRepetitions<br/>";
			return $this->_repetitions;
		}

		if ($this->count==1){
			echo "count=1 returing<br/>";
			$this->_makeRepeat($dtstart,$dtend);
			return $this->_repetitions;
		}
		//list ($h,$min,$s,$d,$m,$y) = explode(":",strftime("%H:%M:%S:%d:%m:%Y",$end));

		list ($startHour,$startMin,$startSecond,$startDay,$startMonth,$startYear,$startWD)
		= explode(":",strftime("0%H:0%M:0%S:%d:%m:%Y:%w",$dtstart));
		//echo "$startHour,$startMin,$startSecond,$startDay,$startMonth,$startYear,$startWD,$dtstart<br/>";
		$dtstartMidnight = mktime(0,0,0,$startMonth,$startDay,$startYear);
		list ($endDay,$endMonth,$endYear,$endWD) = explode(":",strftime("%d:%m:%Y:%w",$dtend));
		$duration = $dtend-$dtstart;
		static $weekdayMap=array("SU"=>0,"MO"=>1,"TU"=>2,"WE"=>3,"TH"=>4,"FR"=>5,"SA"=>6);
		static $weekdayReverseMap=array("SU","MO","TU","WE","TH","FR","SA");
		static $dailySecs = 86400;
		static $weeklySecs = 604800;
		// TODO implement byyearday
		// TODO do full leap year audit e.g. YEARLY repeats
		echo "freq = ".$this->freq."<br/>";
		switch ($this->freq) {
			case "YEARLY":
				// TODO the code doesn't yet deal with multiple bymonths
				if ($this->bymonth=="") $this->bymonth=$startMonth;
				//if ($this->byday=="") $this->byday=$weekdayReverseMap[$startWD];

				// If I have byday and bymonthday then the two considions must be met
				$weekdays = array();
				if ($this->byday!=""){
					foreach (explode(",",$this->byday) as $bday) {
						$weekdays[]=$weekdayMap[$bday];
					}
				}

				if ($this->byyearday!=""){
					echo "byyearday <br/>";
					$days = explode(",",$this->byyearday);

					$start = $dtstart;
					$end = $dtend;
					$countRepeats = 0;
					$currentYearStart = mktime(0,0,0,1,1,$startYear);
					// do the current month first
					while  ($countRepeats < $this->count  && !$this->_afterUntil($currentYearStart)) {
						$currentYear = strftime("%Y",$currentYearStart);
						$currentYearDays = date("L",$currentYearStart)?366:365;
						foreach ($days as $day) {
							if ($countRepeats >= $this->count || $this->_afterUntil($start)) return  $this->_repetitions;

							// TODO I am assuming all + or all -ve
							$details=array();
							preg_match("/(\+|-?)(\d*)/",$day,$details);
							list($temp,$plusminus,$daynumber) = $details;
							if (strlen($plusminus)==0) $plusminus="+";

							// do not go over year end
							if ($daynumber>$currentYearDays) continue;
							if ($plusminus=="+"){
								$targetStart = mktime($startHour,$startMin,$startSecond,12,31,$currentYear-1);
								$targetStart = strtotime("+$daynumber days",$targetStart);
							}
							else {
								$targetStart = mktime($startHour,$startMin,$startSecond,1,1,$currentYear+1);
								$targetStart = strtotime("-$daynumber days",$targetStart);
							}
							$targetEnd = $targetStart+$duration;
							if ($countRepeats >= $this->count) {
								return  $this->_repetitions;
							}
							if ($targetStart>=$dtstartMidnight && !$this->_afterUntil($targetStart)){
								// double check for byday constraints
								if ($this->byday!=""){
									if (!in_array(strftime("%w",$targetStart),$weekdays)){
										continue;
									}
								}
								$countRepeats+=$this->_makeRepeat($targetStart,$targetEnd);
							}
						}
						// now ago to the start of next year
						if ($currentYear+$this->rinterval>2036) return  $this->_repetitions;
						$currentYearStart = mktime(0,0,0,1,1,$currentYear+$this->rinterval);
					}

				}

				// assume for now that its an anniversary of the start month only!
				// TODO relzs this assumption !!
				else if ($this->bymonthday!="") {
					echo "bymonthday".$this->bymonthday." <br/>";
					$days = explode(",",$this->bymonthday);


					$start = $dtstart;
					$end = $dtend;
					$countRepeats = 0;

					$currentMonthStart = mktime(0,0,0,$startMonth,1,$startYear);
					// do the current month first
					while  ($countRepeats < $this->count  && !$this->_afterUntil($currentMonthStart)) {
						list ($currentMonth,$currentYear) = explode(":",strftime("%m:%Y",$currentMonthStart));
						$currentMonthDays = date("t",$currentMonthStart);
						foreach ($days as $day) {
							if ($countRepeats >= $this->count || $this->_afterUntil($start)) return  $this->_repetitions;

							// Assume no negative bymonthday values
							// TODO relax this assumption

							// do not go over month end
							if ($day>$currentMonthDays) continue;

							$targetStart = mktime($startHour,$startMin,$startSecond,$currentMonth,$day,$currentYear);
							$targetEnd = $targetStart+$duration;
							if ($countRepeats >= $this->count) {
								return  $this->_repetitions;
							}
							if ($targetStart>=$dtstartMidnight && !$this->_afterUntil($targetStart)){
								// double check for byday constraints
								if ($this->byday!=""){
									if (!in_array(strftime("%w",$targetStart),$weekdays)){
										continue;
									}
								}
								$countRepeats+=$this->_makeRepeat($targetStart,$targetEnd);
							}
						}
						// now ago to the start of next month
						if ($currentYear+$this->rinterval>2036) return  $this->_repetitions;
						$currentMonthStart = mktime(0,0,0,$currentMonth,1,$currentYear+$this->rinterval);
					}

				}
				// annual repeats of the start date - TODO check this
				else if ($this->byday=="") {
					$start = $dtstart;
					$end = $dtend;
					$countRepeats = 0;
					while ($countRepeats < $this->count && !$this->_afterUntil($start)) {
						$countRepeats+=$this->_makeRepeat($start,$end);

						$currentYear = strftime("%Y",$start);
						list ($h,$min,$s,$d,$m,$y) = explode(":",strftime("%H:%M:%S:%d:%m:%Y",$start));
						if (($currentYear+$this->rinterval)>=2037) break;
						$start = strtotime("+".$this->rinterval." years",$start);
						$end = strtotime("+".$this->rinterval." years",$end);
					}
				}
				else {
					$days = explode(",",$this->byday);
					$start = $dtstart;
					$end = $dtend;
					$countRepeats = 0;

					// do the current month first
					while  ($countRepeats < $this->count  && !$this->_afterUntil($start)) {
						$currentMonth = strftime("%m",$start);
						foreach ($days as $day) {
							if ($countRepeats >= $this->count || $this->_afterUntil($start)) {
								return  $this->_repetitions;
							}

							$details=array();
							preg_match("/(\+|-?)(\d?)(.+)/",$day,$details);
							if (count($details)!=4) {
								echo "<br/><br/><b>PROBLEMS with $day</b><br/><br/>";
								return  $this->_repetitions;
							}
							else {
								list($temp,$plusminus,$weeknumber,$dayname) = $details;
								if (strlen($plusminus)==0) $plusminus="+";
								if (strlen($weeknumber)==0) $weeknumber=1;

								// always check for dtstart (nothing is allowed earlier)
								if ($plusminus=="-") {
									//echo "count back $weeknumber weeks on $dayname<br/>";
									list ($startDay,$startMonth,$startYear,$startWD) = explode(":",strftime("%d:%m:%Y:%w",$start));
									$startLast = date("t",$start);
									$monthEnd = mktime(0,0,0,$startMonth,$startLast,$startYear);
									$meWD = strftime("%w",$monthEnd );
									$adjustment = $startLast - (7+$meWD-$weekdayMap[$dayname])%7;

									$targetstartDay = $adjustment - ($weeknumber-1)*7;
									$targetendDay = $targetstartDay + $endDay-$startDay;
									list ($h,$min,$s,$d,$m,$y) = explode(":",strftime("%H:%M:%S:%d:%m:%Y",$start));

									$testStart = mktime($h,$min,$s,$m,$targetstartDay,$y);
									if ($currentMonth==strftime("%m",$testStart)){
										$targetStart = $testStart;
										$targetEnd = $targetStart + $duration;
										if ($countRepeats >= $this->count) {
											return  $this->_repetitions;
										}
										if ($targetStart>=$dtstartMidnight && !$this->_afterUntil($targetStart)){
											$countRepeats+=$this->_makeRepeat($targetStart,$targetEnd);
										}
									}
								}
								else {
									//echo "count forward $weeknumber weeks on $dayname<br/>";
									list ($startDay,$startMonth,$startYear,$startWD) = explode(":",strftime("%d:%m:%Y:%w",$start));
									$monthStart = mktime(0,0,0,$startMonth,1,$startYear);
									$msWD = strftime("%w",$monthStart );
									$adjustment = 1 + (7+$weekdayMap[$dayname]-$msWD)%7;

									$targetstartDay = $adjustment+($weeknumber-1)*7;
									$targetendDay = $targetstartDay + $endDay-$startDay;
									list ($h,$min,$s,$d,$m,$y) = explode(":",strftime("%H:%M:%S:%d:%m:%Y",$start));

									$testStart = mktime($h,$min,$s,$m,$targetstartDay,$y);
									if ($currentMonth==strftime("%m",$testStart)){
										$targetStart = $testStart;
										$targetEnd = $targetStart + $duration;
										if ($countRepeats >= $this->count) {
											return  $this->_repetitions;
										}
										if ($targetStart>=$dtstartMidnight && !$this->_afterUntil($targetStart)){
											$countRepeats+=$this->_makeRepeat($targetStart,$targetEnd);
										}
									}
								}
							}
						}
						// now ago to the start of the next month
						$start = $targetStart;
						$end = $targetEnd;
						list ($h,$min,$s,$d,$m,$y) = explode(":",strftime("%H:%M:%S:%d:%m:%Y",$start));
						if (($y+$this->rinterval+$m/12)>2036) return  $this->_repetitions;
						$start = mktime($h,$min,$s,$m,1,$y+$this->rinterval);
						$end = $start + $duration;
					}

				}
				return $this->_repetitions;
				break;
			case "MONTHLY":
				if ($this->bymonthday=="" && $this->byday==""){
					$this->bymonthday=$startDay;
				}
				if ($this->bymonthday!="") {
					echo "bymonthday".$this->bymonthday." <br/>";
					// if not byday then by monthday
					$days = explode(",",$this->bymonthday);

					// If I have byday and bymonthday then the two considions must be met
					$weekdays = array();
					if ($this->byday!=""){
						foreach (explode(",",$this->byday) as $bday) {
							$weekdays[]=$weekdayMap[$bday];
						}
					}

					$start = $dtstart;
					$end = $dtend;
					$countRepeats = 0;
					$currentMonthStart = mktime(0,0,0,$startMonth,1,$startYear);

					// do the current month first
					while  ($countRepeats < $this->count  && !$this->_afterUntil($currentMonthStart)) {
						echo $countRepeats ." ".$this->count." ".$currentMonthStart."<br/>";
						list ($currentMonth,$currentYear) = explode(":",strftime("%m:%Y",$currentMonthStart));
						$currentMonthDays = date("t",$currentMonthStart);
						foreach ($days as $day) {
							if ($countRepeats >= $this->count || $this->_afterUntil($start)) return  $this->_repetitions;

							$details=array();
							preg_match("/(\+|-?)(\d+)/",$day,$details);
							if (count($details)!=3) {
								echo "<br/><br/><b>PROBLEMS with $day</b><br/><br/>";
								return  $this->_repetitions;
							}
							else {
								list($temp,$plusminus,$daynumber) = $details;
								if (strlen($plusminus)==0) $plusminus="+";
								if (strlen($daynumber)==0) $daynumber=$startDay;

								// always check for dtstart (nothing is allowed earlier)
								if ($plusminus=="-") {
									// must not go before start of month etc.
									if ($daynumber>$currentMonthDays) continue;

									echo "I need to check negative bymonth days <br/>";
									$targetStart = mktime($startHour,$startMin,$startSecond,$currentMonth,$currentMonthDays+1-$daynumber,$currentYear);
									$targetEnd = $targetStart+$duration;
									if ($countRepeats >= $this->count) {
										return  $this->_repetitions;
									}
									if ($targetStart>=$dtstartMidnight && !$this->_afterUntil($targetStart)){
										$countRepeats+=$this->_makeRepeat($targetStart,$targetEnd);
									}
								}
								else {
									echo "$daynumber $currentMonthDays bd=".$this->byday." <br/>";
									// must not go over end month etc.
									if ($daynumber>$currentMonthDays) continue;
									echo "$startHour,$startMin,$startSecond,$currentMonth,$daynumber,$currentYear<br/>";
									$targetStart = mktime($startHour,$startMin,$startSecond,$currentMonth,$daynumber,$currentYear);
									$targetEnd = $targetStart+$duration;
									echo "$targetStart $targetEnd $dtstartMidnight<br/>";
									if ($countRepeats >= $this->count) {
										return  $this->_repetitions;
									}
									if ($targetStart>=$dtstartMidnight && !$this->_afterUntil($targetStart)){
										// double check for byday constraints
										if ($this->byday!=""){
											if (!in_array(strftime("%w",$targetStart),$weekdays)){
												continue;
											}
										}
										$countRepeats+=$this->_makeRepeat($targetStart,$targetEnd);
										echo "countrepeats = $countRepeats<br/>";
									}
								}
							}
						}
						// now ago to the start of next month
						if (($currentYear+($currentMonth+$this->rinterval)/12)>2036) return  $this->_repetitions;
						$currentMonthStart = mktime(0,0,0,$currentMonth+$this->rinterval,1,$currentYear);
					}

				}
				// This is byday
				else {
					$days = explode(",",$this->byday);
					// TODO I should also iterate over week number if this is used
					//$weeknumbers = explode(",",$this->byweekno);

					$start = $dtstart;
					$end = $dtend;
					$countRepeats = 0;
					$currentMonthStart = mktime(0,0,0,$startMonth,1,$startYear);

					// do the current month first
					while  ($countRepeats < $this->count  && !$this->_afterUntil($currentMonthStart)) {
						list ($currentMonth,$currentYear,$currentMonthStartWD) = explode(":",strftime("%m:%Y:%w",$currentMonthStart));
						$currentMonthDays = date("t",$currentMonthStart);
						$this->sortByDays($days,$currentMonthStart,$dtstart);

						foreach ($days as $day) {
							if ($countRepeats >= $this->count || $this->_afterUntil($start)) {
								return  $this->_repetitions;
							}

							$details=array();
							preg_match("/(\+|-?)(\d?)(.+)/",$day,$details);
							if (count($details)!=4) {
								echo "<br/><br/><b>PROBLEMS with $day</b><br/><br/>";
								return  $this->_repetitions;
							}
							else {
								list($temp,$plusminus,$weeknumber,$dayname) = $details;
								if (strlen($plusminus)==0) $plusminus="+";
								if (strlen($weeknumber)==0) $weeknumber=1;

								$multiplier = $plusminus=="+"?1:-1;
								// always check for dtstart (nothing is allowed earlier)
								if ($plusminus=="-") {
									//echo "count back $weeknumber weeks on $dayname<br/>";
									$startLast = date("t",$currentMonthStart);
									$currentMonthEndWD = ($startLast - 1 + $currentMonthStartWD)%7;

									$adjustment = $startLast - (7+$currentMonthEndWD-$weekdayMap[$dayname])%7;

									$targetstartDay = $adjustment - ($weeknumber-1)*7;
								}
								else {
									//echo "count forward $weeknumber weeks on $dayname<br/>";
									$adjustment = 1 + (7+$weekdayMap[$dayname]-$currentMonthStartWD)%7;

									$targetstartDay = $adjustment+($weeknumber-1)*7;

								}
								$targetStart = mktime($startHour,$startMin,$startSecond,$currentMonth,$targetstartDay,$currentYear);

								if ($currentMonth==strftime("%m",$targetStart)){
									$targetEnd = $targetStart + $duration;
									if ($countRepeats >= $this->count) {
										return  $this->_repetitions;
									}
									if ($targetStart>=$dtstartMidnight && !$this->_afterUntil($targetStart)){
										$countRepeats+=$this->_makeRepeat($targetStart,$targetEnd);
									}
								}

							}
						}
						// now go to the start of next month
						if (($currentYear+($currentMonth+$this->rinterval)/12)>2036) return  $this->_repetitions;
						$currentMonthStart = mktime(0,0,0,$currentMonth+$this->rinterval,1,$currentYear);
					}
				}
				return  $this->_repetitions;

				break;
			case "WEEKLY":
				$days = explode(",",$this->byday);
				$start = $dtstart;
				$end = $dtend;
				$countRepeats = 0;
				$currentWeekDay = strftime("%w",$start);
				// Go to the zero day of the first week (even if this is in the past)
				// this will be the base from which we count the weeks and weekdays
				$currentWeekStart = strtotime("-$currentWeekDay days",$start);

				while  ($countRepeats < $this->count  && !$this->_afterUntil($currentWeekStart)) {
					list ($currentDay,$currentMonth,$currentYear) = explode(":",strftime("%d:%m:%Y",$currentWeekStart));

					foreach ($days as $day) {
						if ($countRepeats >= $this->count || $this->_afterUntil($start)) {
							return  $this->_repetitions;
						}
						$details=array();
						preg_match("/(\+|-?)(\d?)(.+)/",$day,$details);
						if (count($details)!=4) {
							continue;
							echo "<br/><br/><b>PROBLEMS with $day</b><br/><br/>";
							return  $this->_repetitions;
						}
						else {
							list($temp,$plusminus,$daynumber,$dayname) = $details;
							if (strlen($plusminus)==0) $plusminus="+";
							// this is not relevant for weekly recurrence ?!?!?
							//if (strlen($daynumber)==0) $daynumber=1;
							$multiplier = $plusminus=="+"?1:-1;
							if ($plusminus=="-") {
								// TODO find out if I ever have this situation?
								// It would seem meaningless
							}
							else {
								//echo "count forward $daynumber days on $dayname<br/>";
								$targetstartDay = $currentDay+$weekdayMap[$dayname];
							}

							$targetStart = mktime($startHour,$startMin,$startSecond,$currentMonth,$targetstartDay,$currentYear);

							$targetEnd = $targetStart + $duration;
							if ($countRepeats >= $this->count) {
								return  $this->_repetitions;
							}
							if ($targetStart>=$dtstartMidnight && !$this->_afterUntil($targetStart)){
								$countRepeats+=$this->_makeRepeat($targetStart,$targetEnd);
							}

						}
					}

					// now go to the start of next week
					if ($currentYear+($currentMonth/12)>2036) return  $this->_repetitions;
					$currentWeekStart = strtotime("+".($this->rinterval)." weeks",$currentWeekStart);

				}
				return $this->_repetitions;
				break;
			case "DAILY":
				$start = $dtstart;
				$end = $dtend;
				$countRepeats = 0;

				$startYear = strftime("%Y",$start);
				while ($startYear<2027 && $countRepeats < $this->count && !$this->_afterUntil($start)) {
					$countRepeats+=$this->_makeRepeat($start,$end);
					$start = strtotime("+".$this->rinterval." days",$start);
					$end = strtotime("+".$this->rinterval." days",$end);
					$startYear = strftime("%Y",$start);
				}
				return $this->_repetitions;
				break;
			default:
				echo "UNKNOWN TYPE<br/>";
				return $this->_repetitions;
				break;
		}
	}

	function dumpData(){
		echo "Freq : $this->freq <br/>";
		echo "Interval : ".$this->data['INTERVAL']."<br/>";
		switch ($this->freq) {
			case "YEARLY":
				echo "By Month : ".$this->data['BYMONTH']."<br/>";
				break;
			case "MONTHLY":
				echo "By Day : ".$this->data['BYDAY']."<br/>";
				$days = explode(",",$this->data['BYDAY']);
				foreach ($days as $day) {
					$details=array();
					preg_match("/(\+|-?)(\d?)(.+)/",$day,$details);
					if (count($details)!=4) echo "<br/><br/><b>PROBLEMS with $day</b><br/><br/>";
					else {
						if (strlen($details[1])==0) $details[1]="+";
						echo "Event repeat details<br/>";
						if ($details[1]=="-") echo "count back $details[2] weeks on $details[3]<br/>";
						else echo "count forward $details[2] weeks on $details[3]<br/>";

						// Note if no number given then EVERY specified day!!
					}
				}
				break;
			case "WEEKLY":
				echo "By Day : ".$this->data['BYDAY']."<br/>";
				$days = explode(",",$this->data['BYDAY']);
				foreach ($days as $day) {
					$details=array();
					preg_match("/(\+|-?)(\d?)(.+)/",$day,$details);
					if (count($details)!=4) echo "<br/><br/><b>PROBLEMS with $day</b><br/><br/>";
					else {
						if (strlen($details[1])==0) $details[1]="+";
						echo "Event repeat details<br/>";
						if ($details[1]=="-") echo "count back $details[2] weeks on $details[3]<br/>";
						else echo "count forward $details[2] weeks on $details[3]<br/>";

						// Note if no number given then EVERY specified day!!
					}
				}
				break;
			default:
				echo "UNKNOWN TYPE<br/>";
				break;
		}

		// Doesnt yet deal with INTERVAL, UNTIL or COUNT
		print_r($this->data);
		echo "<hr/>";
	}

	function checkDate($test, $start, $end){
		if ($test>=$start && $test<=$end) return true;
		else return false;
	}

	function eventInPeriod($startDate,$endDate, $start, $end){
		// stupid verison to start that scans through EVERY single day!!!
		$checkDate = $startDate;
		while ($checkDate<=$endDate){
			if ($this->checkDate($checkDate,$start,$end)) return true;
			$checkDate+=24*60*60;
		}
		return false;
	}

	function isDuplicate(){
		$sql = "SELECT rr_id from #__jevents_rrule as rr WHERE rr.eventid = '".$this->eventid."'";
		$this->_db->setQuery($sql);
		$matches = $this->_db->loadObjectList();
		if (count($matches)>0 && isset($matches[0]->rr_id)) {
			return $matches[0]->rr_id;
		}
		return false;
	}
}
