<?php
namespace JExtstore\Component\JChat\Site\Model;
/** 
 * @package JCHAT::BLACKBOARD::components::com_jchat
 * @subpackage models
 * @author Joomla! Extensions Store
 * @Copyright (C) 2015 - Joomla! Extensions Store
 * @license GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html   
 */
defined( '_JEXEC' ) or die( 'Restricted access' );
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Factory;
use JExtstore\Component\JChat\Administrator\Framework\Model as JChatModel;
use JExtstore\Component\JChat\Administrator\Framework\Exception as JChatException;
use JExtstore\Component\JChat\Administrator\Framework\Helpers\Users as JChatHelpersUsers;

/**
 * Model for the blackboard signaling channel
 * 
 * @package JCHAT::BLACKBOARD::components::com_jchat
 * @subpackage models
 * @since 2.23
 */ 
class BlackboardModel extends JChatModel {
	/**
	 * This peer1 session ID
	 * @access private
	 * @var int
	 */
	private $peer1;
	
	/**
	 * Client response
	 * @access private
	 * @var array
	 */
	private $response;
	
	/**
	 * 
	 * Store session for WebRTC blackboard
	 * 
	 * @param string $otherPeer
	 * @param string $sdp
	 * @param string $iceCandidate
	 * @param boolean $isCaller
	 * @access public
	 * @return boolean
	 */
	public function storeEntity($otherPeer = null, $sdp = null, $iceCandidate = null, $isCaller = 0) {
		// Check if correct informations are feed to this server model
		if(!$otherPeer) {
			$this->response['storing'] = array('status'=>false, 'exception_message'=>Text::_('COM_JCHAT_NOPEER_SPECIFIED'));
			return $this->response;
		}
		// Missing informations sent by client, we can't go on
		if(!$sdp && !$iceCandidate) {
			$this->response['storing'] = array('status'=>false, 'exception_message'=>Text::_('COM_JCHAT_NODATA_SPECIFIED'));
			return $this->response;
		}
		
		// Check if callee has permissions to use videochat
		$aclParamValue = $this->componentParams->get('allow_blackboard', array(0));
		// Ensure that the ACL parameter has no 'All groups' option selected
		if(is_array($aclParamValue) && !in_array(0, $aclParamValue, false)) {
			// Fetch current user id using session id of the remote callee peer
			$query = "SELECT " . 
					 $this->dbInstance->quoteName('userid') .
				  	 "\n FROM " . 
				  	 $this->dbInstance->quoteName('#__session') .
				  	 "\n WHERE " . 
					 $this->dbInstance->quoteName('session_id') . " = " . $this->dbInstance->quote($otherPeer);
			$otherPeerUserID = $this->dbInstance->setQuery($query)->loadResult();
			$otherPeerUser = Factory::getContainer()->get(\Joomla\CMS\User\UserFactoryInterface::class)->loadUserById($otherPeerUserID); 
			
			// If we are calling a super user skip the ACL permissions check, always allowed
			if(!$otherPeerUser->authorise('core.admin')) {
				$totalParamGroups = $aclParamValue;
				// Cycle on all the group selected and retrieve the child groups
				foreach ($aclParamValue as $aclParamGroup) {
					$totalParamGroups = array_merge($totalParamGroups, JChatHelpersUsers::getChildGroups($this->dbInstance, $aclParamGroup));
				}
				// Remove duplicates
				$totalParamGroups = array_unique($totalParamGroups);
				
				// Fetch current user groups, but firstly user id using session id of the remote callee peer
				$userGroups = $otherPeerUser->getAuthorisedGroups();
				
				// Finally intersect to check ACL permission allowed
				if(!array_intersect($totalParamGroups, $userGroups)) {
					$this->response['storing'] = array('status'=>false, 'exception_message'=>Text::_('COM_JCHAT_OTHERPEER_NOTALLOWED'), 'usermessage'=>true);
					return $this->response;
				}
			}
		}
		
		// Check if the current caller is not already a callee from other peer, respect concurrency model
		$queryNotCallee = "SELECT" .
						  $this->dbInstance->quoteName('peer1') .
						  "\n FROM #__jchat_webrtc_blackboard" .
						  "\n WHERE " .
						  $this->dbInstance->quoteName('peer2') . " = " .
						  $this->dbInstance->quote($this->peer1);
		$imIACallee = $this->dbInstance->setQuery($queryNotCallee)->loadResult();
		
		if($isCaller) {
			// This peer is already a callee from other peer, don't go on starting a collision call!
			if($imIACallee) {
				$this->response['storing'] = array('status'=>false, 'exception_message'=>Text::_('COM_JCHAT_ALREADY_A_CALLEE'));
				return $this->response;
			}
			
			// Check if the current callee is not already busy in another call, AKA is a callee of another user AKA has already a caller, respect concurrency model
			$queryNotCallee = "SELECT" .
							  $this->dbInstance->quoteName('peer1') .
							  "\n FROM #__jchat_webrtc_blackboard" .
							  "\n WHERE " .
							  $this->dbInstance->quoteName('peer2') . " = " .
							  $this->dbInstance->quote($otherPeer);
			$hasAlreadyACaller = $this->dbInstance->setQuery($queryNotCallee)->loadResult();
			if($hasAlreadyACaller && $hasAlreadyACaller != $this->peer1) {
				// The other peer is already a callee from another peer not me, don't go on starting a collision call!
				$this->response['storing'] = array('status'=>false, 'exception_message'=>Text::_('COM_JCHAT_HAS_ALREADY_A_SESSION'), 'usermessage'=>true);
				return $this->response;
			}
			
			// Check if the current callee is not already busy in another call, AKA is a caller of another user AKA has already a callee, respect concurrency model
			$queryNotCaller = "SELECT" .
							  $this->dbInstance->quoteName('peer2') .
							  "\n FROM #__jchat_webrtc_blackboard" .
							  "\n WHERE " .
							  $this->dbInstance->quoteName('peer1') . " = " .
							  $this->dbInstance->quote($otherPeer);
			$isAlreadyACaller = $this->dbInstance->setQuery($queryNotCaller)->loadResult();
			if($isAlreadyACaller) {
				// The other peer is already a callee from another peer not me, don't go on starting a collision call!
				$this->response['storing'] = array('status'=>false, 'exception_message'=>Text::_('COM_JCHAT_HAS_ALREADY_A_SESSION'), 'usermessage'=>true);
				return $this->response;
			}
		} else {
			// This peer is no more a callee from other peer for example the caller hanged up, don't go on starting a collision call!
			if(!$imIACallee) {
				$this->response['storing'] = array('status'=>false, 'exception_message'=>Text::_('COM_JCHAT_NOMORE_A_CALLEE'));
				return $this->response;
			}
		}
		
		// Exchange field and value
		$field = $sdp ? 'sdp' : 'icecandidate';
		$message = $sdp ? $sdp : $iceCandidate;
		
		// Now execute insert/update query with feed data, the result will be a call start/accept 
		$query = "INSERT INTO #__jchat_webrtc_blackboard" .
				 "\n (" . $this->dbInstance->quoteName('peer1') . "," .
				 $this->dbInstance->quoteName('peer2') . "," .
				 $this->dbInstance->quoteName($field) . ")" .
				 "\n VALUES (" .
				 $this->dbInstance->quote($this->peer1) . "," .
				 $this->dbInstance->quote($otherPeer) . "," .
				 $this->dbInstance->quote($message) . ")" .
				 "\n ON DUPLICATE KEY UPDATE" .
				 $this->dbInstance->quoteName($field) . "=" .
				 $this->dbInstance->quote($message);
		
		
		try {
			$this->dbInstance->setQuery($query);
			$this->dbInstance->execute ();
		} catch (JChatException $e) {
			$this->response['storing'] = array('status'=>false, 'exception_message'=>$e->getMessage());
			return $this->response;
		} catch (\Exception $e) {
			$jchatException = new JChatException($e->getMessage(), 'error');
			$this->response['storing'] = array('status'=>false, 'exception_message'=>$jchatException->getMessage());
			return $this->response;
		}
		
		// All went well
		$this->response['storing'] = array('status'=>true);
		
		return $this->response;
	}
 
	/**
	 * Completely deletes a blackboard session, this means that caller or callee
	 * will delete both records and reset status of both peers
	 * 
	 * @param int $ids
	 * @access public
	 * @return array
	 */
	public function deleteEntity($ids): array {
		// Check if a specific peer remote ID has been specified. At last this is mandatory
		$where = null;
		if($ids) {
			$where = "\n OR " . 
					 $this->dbInstance->quoteName('peer1') . " = " . 
				 	 $this->dbInstance->quote($ids);
		}
		
		// End a peers conversation call
		$query = "DELETE FROM #__jchat_webrtc_blackboard" . 
				 "\n WHERE" .
				 $this->dbInstance->quoteName('peer1') . " = " . 
				 $this->dbInstance->quote($this->peer1) .
				 $where;

		try {
			$this->dbInstance->setQuery($query);
			$this->dbInstance->execute ();
		} catch (JChatException $e) {
			$this->response['storing'] = array('status'=>false, 'exception_message'=>$e->getMessage());
			return $this->response;
		} catch (\Exception $e) {
			$jchatException = new JChatException($e->getMessage(), 'error');
			$this->response['storing'] = array('status'=>false, 'exception_message'=>$jchatException->getMessage());
			return $this->response;
		}
		
		// All went well
		$this->response['storing'] = array('status'=>true);
		
		return $this->response;
	}
	
	/**
	 * Class constructor
	 * @access public
	 * @param Object& $wpdb
	 * @param Object& $userObject
	 * @return Object &
	 */
	public function __construct($config = array(), MVCFactoryInterface $factory = null) {
		// Store ssessionID peer1 caller
		$this->peer1 = $config['sessiontable']->session_id;
		
		// Get component params
		$this->getComponentParams();
		
		parent::__construct( $config, $factory );
	}
}