<?php
/**
 * Created by JetBrains PhpStorm.
 * User: atmail
 * Date: 7/06/12
 * Time: 1:08 PM
 * To change this template use File | Settings | File Templates.
 */

class Atmail_Dav_Optimizer
{
	const OPTIMIZER_VERSION = '0.1';

	const LOG_STATUS = -1;
	const LOG_ERROR = 0;
	const LOG_INFO = 1;
	const LOG_DEBUG = 2;

	public $logLevel = self::LOG_ERROR;
	/**
	 * When set to true, nothing is changed, only simulated
	 */
	public $dryRun = false;
	protected $output;
	protected $accounts = array();
	protected $calendars = array();
	protected $delegates;
	protected $xattrBin;
	protected $ctag = 1;
	protected $total = 0;
	protected $completed = 0;
	protected $startTime = 0;
	protected $errors = array();
	/**
	 * Sets up the migrator
	 */
	public function __construct()
	{
		error_reporting(E_ALL);
		$this->output = fopen('php://stderr', 'w');
		$this->db = Zend_Registry::get('dbAdapter');
	}

	public function start()
	{
		$this->findTotal();
		$this->completed = 0;
		$this->startTime = time();
		$this->optimize();
		if(count($this->errors) > 0)
		{
			$errmsg = "\n\nThe following errors were encounted during the migration :\n";
			foreach($this->errors as $k => $v)
			{
				$errmsg .= $k . $v . "\n";
			}
			$this->error($errmsg);
		}
	}

	protected function findTotal()
	{
		// total entries
		$this->db->getConnection();
		$result = $this->db->query("SELECT calendarid FROM calendarObjects", array())->fetchAll();
		$this->db->closeConnection();
		$this->total = is_array($result) ? count($result) : 0;
		return $this->total;
	}

	public static function optimizeDates($vObject)
	{
		
		require_once("library/DateTime.php");
		$start = false;
		$end = false;
		$rrule = 0;
		foreach($vObject->children as $vObjectChild)
		{
			if(get_class($vObjectChild) == 'Sabre_VObject_Component_VEvent' ||
				(get_class($vObjectChild) == 'Sabre_VObject_Component' && $vObjectChild->name == "VEVENT"))
			{
				// found an event for this object
				// lets grab the start date
				foreach($vObjectChild->children as $vObjectChildProperty)
				{
					if($vObjectChildProperty->name == "RRULE")
					{
						$rrule = 1;
					}
					if(		(get_class($vObjectChildProperty) == 'Sabre_VObject_Property_DateTime' || get_class($vObjectChildProperty) == 'Sabre_VObject_Element_DateTime')
						&&	 	($vObjectChildProperty->name == 'DTEND' || $vObjectChildProperty->name == 'DTSTART'))
					{
						
						if( !method_exists('DateTime','getTimestamp') )
							$dateStamp = Sabre_VObject_DateTimeParser::parse($vObjectChildProperty->value)->format('U');
						else
							$dateStamp = Sabre_VObject_DateTimeParser::parse($vObjectChildProperty->value)->getTimestamp();
						
						switch($vObjectChildProperty->name)
						{
							case 'DTEND':
								{
								if($end === false || $dateStamp > $end)
								{
									$end = $dateStamp;
								}
								} break;
							case 'DTSTART':
								{
								if($start === false || $dateStamp < $start)
								{
									$start = $dateStamp;
								}
								} break;
						}
					}
				}
			}
		}
		if($start === false)
		{
			$start = 0;
		}
		if($end === false)
		{
			$end = 4294967295;
		}

		return array($start, $end, $rrule);
	}

	protected function optimize()
	{
		$this->db->getConnection();

		$calendarentries = $this->db->query("SELECT id,calendardata FROM calendarObjects", array())->fetchAll();

		// Making sure we're not creating any accounts twice.
		foreach ($calendarentries as $calendarentry)
		{
			try
			{
				$vObject = Sabre_VObject_Reader::read($calendarentry['calendardata']);
			}
			catch(Exception $e)
			{
				file_put_contents("php://stderr", $e->getMessage() . "\n");
				file_put_contents("php://stderr", "Error while processing calendar entry id " . $calendarentry['id'] . ". Skipping.\n");
				continue;
			}
			list($start, $end, $rrule) = self::optimizeDates($vObject);
			if(!$this->dryRun)
			{
				$this->db->query(
					'update calendarObjects set `start` = ?, `end` = ?, `rrule` = ? where id = ?', array($start, $end, $rrule, $calendarentry['id'])
				);
			}
			$this->completed++;
			$this->status();
		}
		$this->db->closeConnection();
	}

	public function log($level, $message)
	{
		if (($level > $this->logLevel) || ($this->logLevel != self::LOG_ERROR && $level == self::LOG_STATUS))
		{
			return;
		}
		ob_start();

		switch ($level)
		{
			case self::LOG_STATUS :
				break;
			case self::LOG_ERROR :
				echo "\x1B[31m";
				break;
			default:
				$message .= "\n";
				echo date('[H:i]') . ' ';
				break;
		}

		switch (gettype($message))
		{
			case 'string' :
				echo $message;
				break;
			default :
				var_dump($message);
				break;
		}
		echo "\x1B[0m";
		$buffer = ob_get_clean();
		fwrite($this->output, $buffer);
	}

	public function debug($message)
	{
		$this->log(self::LOG_DEBUG, $message . "\n");
	}

	public function info($message)
	{
		$this->log(self::LOG_INFO, $message ."\n");
	}

	public function error($message)
	{
		$this->log(self::LOG_ERROR, $message . "\n");
	}

	public function status($message = '')
	{
		if($message == '')
		{
			$message = "\r" . $this->completed . "/" . $this->total . " in " . (time() - $this->startTime) . " seconds" ;
		}
		$this->log(self::LOG_STATUS, $message);
	}

}
