<?php
class PushSubscriber extends AppModel {
	public $isHardcoded = true;
	public $useTable = 'push_subscribers';
	public $order = array('PushSubscriber.modified' => DESC);

	public $displayField = 'registration_key';

	# Platform types
	const TYPE_IOS = 'ios';
	const TYPE_ANDROID = 'android';

	function __construct($id = false, $table = null, $ds = null) {
		parent::__construct($id, $table, $ds);
		$this->displayField = 'registration_key';
	}

	/**
	 * Send push notification to all provided android keys.
	 *
	 * @param	apiKey
	 *		GCM API key to use.
	 * @param	registration_ids
	 *		Array of registration IDs of devices to send notification to.
	 * @param	message
	 *		Message to send. This can be a php array and it will be encoded to json automatically.
	 * @param collapseKey
	 *		Optional parameter, serves for grouping severall messages in one notiffication item.
	 * @param timeToLive
	 *		Optional parameter, represents time (in seconds) in which will GCM try do deliver message.
	 */
	public function sendNotificationsToAndroid($message, $registrationIds = null, $collapseKey = null, $timeToLive = null) {
		$results = array();

		# Get the apiKey
		$apiKey = Configure::read('Push.gcm_secret');
		if(!$apiKey) return;

		# Get the subscribers
		if($registrationIds === null) $registrationIds = $this->getSubscribers(self::TYPE_ANDROID);
		$registrationIds = array_values($registrationIds);
		if(!$registrationIds) return;

		# Build message
		$fields = array(
			'data' => array('message' => $message)
		);

		# Add collapse key
		if(!empty($collapseKey)) {
			$fields['data']['collapse_key'] = $collapseKey;
		}

		# Add time to live
		if(!empty($timeToLive)) {
			$fields['data']['time_to_live'] = $timeToLive;
		}

		# Define headers
		$headers = array(
			'Authorization: key=' . $apiKey,
			'Content-Type: application/json'
		);

		# Split into chunks
		$chunks = array_chunk($registrationIds, 990);
		foreach($chunks as $i => $chunk) {

			# Set the chunks
			$fields['registration_ids'] = $chunk;

			# Open connection
			$ch = curl_init();

			# Set the url, number of POST vars, POST data
			curl_setopt($ch, CURLOPT_URL, 'https://android.googleapis.com/gcm/send');

			curl_setopt($ch, CURLOPT_HEADER, true);
			curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

			# Disabling SSL Certificate support temporarly
			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

			# Set post fields
			curl_setopt($ch, CURLOPT_POST, true);
			curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($fields));

			# Execute post
			$response = curl_exec($ch);
			if ($response === false) {
				throw new Exception('Push notifications for android failed: ' . curl_error($ch));
			}

			# Make sure we received the HTTP 200
			$httpStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
			if($httpStatus != 200) {
				throw new Exception('Push notifications for android failed with HTTP code: ' . $httpStatus);
			}

			# Get the body
			$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
			$results[] = json_decode(substr($response, $headerSize), true);

			# Close connection
			curl_close($ch);
		}

		return $results;
	}


	/**
	 * Send push notification to all provided iOS tokens.
	 *
	 * @param	message
	 *		Message to send. This can be a php array, as it will be encoded to json later.
	 * @param	custom
	 *		Optional custom parameters.
	 * @param	tokens
	 *		Tokens of iOS devices to send notifications to.
	 * @param	sound
	 *		Optional sound for this notification.
	 * @param	badge
	 *		Optional badge for this notification.
	 */
	public function sendNotificationsToIOS($message, $custom = null, $tokens = null, $sound = null, $badge = null) {
		require_once VECTORCMS_ROOT . '/Vendor/ApnsPHP/Autoload.php';
		try {

			# Get the certificate
			$gateway = Configure::read('Push.apple_gateway');
			$passphrase = Configure::read('Push.apple_passphrase');
			if($cert = $gateway ? Configure::read('Push.apple_production_certificate') : Configure::read('Push.apple_dev_certificate')) {

			# Create pem file
			$pemFile = tempnam(sys_get_temp_dir(), 'cc_');
			file_put_contents($pemFile, preg_replace('~^\s*~ m', '', $cert));

			} else {
				return;
			}

			# Get tokens
			if($tokens === null) {
				$tokens = $this->getSubscribers(self::TYPE_IOS);
			}
			if(empty($tokens)) return;

			# Initialize push
			$push = new ApnsPHP_Push(
				($gateway ? ApnsPHP_Abstract::ENVIRONMENT_PRODUCTION : ApnsPHP_Abstract::ENVIRONMENT_SANDBOX),
				$pemFile
			);

			# Connect
			$push->connect();

			# Prepare messages
			foreach($tokens as $token) if($token) {
				try {

					# Initialize message with default sound
					$pushMessage = new ApnsPHP_Message($token);
					$pushMessage->setSound();

					# Set message
					$pushMessage->setText($message);

					# Set custom properties
					$custom = (array)$custom;
					foreach($custom as $key => $value) if(is_string($value)) {
						$pushMessage->setCustomProperty($key, $value);
					}

					# Set sound
					if(!empty($sound)) {
						$pushMessage->setSound($sound);
					}

					# Set badge
					if(!empty($badge)) {
						$pushMessage->setBadge((int)$badge);
					}

					# Add message to queue
					$push->add($pushMessage);

				} catch(Exception $e) {
					CakeLog::write('error', $e->getMessage());
					continue;
				}
			}

			# Send messages from queue
			$push->send();

			# Disconnect from push
			$push->disconnect();

			# Get errors
			$aErrorQueue = $push->getErrors();
			if (!empty($aErrorQueue)) {
				CakeLog::write('error', print_r($aErrorQueue, true));
			}

			# Remove .pem file
			@unlink($pemFile);

		} catch(Exception $e) {
			@unlink($pemFile);
			throw $e;
		}
	}

	/**
	 * Get all of the active subscribers.
	 *
	 * @param	type
	 *		Use either TYPE_IOS or TYPE_ANDROID.
	 * @param	conditions
	 *		Additional conditions for the query.
	 *
	 * @return
	 *		The list of all subscribers.
	 */
	public function getSubscribers($type, $conditions = array()) {
		$list = array();

		# Validate type
		if(!in_array($type, array(self::TYPE_IOS, self::TYPE_ANDROID))) {
			throw new Exception('Unknown subscriber type: ' . $type);
		}

		# Get subscribers from default table
		$subscribers = $this->find('all', array(
			'conditions' => array_merge($conditions, array(
				'PushSubscriber.type =' => $type,
				'PushSubscriber.is_active' => 1))
		));

		# Build sending lists
		foreach($subscribers as $subscriber) {
			$list[] = $subscriber['PushSubscriber']['registration_key'];
		}

		return $list;
	}

# ~ Get the previous id	 - - - - - - - - - - - - - - - - - - - - - - - - - - - #
	public function getPreviousId($data) {
		return $this->field('id', array(
			'registration_key' => $data['registration_key']
		));
	}

# ~ Clean up the previous entry	 - - - - - - - - - - - - - - - - - - - - - - - #
	public function beforeSave() {

		# Skip everything if data exists in database
		if(empty($this->data[$this->alias]['id'])) {

			# Get previous id
			$existing = $this->find('first', array(
				'fields' => array(
					'id', 'created'),
				'conditions' => array(
					'registration_key' => $this->data[$this->alias]['registration_key'])
			));

			# If there is a previous entry
			if($existing) {
				$data = $this->data;

				# Remove previous
				$this->delete($existing[$this->alias]['id']);

				# Mark to update
				$this->data = $data;
				$this->data[$this->alias]['id'] = $existing[$this->alias]['id'];
				$this->data[$this->alias]['created'] = $existing[$this->alias]['created'];
			}
		}

		return parent::beforeSave();
	}

}
