<?php
namespace JExtstore\Component\JChat\Site\Model;
/** 
 * @package JCHAT::LIVESTREAMING::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\Filesystem\Folder;
use Joomla\CMS\Filesystem\File;
use Joomla\CMS\Date\Date;
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::LIVESTREAMING::components::com_jchat
 * @subpackage models
 * @since 2.48
 */ 
class LivestreamingModel extends JChatModel {
	/**
	 * This peer1 session ID
	 * @access private
	 * @var int
	 */
	private $mySessionId;
	
	/**
	 * Client response
	 * @access private
	 * @var array
	 */
	private $response;
	
	/**
	 * Retrieve a broadcaster name based on the overrides chain
	 *
	 * @access public
	 * @return string
	 */
	public function getBroadcasterName() {
		$broadcasterSessionId = $this->getState('livestreaming_broadcaster_sessionid');
		if(!$broadcasterSessionId) {
			return '';
		}
		
		$userFieldName = $this->componentParams->get('usefullname', 'username');
		$broadcasterName = '';
		
		// End a peers conversation call
		$query = "SELECT u.$userFieldName, ccs.override_name" .
				 "\n FROM " . $this->dbInstance->quoteName('#__session') . " AS sess" .
				 "\n LEFT JOIN " . $this->dbInstance->quoteName('#__users') . " AS u ON sess.userid = u.id" .
				 "\n LEFT JOIN " . $this->dbInstance->quoteName('#__jchat_sessionstatus') . " AS ccs ON sess.session_id = ccs.sessionid".
				 "\n WHERE sess.session_id = " . $this->dbInstance->quote($broadcasterSessionId);
				 
		$this->dbInstance->setQuery($query);
		$nameOfTheUser = $this->dbInstance->loadAssoc();
		
		// Guest name override: user field name -> override name -> auto generated
		if($nameOfTheUser) {
			if(!$nameOfTheUser[$userFieldName]) {
				if(!$nameOfTheUser['override_name']) {
					$broadcasterName = JChatHelpersUsers::generateRandomGuestNameSuffix($broadcasterSessionId, $this->componentParams);
				} else {
					$broadcasterName = $nameOfTheUser['override_name'];
				}
			} else {
				$broadcasterName = $nameOfTheUser[$userFieldName];
			}
		}
		
		// Set the model state for a found user based on the sessionid AKA livestreaming ID, if not avoid to display the interface
		$this->setState('livestreaming_exists', $broadcasterName);
		$this->setState('livestreaming_ismine', ($broadcasterSessionId == $this->mySessionId));
		
		return $broadcasterName;
	}
	
	/**
	 *
	 * Store the session status for livestreaming role and state
	 *
	 * @access public
	 * @return boolean
	 */
	public function checkIfStreamingStale() {
		$broadcasterSessionId = $this->getState('livestreaming_broadcaster_sessionid');
		if(!$broadcasterSessionId) {
			return false;
		}
		
		// End a peers conversation call
		$query = "SELECT " . $this->dbInstance->quoteName('time') .
				 "\n FROM " . $this->dbInstance->quoteName('#__session') . " AS sess" .
				 "\n WHERE sess.session_id = " . $this->dbInstance->quote($broadcasterSessionId);
		
		$this->dbInstance->setQuery($query);
		$isSessionStillAvailable = $this->dbInstance->loadResult();

		return (bool)$isSessionStillAvailable;
	}
	
	/**
	 *
	 * Check if a live recording is available for the current broadcaster sessionid
	 *
	 * @access public
	 * @return boolean
	 */
	public function checkIfRecordingAvailable() {
		$broadcasterSessionId = $this->getState('livestreaming_broadcaster_sessionid');
		if(!$broadcasterSessionId) {
			return false;
		}
		
		// Check if a recording live session is available for this broadcaster session ID
		$folderExists = file_exists(JPATH_ROOT . '/media/com_jchat/livestreaming/archive/' . $broadcasterSessionId);
		return $folderExists;
	}
	
	/**
	 *
	 * Store the session status for livestreaming role and state
	 *
	 * @param string $broadcasterSessionId
	 * @param int $watcherIsWatching
	 * @access public
	 * @return boolean
	 */
	public function updateEntity($broadcasterSessionId = null, $watcherIsWatching = 0) {
		$streamingHash = $broadcasterSessionId ? $broadcasterSessionId : $this->mySessionId;
		
		$query = "INSERT INTO #__jchat_sessionstatus" .
				 "\n (" . $this->dbInstance->quoteName('sessionid') . "," . 
				 		  $this->dbInstance->quoteName('livestreaming_hash') . "," . 
				 		  $this->dbInstance->quoteName('livestreaming_watch') . " ) VALUES (" .
		 		  "\n " . $this->dbInstance->quote($this->mySessionId) . "," .
						  $this->dbInstance->quote($streamingHash) . "," . 
				 		  (int)($watcherIsWatching) . ") " .
				 "\n ON DUPLICATE KEY UPDATE " . 
				 $this->dbInstance->quoteName('livestreaming_hash') . " = " . $this->dbInstance->quote($streamingHash) . "," .
				 $this->dbInstance->quoteName('livestreaming_watch') . " = " . (int)($watcherIsWatching);
		
		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;
	}
	
	/**
	 * Store uploaded file to the media recordings folder,
	 * fully manage error messages and ask for database insert
	 *
	 * @access public
	 * @param bool $updateNulls
	 * @return Object
	 */
	public function storeEntity($updateNulls = false) {
		// Response JSON object
		$response = new \stdClass ();
		$this->getComponentParams();
		$livestreamingMediaFolder = JPATH_ROOT . '/media/com_jchat/livestreaming';
		$livestreamingArchiveFolder = JPATH_ROOT . '/media/com_jchat/livestreaming/archive/' . $this->mySessionId;
		
		// Get uploaded model state
		$uploadedMediaFile = $this->getState('blobfile');
		$uploadedMediaFileName = $this->getState('blobfilename');
		
		try {
			$tmpFile = $uploadedMediaFile['tmp_name'];
			
			if(!$tmpFile) {
				throw new \Exception(Text::_('COM_JCHAT_NOFILE_SELECTED'));
			}
			
			if(!is_dir($livestreamingMediaFolder)) {
				Folder::create($livestreamingMediaFolder);
			}
			
			if(!is_writable($livestreamingMediaFolder)) {
				if(!chmod($livestreamingMediaFolder, 0775)) {
					throw new \Exception( Text::_('COM_JCHAT_MEDIADIR_WRITABLE'));
				}
			}
			
			// If the recording option is enabled, archive this live streaming for later watchers
			if($this->app->getMenu()->getParams($this->getState('propagatedItemid'))->get('record_livestreaming', 0) && file_exists($tmpFile)) {
				if(!is_dir($livestreamingArchiveFolder)) {
					Folder::create($livestreamingArchiveFolder);
					$lastChunkValue = 0;
					File::copy($tmpFile, $livestreamingArchiveFolder . '/' . $lastChunkValue . '.webm');
					
					// Add a record to the database table #__jchat_recordings
					$this->setState('livestreaming_broadcaster_sessionid', $this->mySessionId);
					$recorderTable = $this->getTable('Recorder');
					$recorderTable->title = 'live_streaming_' . $this->getBroadcasterName() . '_' . $this->mySessionId;
					$recorderTable->size = '-';
					$recorderTable->timerecord = Date::getInstance()->toSql(true);
					$recorderTable->peer1 = $this->getBroadcasterName();
					$recorderTable->peer2 = 'livestreaming';
					$recorderTable->store();
				} else {
					$currentChunkFiles = Folder::files($livestreamingArchiveFolder);
					asort($currentChunkFiles, SORT_NUMERIC);
					$lastChunkFileName = array_pop($currentChunkFiles);
					$lastChunkValue = str_ireplace('.webm', '', $lastChunkFileName);
					$lastChunkValue++;
					File::copy($tmpFile, $livestreamingArchiveFolder . '/' . $lastChunkValue . '.webm');
				}
			}

			// Check if file already esists
			if(file_exists($livestreamingMediaFolder . '/' . $uploadedMediaFileName)) {
				unlink($livestreamingMediaFolder . '/' . $uploadedMediaFileName);
			}
			
			if(!move_uploaded_file($tmpFile, $livestreamingMediaFolder . '/' . $uploadedMediaFileName)) {
				throw new \Exception( Text::_('COM_JCHAT_MEDIAUPLOAD_ERROR'));
			}
		} catch ( \Exception $e ) {
			$response->result = false;
			$response->exception_message = $e->getMessage ();
			return $response;
		}
		
		// Manage exceptions from DB Model and return to JS domain
		$response->result = true;
		
		return $response;
	}
	
	/**
	 * Delete/archive uploaded file to the media recordings folder,
	 * fully manage error messages and ask for database insert
	 *
	 * @access public
	 * @return Object
	 */
	public function deleteEntity($ids) {
		// Response JSON object
		$response = new \stdClass ();
		$this->getComponentParams();
		$livestreamingMediaFolder = JPATH_ROOT . '/media/com_jchat/livestreaming';
		
		// Get uploaded model state
		$uploadedMediaFileName = $this->getState('blobfilename');
		
		try {
			// Check if file already esists
			if(file_exists($livestreamingMediaFolder . '/' . $uploadedMediaFileName)) {
				unlink($livestreamingMediaFolder . '/' . $uploadedMediaFileName);
			}
		} catch ( \Exception $e ) {
			$response->result = false;
			$response->exception_message = $e->getMessage ();
			return $response;
		}
		
		// Manage exceptions from DB Model and return to JS domain
		$response->result = true;
		
		return $response;
	}
	
	/**
	 * Class constructor
	 * @access public
	 * @param array $config
	 * @param object $factory
	 */
	public function __construct($config = array(), MVCFactoryInterface $factory = null) {
		parent::__construct( $config, $factory );
		
		// Store ssessionID peer1 caller
		$this->mySessionId = $config['sessiontable']->session_id;
		$this->setState('livestreamingid', $this->mySessionId);
		$this->response = array();
	}
}