<?php
/**
* @package RSform!Pro
* @copyright (C) www.rsjoomla.com
* @license GPL, http://www.gnu.org/copyleft/gpl.html
*/

defined('_JEXEC') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Date\Date;

class RSFormGoogleCalendar
{
	/*
	*	Google client ID
	*/
	protected $_clientID;
	
	/*
	*	Google secret
	*/
	protected $_secret;
	
	/* 
	*	stores the access token
	*/
	protected $access_token;
	
	/*
	*	Constructor
	*/
	
	protected $client;
	
	public function __construct() {
		require_once __DIR__ . '/Google/autoload.php';

		$this->getCredentials();
		
		$this->client = new Google_Client();
		$this->access_token = $this->getAccessToken();
		if ($this->access_token) {
			$this->client->setAccessToken($this->access_token);
		}
		if ($this->isInvalidToken()) {
			$this->refreshToken();
			$this->access_token = $this->getAccessToken();
		}

		if (!class_exists('RSFormProHelper') && file_exists(JPATH_ADMINISTRATOR . '/components/com_rsform/helpers/rsform.php'))
		{
			require_once JPATH_ADMINISTRATOR . '/components/com_rsform/helpers/rsform.php';
		}
	}
	
	public static function getInstance() {
		static $instance = null;

		if ($instance === null)
		{
			$instance = new RSFormGoogleCalendar();
		}

		return $instance;
	}
	
	protected function getCredentials() {
		$this->_clientID  = RSFormProHelper::getConfig('google.calendar.clientid');
		$this->_secret = RSFormProHelper::getConfig('google.calendar.secret');
	}
	
	/*
	*	Get auth URL
	*/
	public function getAuthURL($redirectUri) {
		$this->client->setClientId($this->_clientID);
		$this->client->setClientSecret($this->_secret);
		$this->client->setRedirectUri($redirectUri);
		$this->client->addScope(array(Google_Service_Calendar::CALENDAR));
		$this->client->setAccessType('offline');
		$this->client->setApprovalPrompt('force');

		return $this->client->createAuthUrl();
	}
	
	public function getAccessToken()
	{
		if ($accesstoken = RSFormProHelper::getConfig('google.calendar.accesstoken'))
		{
			return $accesstoken;
		}

		return false;
	}
	
	public function revokeToken() {
		$status = false;
		if ($this->access_token) {
			try {
				$this->client->setClientId($this->_clientID);
				$this->client->setClientSecret($this->_secret);
				$revoked = $this->client->revokeToken();

				if ($revoked) {
					$this->storeAccessToken('');
					$this->access_token = false;
					$status = true;
				}
			} catch (Exception $e) {
				if (Factory::getApplication()->isClient('administrator')) {
					Factory::getApplication()->enqueueMessage(Text::sprintf('RSFP_GOOGLE_CALENDAR_ERROR_NO_CALENDAR', $e->getMessage()), 'error');
				}
			}
		}
		
		return $status;
	}
	
	
	protected function refreshToken() {
		$refreshToken = $this->client->getRefreshToken();
		if (!is_null($refreshToken)) {
			try {
				$this->client->setClientId($this->_clientID);
				$this->client->setClientSecret($this->_secret);
				$this->client->refreshToken($refreshToken);

				$newAccessToken = $this->client->getAccessToken();
				$this->storeAccessToken($newAccessToken);
			} catch(Exception $e) {
				if (Factory::getApplication()->isClient('administrator')) {
					Factory::getApplication()->enqueueMessage(Text::sprintf('RSFP_GOOGLE_CALENDAR_ERROR_NO_CALENDAR', $e->getMessage()), 'error');
				}
			}
		}
	}
	
	public function storeAccessToken($accessToken) {
		RSFormProConfig::getInstance()->set('google.calendar.accesstoken', $accessToken);
	}
	
	public function isInvalidToken() {
		if ($this->access_token) {
			$invalid = $this->client->isAccessTokenExpired();
			return $invalid;
		}
		
		return true;
	}
	 
	public function retrieveAccessToken($code, $redirectUri) {
		$this->client->setClientId($this->_clientID);
		$this->client->setClientSecret($this->_secret);
		$this->client->addScope(array(Google_Service_Calendar::CALENDAR));
		$this->client->setRedirectUri($redirectUri);
		$this->client->setAccessType("offline");
		$this->client->authenticate($code);
		
		return $this->client->getAccessToken();
	}
	
	public static function getService($client) {
		static $service;
		if (is_null($service)) {
			$service = new Google_Service_Calendar($client);
		}
		
		return $service;
	}

	public function getCalendarsIds($form_id = null) {
		// return only if the token is still valid
		if (!$this->isInvalidToken()) {
			try {
				$service = self::getService($this->client);

				$response = $service->calendarList->listCalendarList(array('minAccessRole' => 'writer')); // only the calendars that we can read and write
				$calendars = $response->getItems();

				$values = array();
				foreach ($calendars as $calendar) {
					$values[($calendar->primary ? 'primary' : $calendar->id)] = new stdClass();
					$values[($calendar->primary ? 'primary' : $calendar->id)]->title = RSFormProHelper::htmlEscape($calendar->summary).($calendar->primary ? ' (primary)' : '');
					$values[($calendar->primary ? 'primary' : $calendar->id)]->timezone = $calendar->timeZone;
				}

				return empty($values) ? false : $values;
			} catch (Exception $e) {
				if (!is_null($form_id)) {
					$dataLog = new stdClass();
					$dataLog->form_id = $form_id;
					$dataLog->message = $e->getMessage();
					$this->storeErrorLog($dataLog);
				}
			}
		}

		return false;
	}


	public function addEvent($calendar_id, $data = array(), $use_recurrence = 0)
	{
		$return = new stdClass();

		try
		{
			// check to see if any data is passed
			if (empty($data))
			{
				throw new Exception(Text::_('RSFP_GOOGLE_CALENDAR_ERROR_NO_DATA_VALUES_PASSED_TO_THE_CALENDAR'));
			}

			// check that the spreadsheet id is not empty
			if (empty($calendar_id))
			{
				throw new Exception(Text::_('RSFP_GOOGLE_CALENDAR_ERROR_NO_CALENDAR_ID_SPECIFIED'));
			}

			// check if the token is still valid
			if ($this->isInvalidToken())
			{
				throw new Exception(Text::_('RSFP_GOOGLE_CALENDAR_ERROR_INVALID_ACCESS_TOKEN'));
			}

			$service 	= self::getService($this->client);

			// clean some of the values
			$clean_values = array('summary', 'location', 'description', 'attendees');
			foreach ($data as $colname => $value)
			{
				if (!in_array($colname, $clean_values)) {
					continue;
				}

				$value = str_replace("\r\n", "\n", $value);
				// for textareas
				$value = str_replace(array("\n <br />", "\n<br />", "<br />\n", "<br /> \n", "<br>\n", "<br> \n"), "\n", $value);
				// for file uploads
				$value = str_replace('<br />', "\n", $value);
				$data[$colname] = $value;
			}

			$event_options = array(
				'summary' => $data['summary'],
				'location' => $data['location'],
				'description' => $data['description']
			);

			if (empty($data['allday'])) {
				// handle the dates and the calendar timezone to match the selection
				$start = Date::getInstance($data['start']);
				$end = Date::getInstance($data['end']);

				// fail-safe if no timeZone are saved
				if (empty($data['timeZone'])) {
					$data['timeZone'] = Factory::getConfig()->get('offset');
				}


				$timezone_start = new DateTimeZone($data['timeZone']);
				$timezone_end = new DateTimeZone($data['timeZone']);

				$offset_start = $timezone_start->getOffset($start);
				$offset_end = $timezone_end->getOffset($end);

				if ($offset_start !== 0) {
					// reverse offset so that the hours match in the calendar with the one selected
					$offset_start = -$offset_start;
					$offset_start = ($offset_start > 0 ? '+' . $offset_start : $offset_start) . ' second';
					$start->modify($offset_start);
				}

				if ($offset_end !== 0) {
					$offset_end = -$offset_end;
					$offset_end = ($offset_end > 0 ? '+' . $offset_end : $offset_end) . ' second';
					$end->modify($offset_end);
				}

				$start->setTimezone($timezone_start);
				$end->setTimezone($timezone_end);

				$start_timezone = $start->format(\DateTime::RFC3339, true, false);
				$end_timezone = $end->format(\DateTime::RFC3339, true, false);

				$event_options['start'] = array('dateTime' => $start_timezone, 'timeZone' => $data['timeZone']);
				$event_options['end'] = array('dateTime' => $end_timezone, 'timeZone' => $data['timeZone']);
			} else {
				$start = Date::getInstance($data['start']);
				$end = Date::getInstance($data['end']);

				$start_allday = $start->format('Y-m-d', true, false);
				$end_allday = $end->format('Y-m-d', true, false);

				$event_options['start'] = array('date' => $start_allday);
				$event_options['end'] = array('date' => $end_allday);
			}

			// remove unwanted spaces form the attendees
			$data['attendees'] = trim($data['attendees']);

			if (!empty($data['attendees'])) {
				$attendees = str_replace(',', "\n", $data['attendees']);
				$attendees = explode("\n", $attendees);

				// remove any spaces from each email
				array_walk($attendees, 'trim');

				$event_options['attendees'] = array();

				foreach ($attendees as $attendee) {
					$event_options['attendees'][] = array('email' => $attendee);
				}
			}

			// handle recurrence
			if ($use_recurrence) {
				$data['frequency'] = trim($data['frequency']);
				$data['count'] = trim($data['count']);

				if (!empty($data['frequency'])) {
					// build the rule
					$rules = array('RRULE:FREQ='.$data['frequency']);

					if (!empty($data['count'])) {
						$rules[] = 'COUNT='.$data['count'];
					}

					$rule = implode(';', $rules);

					$event_options['recurrence'] = array($rule);
				}
			}


			$event = new Google_Service_Calendar_Event($event_options);
			$service->events->insert($calendar_id, $event);

			// All done
			$return->status = true;
		}
		catch (Exception $e)
		{
			$return->status = false;
			$return->msg 	= $e->getMessage();
			$return->code 	= $e->getCode();
		}

		return $return;
	}

	
	public function storeErrorLog($dataLog)
	{
		$dataLog->date = Factory::getDate()->toSql();

		if (!isset($dataLog->code))
		{
			$dataLog->code = '';
		}

		Factory::getDbo()->insertObject('#__rsform_google_calendar_error_log', $dataLog);

		$this->deleteOldErrorLog($dataLog->form_id);
	}

	protected function deleteOldErrorLog($form_id) {
		$db 	= Factory::getDbo();
		$query 	= $db->getQuery(true);

		// get the l5'th one
		$query->select('*')
			->from($db->qn('#__rsform_google_calendar_error_log'))
			->where($db->qn('form_id').' = '.$db->q($form_id))
			->order($db->qn('date').' DESC');
		$db->setQuery($query, 15, 1);

		if ($resource = $db->loadAssoc()) {
			$query = $db->getQuery(true);
			$query->delete($db->qn('#__rsform_google_calendar_error_log'))
				->where($db->qn('date').' < '.$db->q($resource['date']))
				->where($db->qn('form_id').' = '.$db->q($form_id));
			$db->setQuery($query);
			$db->execute();
		}

	}
}