<?php
namespace JExtstore\Component\JChat\Site\Model;
/** 
 * @package JCHAT::WEBRTC::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 Joomla\CMS\Filter\InputFilter;
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;

/**
 * Group users chat model
 * 
 * @package JCHAT::WEBRTC::components::com_jchat
 * @subpackage models
 * @since 2.5
 */ 
class ConferenceModel extends JChatModel {
	/**
	 * This peer1 session ID
	 * @access private
	 * @var int
	 */
	private $peer1;
	
	/**
	 * Client response
	 * @access private
	 * @var array
	 */
	private $response;
	
	/**
	 * Stream model instance
	 * @access public
	 * @var object
	 */
	public $streamModel;
	
	/**
	 * User instance
	 * @access public
	 * @var object
	 */
	public $myUser;
	
	/**
	 * User field name
	 * @access public
	 * @var string
	 */
	public $userFieldName;
	
	/**
	 * 
	 * Store contact user ID for current owner
	 * 
	 * @param string $otherPeer
	 * @param string $sdp
	 * @param string $iceCandidate
	 * @param int $videoCam
	 * @param int $isCaller
	 * @params string $otherPeers Used to chain calls with other participants
	 * 
	 * @access public
	 * @return boolean
	 */
	public function storeEntity($otherPeer = null, $sdp = null, $iceCandidate = null, $videoCam = null, $isCaller = 0, $otherPeers = null) {
		// 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_videochat', 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;
				}
			}
		}
		
		// Avoid collision call based on concurrency model
		if(!$isCaller) {
			// 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_conference" .
							  "\n WHERE " .
							  $this->dbInstance->quoteName('peer2') . " = " .
							  $this->dbInstance->quote($this->peer1);
			$imIACallee = $this->dbInstance->setQuery($queryNotCallee)->loadResult();
			// 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_conference" .
				 "\n (" . $this->dbInstance->quoteName('peer1') . "," .
				 $this->dbInstance->quoteName('peer2') . "," .
				 $this->dbInstance->quoteName($field) . "," .
				 $this->dbInstance->quoteName('videocam') . "," .
				 $this->dbInstance->quoteName('other_peers') . ")" .
				 "\n VALUES (" .
				 $this->dbInstance->quote($this->peer1) . "," .
				 $this->dbInstance->quote($otherPeer) . "," .
				 $this->dbInstance->quote($message) . "," .
				 $this->dbInstance->quote($videoCam) . "," .
				 $this->dbInstance->quote($otherPeers) . ")" .
				 "\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 conversation call, 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 {
		// End a peers conversation call
		$query = "DELETE FROM #__jchat_webrtc_conference" . 
				 "\n WHERE (" .
				 $this->dbInstance->quoteName('peer1') . " = " . 
				 $this->dbInstance->quote($this->peer1) .
				 "\n AND " .
				 $this->dbInstance->quoteName('peer2') . " = " . 
				 $this->dbInstance->quote($ids) . ")" .
				 "\n OR (" .
				 $this->dbInstance->quoteName('peer1') . " = " . 
				 $this->dbInstance->quote($ids) .
				 "\n AND " .
				 $this->dbInstance->quoteName('peer2') . " = " . 
				 $this->dbInstance->quote($this->peer1) . ")";

		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);
		
		// Remove my own deleted call from the session to avoid false double
		if(isset($this->sessionName['jchat_conference_webrtc_caller_sessions'][$ids])) {
			unset($this->sessionName['jchat_conference_webrtc_caller_sessions'][$ids]);
		}
		
		return $this->response;
	}
	
	/**
	 *
	 * Store contact user ID for current owner
	 *
	 * @param int $videoCam
	 * @access public
	 * @return boolean
	 */
	public function updateEntity($videoCam) {
		// End a peers conversation call
		$query = "UPDATE #__jchat_webrtc_conference" .
				 "\n SET " .
				 $this->dbInstance->quoteName('videocam') . " = " . (int)$videoCam .
				 "\n WHERE" .
				 $this->dbInstance->quoteName('peer1') . " = " .
				 $this->dbInstance->quote($this->peer1);
		
		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 array $config
	 * @param object $factory
	 */
	public function __construct($config = array(), MVCFactoryInterface $factory = null) {
		// Parent model construct
		parent::__construct( $config, $factory );
		
		// Store ssessionID peer1 caller
		$this->peer1 = $config['sessiontable']->session_id;
		$this->response = array();
		
		// Get component params
		$this->getComponentParams();
		
		// Reference to the stream model
		if(isset($config['streamModel'])) {
			$this->streamModel = $config['streamModel'];
		}
		
		// Store the owned user object instance
		$this->myUser = $this->app->getIdentity();
		
		$filter = InputFilter::getInstance();
		$this->userFieldName = $filter->clean($this->componentParams->get('usefullname', 'username'), 'word');
		
	}
}