<?php
namespace ALPCAL\Draw;

/**
 * Main class to create the web calendar.
 
 Requires:
 	setYearMonth($Y,$M); // needs to run to set the variables to retrieve
 	getData(); // retrieves data and puts in in object
 */

use DateTime, DateInterval, Exception;
use ALPCAL\Util\CacheFileRenamer;
use ALPCAL\Storage\CacheFiles;

Class Calendar extends \ALPCAL\Defaults
{
	// DateTime items
		// main working vars
		public $todayLiteral;
		public $workingMonth;
		public $workingMonth_Prev;
		public $workingMonth_Next;
		public $allEvents = array();
	
		// draw range for the visual calendar (first visual, last visual takign into consideration next and last month)
		public $drawRangeStart;
		public $drawRangeEnd;
	// DateTime items
	
	// sequence for days of week to draw and workers
	public $daysOfWeekSequence = array();
	public $goBackDaysSequence = array();
	
	public $events = null; // contains the events in order to show them
	public $drawEvents = array(); //  events in format required to draw this calendar
	
	// error handler
	public $error = false;
	
	
	function __construct($alpcal_options)
	{
		parent::__construct($alpcal_options); // put the construct items in place
		$this->setWeekdaysArray(); // sets the array required for the first day of the week

  	}

	/**
	 * called to initiate the fetching of data
	 */
	public function getData()
	{
		$this->setDrawRange(); // sets variables for draw range (visible calendar start and visible calendar end)
		// check if cached version falls within time limits
		
		$this->cacheFiles = new CacheFiles($this->alpcal_options['cache_folder']); // create an object for dealing with file storage
		$this->cacheFiles->setFileName(
						$this->drawRangeStart->format('Y_m_d')
						.' to '.
						$this->drawRangeEnd->format('Y_m_d')
										);
		$this->cacheFiles->setFileFolder("Months");
		$this->cacheFiles->getFile();
		
		// if the file is older than allowed in cache, go fetch it
		if ($this->cacheFiles->fileAge > $this->alpcal_options['cache_length_months'])
			{
			echo "<!-- FETCHING NEW DATA FROM REMOTE SOURCE(S) -->";
			try {
				$this->fetchEventTypes();
				$this->fetchRemoteCalendarData(); // go get the data for the draw range
				$this->setDrawEvents(); // sets the assoc array DrawEvents which puts calendar items into usable format for drawing
				$this->cacheFiles->writeFile($this);
				}
			catch(Exception $e)
				{
				$this->setEventsFromCache();
				}
			}
		else{
			$this->setEventsFromCache();
			}

	}
	
	public function fetchEventTypes()
	{
		$et = new \ALPCAL\Remote\Categories($this->alpcal_options);
		$et->set_stDt($this->drawRangeStart);
		$et->set_enDt($this->drawRangeEnd);
		$this->EventTypes = $et->returnEventTypes();

	}
	
	public function setEventsFromCache()
	{
		$decoded = json_decode($this->cacheFiles->returnFileLiteral(),true);
		$decodedObj = json_decode($this->cacheFiles->returnFileLiteral());
//		$this->events = $decoded['events'];
		$this->allEvents = $decoded['allEvents'];
		$this->drawEvents = $decoded['drawEvents'];
		$this->EventTypes = $decodedObj->EventTypes;
	}
	
	/**
	 * puts calendar items into usable format for this system
	 * OUTPUT: $this->drawEvents as assoc array
	 */ 
	public function setDrawEvents()
	{
		$events = array(); // working var to hold output
		$dateEventBuilder = clone $this->drawRangeStart;
		$deb = 1;
		while($dateEventBuilder<=$this->drawRangeEnd)
			{
			$events[$deb][0] = $dateEventBuilder->format('Y-m-d');
			// events is the variable we are using to store the events in.
			//The index of the calendar, starting at 1 denotes the index on the calendar, not the day of the month.
			//  let's capture and display events from the previous month that leaked over
			if($deb == 1)
				{
				foreach($this->allEvents as $preMonthEvent)
					{
					$preMonthEvent = json_decode(json_encode($preMonthEvent), true); // convert stdclass to assoc array
					if ( strtotime($preMonthEvent['StDT']) < strtotime($events[1][0]) )
						{
						// first day of calendar = $events[1][0] 	// USED TO HVE THIS WRONG @ $events[$deb][0]
						$eLen=1+date_diff(date_create($preMonthEvent['EnDT']),date_create($events[1][0]))->format("%a");
						for($pos = 1; $pos<20; $pos++)
							{
							if(!$events[1][$pos])
								{
								$events[1][$pos] = $preMonthEvent;
								$events[1][$pos]["len"] = $eLen;
								for($dayOfEvent = 1; $dayOfEvent<$eLen; $dayOfEvent++)
									{
									$events[1+$dayOfEvent][$pos] = $preMonthEvent;
									$events[1+$dayOfEvent][$pos]["lenLeft"] = $eLen-$dayOfEvent;
									}
								break;
								}
							}
						}
					else{
						break(1);
						}
					}
				// /let's capture and display events from the previous month that leaked over
				} // if($deb == 1)

			foreach($this->allEvents as $event)
				{
				$event = json_decode(json_encode($event), true); // convert stdclass to assoc array
				$diff=date_diff(($dateEventBuilder),date_create($event['StDT']));
		//		echo $diff->format("%a")."~<Br/>";
				if ($diff->format("%a")==0)
					{
					$eLen=1+date_diff(date_create($event['EnDT']),date_create($event['StDT']))->format("%a");

					for($pos = 1; $pos<20; $pos++)
						{
						if(!$events[$deb][$pos])
							{
							$events[$deb][$pos] = $event;
							$events[$deb][$pos]["len"] = $eLen;
							// todo: get this to extend the length of the event through the additional variables. $eLen is the var to use
							for($dayOfEvent = 1; $dayOfEvent<$eLen; $dayOfEvent++)
								{
								$events[$deb+$dayOfEvent][$pos] = $event;
								$events[$deb+$dayOfEvent][$pos]["lenLeft"] = $eLen-$dayOfEvent;
								}
							break;
							}
						}

					}
				}
			date_add($dateEventBuilder,date_interval_create_from_date_string("1 days"));
			$deb++;
			}

		$this->drawEvents = $events;
	}
	
//	/**
//	 * Returns alpcal_options to children classes
//	 */
//	public function get_alpcal_options()
//	{
//		return $this->alpcal_options;
//	}
	
	/**
	 * 
	 */
	public function fetchRemoteCalendarData()
	{	
		$calOrd = 0;
		// initialize each of the classes for fetchers that exist
		foreach ($this->alpcal_options['calendars_additional'] as $v)
		{
			$newClass = 'ALPCAL\\Remote\\' . $v;
			if (method_exists($newClass,returnEvents))
			{
				$this->calendarData[$v] = new $newClass($this->alpcal_options);
				$this->calendarData[$v]->set_stDt($this->drawRangeStart);
				$this->calendarData[$v]->set_enDt($this->drawRangeEnd);
				$this->calendarData[$v]->theseEvents = $this->calendarData[$v]->returnEvents();
			}
			else
			{
				throw new Exception("Remote class of \"{$v}\" not found by fetchRemoteCalendarData in Draw\\Calendar.");
			}
			$arrayOrd = 0;
			foreach ($this->calendarData[$v]->theseEvents as $key=>$val)
			{
				$this->calendarData[$v]->theseEvents[$key]->calPlugin['calOrd'] = $calOrd;
				$this->calendarData[$v]->theseEvents[$key]->calPlugin['ord'] = $arrayOrd;
				$this->calendarData[$v]->theseEvents[$key]->calPlugin['whichCal'] = $v;
				if ( $v != 'AlpineCalendar_ca' ) { 
					$this->calendarData[$v]->theseEvents[$key]->EventType_Primary = $this->alpcal_options['calendars_additional_categ']; 
				} 
				$arrayOrd++;
			}
			$calOrd++;
			$this->allEvents = (array) array_merge($this->allEvents,$this->calendarData[$v]->theseEvents);
		}
		
		usort($this->allEvents,array($this, "usort_allEvents"));
		
//		echo "<pre>";
//		print_r($this->allEvents);
//		die("</pre>");
	
	}
	
	/**
	 * sorting the allEvents 
	 *	(StDT, EnDT, ord, calOrd)
	 */
	public function usort_allEvents($a, $b)
	{
		if ( $a->StDT < $b->StDT ) return -1;
		if ( $a->StDT > $b->StDT ) return 1;
		// equality of start dates
		if ( $a->EnDT < $b->EnDT ) return -1;
		if ( $a->EnDT > $b->EnDT ) return 1;
		// equality of end dates
		if ( $a->calPlugin['calOrd'] < $b->calPlugin['calOrd'] ) return -1;
		if ( $a->calPlugin['calOrd'] > $b->calPlugin['calOrd'] ) return 1;
		// equality of which calendar
		if ( $a->calPlugin['ord'] < $b->calPlugin['ord'] ) return -1;
		if ( $a->calPlugin['ord'] > $b->calPlugin['ord'] ) return 1;
		// equality of which calendar
		return 0; // equality
	}
	
	
	/**
	 *  Determine and set the first and last visual day for the calendar that's being drawn
	 */
	public function setDrawRange()
	{
		$this->drawRangeStart = clone $this->workingMonth;
		foreach ($this->goBackDaysSequence as $k=>$v)
			{
			if ($this->drawRangeStart->format('w')==$k) { $goBackDays = $v; }
			}
		if($goBackDays) {	date_add($this->drawRangeStart,date_interval_create_from_date_string("-".$goBackDays." days"));	}
		$this->drawRangeEnd = clone $this->drawRangeStart;
		while ($this->drawRangeEnd < $this->workingMonth_Next)
			{
			date_add($this->drawRangeEnd,date_interval_create_from_date_string("7 days"));
			}
		date_add($this->drawRangeEnd,date_interval_create_from_date_string("-1 days"));
	}
	
	/**
	 * Sets up the array to go through weekdays based on which day of week to show first
	 */
	public function setWeekdaysArray()
	{
		switch ($this->alpcal_options['first_day'])
		{
			case 'MON':
				$this->daysOfWeekSequence = array(1,2,3,4,5,6,0);
				$this->goBackDaysSequence = array(6,0,1,2,3,4,5);
				break;
			case 'SUN':
				$this->daysOfWeekSequence = array(0,1,2,3,4,5,6);
				$this->goBackDaysSequence = array(0,1,2,3,4,5,6);
				break;
			default:
				$this->error = 'First day of week has not been set in administration.';
				throw new Exception($this->error);
		}

	}
	
	/**
	 * Initiate variables for current, previous and next month which are used throughout
	 */
	public function setYearMonth($Y,$M)
	{
		$this->todayLiteral = new DateTime();
		$thisD = clone $this->todayLiteral;
		$setY = ($Y && $Y>2000) ? $Y : $thisD->format('Y');
		$setM = ($M && $M<13 && $M>0) ? $M : $thisD->format('m');
		// make sure that the setY and setM are valid
		try	{
			$testD = new DateTime($setY.'/'.$setM.'/15');
			}
		catch (Exception $e)
			{
			$setY = $thisD->format('Y');
			$setM = $thisD->format('m');
			}
		$this->workingMonth = new DateTime($setY.'/'.$setM.'/1');
		$this->workingMonth_Next = clone $this->workingMonth;
		$this->workingMonth_Next->add(new DateInterval('P1M'));
		$this->workingMonth_Prev = clone $this->workingMonth;
		$this->workingMonth_Prev->sub(new DateInterval('P1M'));
	}
	
	
	public function showNS()
	{
		echo 'NAMESPACE = ' . __NAMESPACE__ . "\n<hr/>";
	}

}