<?php
namespace JExtstore\Component\JChat\Site\Model;
/**  
 * @package JCHAT::STREAM::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\Uri\Uri;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Filter\OutputFilter;
use Joomla\CMS\Filter\InputFilter;
use Joomla\CMS\Language\Multilanguage;
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\Messages as JChatHelpersMessages;
use JExtstore\Component\JChat\Administrator\Framework\Helpers\Users as JChatHelpersUsers;
use JExtstore\Component\JChat\Administrator\Framework\Html\Menu as JChatHtmlMenu;
use JExtstore\Component\JChat\Administrator\Framework\Helpers\Language as JChatHelpersLanguage;

/**
 * Stream model public responsibilities interface
 * The entity to perform CRUD operation on is the Stream
 * It supports special get/store/delete responsibilities to be a 
 * more generic stream resource for chat service
 *
 * @package JCHAT::STREAM::components::com_jchat
 * @subpackage models
 * @since 1.0
 */
interface IStreamModel {
	/**
	 * Get the Data on read from Stream
	 * @access public
	 * @return array
	 */
	public function getChatData();
	
	/**
	 * Detect if live support mode is active and returns query chunks to
	 * filter users/messages if users is not a chat admins
	 *
	 * @access private
	 * @return array
	 */
	public function getQueryLiveSupport();
	
	/**
	 * Translate chat access levels into user groups and generates filtering query
	 * accordingly for the users taken into account by the buddylist, thus avoiding
	 * show disabled users into chat of enabled users refreshing session lifetime during navigation
	 *
	 * @access private
	 * @return array
	 */
	public function getQueryAccessLevels();
	
	/**
	 * Filter the buddylist based on the current user groups belonging
	 * Users will be be able to chat only with users in the same users groups
	 *
	 * @access public
	 * @return array
	 */
	public function getQueryMyUsersGroups($joinTable = 'sess.userid');
	
	/**
	 * Get query parts needed for SELECT & JOIN
	 * tables for database integration component
	 *
	 * @access private
	 * @param string $queryType
	 * @return array
	 */
	public  function getQueryParts($queryType);
	
	/**
	 * Write user status on Stream
	 * 
	 * @access public
	 * @param string $status
	 * @return array
	 */
	public function storeUserStatus($fieldName, $fieldValue);
	
	/**
	 * Write user Skype ID on Stream
	 * 
	 * @access public
	 * @param int $skypeID
	 * @return array
	*/
	public function storeUserStateFromRequest($statusVarName, $statusVarValue);
	
	/**
	 * Store banning state for current session id user
	 *
	 * @access public
	 * @return array
	 */
	public function storeBannedUsersState($bannedUserInfo);
	
	/**
	 * Write private message on Stream
	 * 
	 * @access public
	 * @param string $to
	 * @param int $tologged
	 * @param string $message
	 * @param Object $mailer
	 * @return array
	 */
	public function storePrivateMessage($to, $tologged, $message, $mailer);
	
	/**
	 * Write group message on Stream
	 * 
	 * @access public
	 * @param int $to
	 * @param string $message
	 * @return array
	 */
	public function storeGroupMessage($to, $message);
	
	/**
	 * Add a new chatroom to the database
	 *
	 * @access public
	 * @param string $roomName
	 * @param string $roomDescription
	 * @param int $roomAccess
	 * @param string $roomMenuitems
	 * @return array
	 */
	public function storeNewChatroom($roomName, $roomDescription, $roomAccess, $roomMenuItems);
	
	/**
	 * Delete conversation from session
	 * 
	 * @access public
	 * @param int $from
	 * @return array
	 */
	public function deleteConversation($from);
	
	/**
	 * Delete chatroom from the frontend stream
	 *
	 * @access public
	 * @param int $chatroomID
	 * @return array
	 */
	public function deleteChatroom($chatroomID);
	
	/**
	 * Retrieve guest info user informations, usually stored by guest activation form
	 *
	 * @access public
	 * @param string $session_id
	 * @return array
	 */
	public function getInfoGuest($session_id);
	
	/**
	 * Load chatrooms from DB using caching system
	 *
	 * @access private
	 * @param int $menuItemid
	 * @return array
	 */
	public function loadChatRooms($menuItemid);
	
	/**
	 * Get user profile based on integration type
	 *
	 * @access public
	 * @param int $id
	 * @param string $name
	 * @param Object $cParams
	 * @return string
	 */
	public function formatUserProfileLink($id, $name, $cParams);
	
	/**
	 * Retrieve old messages based on time period and private conversation
	 * between to logged in and registered users
	 *
	 * @access public
	 * @param int $fromLoggedID
	 * @param string $fromUserID
	 * @param string $timePeriod
	 * @param int $minMessageId
	 * @return array
	 */
	public function fetchHistoryMessages($fromLoggedID, $fromUserID, $timePeriod, $minMessageId);
}

/**
 * Main stream class concrete implementation
 * 
 * @package JCHAT::STREAM::components::com_jchat
 * @subpackage models 
 * @since 1.0
 */
class StreamModel extends JChatModel implements IStreamModel {
	/**
	 * Response aray
	 * @access private
	 * @var array
	 */
	private $response;
	
	/**
	 * Main private messages
	 * @access private
	 * @var array
	 */
	private $messages;
	
	/**
	 * Public group messages
	 * @access private
	 * @var array
	 */
	private $wallMessages;
	
	/**
	 * User Object
	 * @access private
	 * @var Object &
	 */
	private $myUser;
	
	/**
	 * User chatroom ID if any
	 * @access private
	 * @var Object &
	 */
	private $myChatRoom;
	
	/**
	 * User session table Object
	 * @access private
	 * @var Object &
	 */
	private $userSessionTable;
	
	/**
	 * Type of social extension integrated if any from main config params
	 * @access private
	 * @var string
	 */
	private $integratedExtensions;
	
	/**
	 * Monitor typing status changes from users interaction with chatbox
	 * @access private
	 * @var string
	 */
	private $typingStatusChanged;
	
	/**
	 * Keep track of the target typing user to that user is writing
	 * @access private
	 * @var string
	 */
	private $typingTo;
	
	/**
	 * Discard operations if typing is not enabled
	 * @access private
	 * @var string
	 */
	private $typingEnabled;
	
	/**
	 * Sess SQL query for the client id
	 * @access private
	 * @var string
	 */
	private $sessClientId;
	
	/**
	 * Session SQL query for the client id
	 * @access private
	 * @var string
	 */
	private $sessionClientId;
	
	/**
	 * Store the current state of the meeting feature
	 * @access private
	 * @var boolean
	 */
	private $meetingsFeatureEnabled;
	
	/**
	 * Store the current state of the livestreaming feature based on menu items
	 * @access private
	 * @var boolean
	 */
	private $livestreamingFeatureEnabled;
	
	/**
	 * Params Object
	 * @access protected
	 * @var Object &
	 */
	protected $componentParams;
	
	/**
	 * Convert time to day/hours/minutes
	 * @access private
	 * @param int $time
	 * @return string
	 */
	private function convertToDaysHoursMins($time) {
	    settype($time, 'integer');
	    $time2Display = null;
	    if ($time < 0) {
	        return;
	    }
	
	    // case: show years
	    $years = floor(((($time/60)/60)/24)/365);
	    if($years > 0) {
	    	$time2Display = $years . Text::_('COM_JCHAT_YEARS');
	    }
	    
	    // case: show days
	    $days = floor((($time/60)/60)/24);
	    if($days > 0 && $days < 365) {
	    	$time2Display = $days . Text::_('COM_JCHAT_DAYS');
	    }
	    
	    // case: show hours
	    $hours = floor(($time/60)/60);
	    if($hours > 0 && $hours < 24) {
	    	$time2Display = $hours . Text::_('COM_JCHAT_HOURS');
	    }
	    
	    // case: show minutes
	    $minutes = floor($time/60);
	    if($minutes > 0 && $minutes < 60) {
	        $time2Display = $minutes . Text::_('COM_JCHAT_MINUTES');
	    }
	    
		// case: show seconds
	    $seconds = $time;
	    if($seconds > 0 && $seconds < 60) {
	        $time2Display = $seconds . Text::_('COM_JCHAT_SECONDS');
	    }
	    
	    return $time2Display;
	}
	
	/**
	 * Si occupa di controllare se esistono nuovi MSGFILE presenti nel database con status = 1
	 * che non sono stati refreshati in sessione e li pone nella response['downloads'] memorizzandoli
	 * separatamente in sessione per evitare doppie notifiche e aggiornamenti
	 * 
	 * @access private
	 * @return void
	 */
	private function refreshMsgFileSessionStatus() {
		$query = "SELECT id, " . $this->dbInstance->quoteName('to') . " FROM #__jchat" .
				 "\n WHERE type=" . $this->dbInstance->quote('file') .
				 "\n AND status = 1" .
				 "\n AND " . $this->dbInstance->quoteName('from') . " = " . $this->dbInstance->quote($this->userSessionTable->session_id);
		$this->dbInstance->setQuery($query);
		$msgFiles = $this->dbInstance->loadObjectList();
		
		// Gestiamo il session array downloads già notificati
		if(!isset($this->sessionName['jchat_notified_downloads'])) {
			$this->sessionName['jchat_notified_downloads'] = array();
		} 
		
		if(is_array($msgFiles) && count($msgFiles)) {
			foreach ($msgFiles as $msgFile) {
				if(!array_key_exists($msgFile->id, $this->sessionName['jchat_notified_downloads'])) {
					$conversation2Refresh = &$this->sessionName['jchat_user_' . $msgFile->to];
					if(is_array($conversation2Refresh) && count($conversation2Refresh)) {
						$conversation2Refresh[$msgFile->id]['status'] = $this->sessionName['jchat_user_' . $msgFile->to][$msgFile->id]['status'] = 1;
						$this->sessionName['jchat_notified_downloads'][$msgFile->id] = true;
						$this->response['downloads'][] = array($msgFile->to, $msgFile->id);
					}
				}
			} 
		}
	}
	
	/**
	 * Get user profile based on integration type
	 *
	 * @access private
	 * @param int $id
	 * @param string $name
	 * @param Object $cParams
	 * @return string
	 */
	private function getUserProfileLink($id, $name, $cParams) {
		// User id required
		if(!$id) {
			return null;
		}

		if(!$cParams->get('3pdintegration', null) && !$cParams->get('joomlauser_profilelink', 0)) {
			return null;
		}

		// Get list of current chatrooms available
		$cachable = $cParams->get('caching', false);
		if($cachable) {
			// By default callback handler
			$cache = $this->getExtensionCache();
			$profileLink = $cache->get(array($this, 'formatUserProfileLink'), array($id, $name, $cParams));
		} else {
			$profileLink = $this->formatUserProfileLink($id, $name, $cParams);
		}

		return $profileLink;
	}
	
	/**
	 * Retrieve TURN server credentials from Twilio service based on $sid and $token registered account
	 *
	 * @access private
	 * @param string $sid
	 * @param string $token
	 * @return mixed, false if error or the retrieved object including iceServers property
	 */
	private function getTURNServer($sid, $token) {
		if(version_compare(PHP_VERSION, '5.3', '>=')) {
			// Composer autoloader
			require_once JPATH_COMPONENT_ADMINISTRATOR . '/Framework/composer/autoload_real.php';
		} else {
			return false;
		}
			
		try {
			// Initialize the client
			$client = new \Twilio\Rest\Client($sid, $token);
			
			//Creates a token
			$tokenObject = $client->tokens->create();
		} catch (\Exception $e) {
			return false;
		}
		
		return $tokenObject;
	}
	
	/**
	 * Translates all the parameters of type ACL group including child groups
	 *
	 * @access private
	 * @param array $aclParameters
	 * @return boolean
	 */
	private function translatesACLParameters($aclParameters) {
		// Iterate over ACl parameters
		foreach ($aclParameters as $aclParameter) {
			$aclParamValue = $this->componentParams->get($aclParameter, array(0));
			$totalParamGroups = $aclParamValue;
			// Ensure that the ACL parameter has no 'All groups' option selected
			if(is_array($aclParamValue) && !in_array(0, $aclParamValue, false)) {
				// 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);
			}
			// Final reassignment to the component params array, with the $totalparamGroups augmented with childs
			$this->componentParams->set($aclParameter, $totalParamGroups);
		}

		return true;
	}
	
	/**
	 * Get users status
	 * 
	 * @access protected
	 * @return void
	 */
	protected function getUserStateFromDB() { 
		// If not guest
		if($this->myUser->id) {
			$sql = 	"SELECT " .
					$this->dbInstance->quoteName('status') . "," .
					$this->dbInstance->quoteName('skypeid') . "," . 
					$this->dbInstance->quoteName('roomid') .
					"\n FROM " .
					$this->dbInstance->quoteName('#__jchat_userstatus') .
					"\n WHERE " . $this->dbInstance->quoteName('userid') ." = " . $this->dbInstance->quote($this->myUser->id);
			$this->dbInstance->setQuery($sql);
			$userStatus = $this->dbInstance->loadAssoc();
		}
		
		$sql = 	"SELECT " . 
				$this->dbInstance->quoteName('status') . "," .
				$this->dbInstance->quoteName('override_name') . "," .
			   	$this->dbInstance->quoteName('skypeid') . "," .
			   	$this->dbInstance->quoteName('roomid') .
				"\n FROM " .
				$this->dbInstance->quoteName('#__jchat_sessionstatus') .
				"\n WHERE " . $this->dbInstance->quoteName('sessionid') ." = " . $this->dbInstance->quote($this->userSessionTable->session_id); 
		$this->dbInstance->setQuery($sql); 
		$chat = $this->dbInstance->loadAssoc();  
		
		if (empty($chat['status'])) {
			$chat['status'] = 'available';
		} else {
			if ($chat['status'] == 'offline') {
				$this->sessionName['jchat_sessionvars']['buddylist'] = 0;
			}
		}
		
		$overrideName = null;
		if(!empty($chat['override_name'])) {
			$overrideName = $chat['override_name'];
		}
		
		$status = null;
		if(!empty($userStatus['status'])) {
			$status = $userStatus['status'];
		} elseif(!empty($chat['status'])) {
			$status = $chat['status'];
		}
		
		$skypeId = null;
		if(!empty($userStatus['skypeid'])) {
			$skypeId = $userStatus['skypeid'];
		} elseif(!empty($chat['skypeid'])) {
			$skypeId = $chat['skypeid'];
		}
		
		$roomid = null;
		if(!empty($userStatus['roomid'])) {
			$roomid = $userStatus['roomid'];
		} elseif(!empty($chat['roomid'])) {
			$roomid = $chat['roomid'];
		} elseif(isset($userStatus['roomid']) || isset($chat['roomid'])) {
			$roomid = 0;
		}
		
		$status = array('status' => $status, 'override_name' => $overrideName, 'skype_id' => $skypeId, 'room_id' => $roomid);
		$this->response['userstatus'] = $status;
	}
  
	/**
	 * Get meeting status
	 *
	 * @access protected
	 * @return string
	 */
	protected function getMeetingStateFromDB() {
		$sql = 	"SELECT " .
				$this->dbInstance->quoteName('meeting_hash') .
				"\n FROM " .
				$this->dbInstance->quoteName('#__jchat_sessionstatus') .
				"\n WHERE " . $this->dbInstance->quoteName('sessionid') ." = " . $this->dbInstance->quote($this->userSessionTable->session_id);
		$this->dbInstance->setQuery($sql);
		$meetingHash = $this->dbInstance->loadResult();

		return $meetingHash;
	}
	
	/**
	 * Check if a user is banned to prevent instantly the chat app execution
	 *
	 * @access protected
	 * @return boolean
	 */
	protected function getBannedStatus() {
		$queryUserBannedUsers = "SELECT" .
								$this->dbInstance->quoteName('banstatus') .
								"\n FROM #__jchat_userstatus" .
								"\n WHERE" .
								"\n " . $this->dbInstance->quoteName('userid') . " = " . (int)$this->myUser->id;
		$bannedUserStatus = $this->dbInstance->setQuery($queryUserBannedUsers)->loadResult();

		$querySessionBannedUsers = 	"SELECT" .
									$this->dbInstance->quoteName('banstatus') .
									"\n FROM #__jchat_sessionstatus" .
									"\n WHERE" .
									"\n " . $this->dbInstance->quoteName('sessionid') . " = " . $this->dbInstance->quote($this->userSessionTable->session_id);
		$bannedSessionStatus = $this->dbInstance->setQuery($querySessionBannedUsers)->loadResult();

		return (bool)($bannedUserStatus || $bannedSessionStatus);
	}
	
	/**
	 * Inject chatrooms list on first initialize and subsequent buddylist dispatching
	 *
	 * @access protected
	 * @param array $buddylist
	 * @return void
	 */
	protected function getChatRoomsList($buddylist) {
		// Get list of current chatrooms available
		$cachable = $this->componentParams->get('caching', false);
		$menuItemid = $this->app->input->getInt('jchat_itemid');
		if($cachable) {
			// By default callback handler
			$cache = $this->getExtensionCache();
			$rooms = $cache->get(array($this, 'loadChatRooms'), array($menuItemid));
		} else {
			$rooms = $this->loadChatRooms($menuItemid);
		}
		
		if($buddylist) {
			$showChatroomsUsersDetails = $this->componentParams->get('chatrooms_users_details', 1);
			$currentlyValidSessions = array_keys($buddylist);
			$currentlyValidSessions = "'" . implode("','", $currentlyValidSessions) . "'"; 
			// Count rooms from userstate
			$query = "SELECT COUNT(rooms.roomid) AS numusers, rooms.roomid" .
					 "\n FROM #__jchat_userstatus AS rooms" .
					 "\n INNER JOIN #__session AS sess" .
					 "\n ON sess.userid = rooms.userid" .
					 "\n WHERE " . $this->sessClientId . " AND sess.session_id IN(" . $currentlyValidSessions . ")" .
					 "\n GROUP BY rooms.roomid";
			$this->dbInstance->setQuery($query);
			$roomsUserstateCount = $this->dbInstance->loadAssocList('roomid');
			
			// Count rooms from sessionstate
			$query = "SELECT COUNT(rooms.roomid) AS numusers, rooms.roomid" .
					"\n FROM #__jchat_sessionstatus AS rooms" .
					"\n WHERE rooms.sessionid IN(" . $currentlyValidSessions . ")" .
					"\n GROUP BY rooms.roomid";
			$this->dbInstance->setQuery($query);
			$roomsSessionstateCount = $this->dbInstance->loadAssocList('roomid');
			
			// Needs activating users details for joined rooms
			// Select all single users that are in a chatroom then merge with room data
			if($showChatroomsUsersDetails) {
				$query = "SELECT rooms.roomid, sess.session_id AS sessionid" .
						 "\n FROM #__jchat_userstatus AS rooms" .
						 "\n INNER JOIN #__session AS sess" .
						 "\n ON sess.userid = rooms.userid" .
						 "\n WHERE " . $this->sessClientId . " AND sess.session_id IN(" . $currentlyValidSessions . ")" .
						 "\n ORDER BY rooms.roomid";
				$this->dbInstance->setQuery($query);
				$roomsUserstateUsers = $this->dbInstance->loadObjectList();
				
				$query = "SELECT rooms.roomid, rooms.sessionid" .
						 "\n FROM #__jchat_sessionstatus AS rooms" .
						 "\n WHERE rooms.sessionid IN(" . $currentlyValidSessions . ")" .
						 "\n ORDER BY rooms.roomid";
				$this->dbInstance->setQuery($query);
				$roomsSessionstateUsers = $this->dbInstance->loadObjectList();
			}
			
			
			// Manage total users per each chat rooms
			if(count($rooms)) {
				foreach ($rooms as &$room) {
					if(array_key_exists($room['id'], $roomsUserstateCount)) {
						$currentUsers = $roomsUserstateCount[$room['id']]['numusers'];
						$room['numusers'] += $currentUsers;
					}
					if(array_key_exists($room['id'], $roomsSessionstateCount)) {
						$currentSessions = $roomsSessionstateCount[$room['id']]['numusers'];
						$room['numusers'] += $currentSessions;
					}
					
					// Needs activating users details for joined rooms
					if($showChatroomsUsersDetails) {
						if(count($roomsUserstateUsers)) {
							foreach ($roomsUserstateUsers as $userStateUser) {
								if($userStateUser->roomid == $room['id']) {
									$room['users'][] = $buddylist[$userStateUser->sessionid]['name'];
								}
							}
						}
						if(count($roomsSessionstateUsers)) {
							foreach ($roomsSessionstateUsers as $sessionStateUser) {
								if($sessionStateUser->roomid == $room['id']) {
									$room['users'][] = $buddylist[$sessionStateUser->sessionid]['name'];
								}
							}
						}
					}
				}
			}
		}
	
		// Inject into client response
		$this->response['chatrooms'] = $rooms;
	}
	
	/**
	 * Inject my chatroom users on first initialize and subsequent buddylist dispatching
	 *
	 * @access protected
	 * @access array $buddylist the current buddylist to filter
	 * @return void
	 */
	protected function getMyChatRoomUsers($buddylist) {
		$usersInMyRoom = array();
		
		if($this->myChatRoom && $buddylist) {
			// Session users in my room from valid Joomla sessions JOIN userstate
			$query = "SELECT sess.session_id" .
					 "\n FROM #__session AS sess" .
					 "\n INNER JOIN #__jchat_sessionstatus AS status" .
					 "\n ON sess.session_id = status.sessionid" .
					 "\n WHERE " . $this->sessClientId . " AND status.roomid = " . (int)$this->myChatRoom;
			$this->dbInstance->setQuery($query);
			$sessionsUserState = $this->dbInstance->loadColumn();
			
			// Session users in my room from valid Joomla sessions JOIN sessionstate
			$query = "SELECT sess.session_id" .
					 "\n FROM #__session AS sess" .
					 "\n INNER JOIN #__jchat_userstatus AS status" .
					 "\n ON sess.userid = status.userid" .
					 "\n WHERE " . $this->sessClientId . " AND status.roomid = " . (int)$this->myChatRoom;
			$this->dbInstance->setQuery($query);
			$usersUserState = $this->dbInstance->loadColumn();
			
			// Merge both arrays and ensure unique values
			$totalSessions = array_merge($sessionsUserState, $usersUserState);
			$totalSessions = array_unique($totalSessions);
			
			if(count($totalSessions)) {
				foreach ($totalSessions as $userInMyRoom) {
					if(array_key_exists($userInMyRoom, $buddylist)) {
						$userData = $buddylist[$userInMyRoom];
						$usersInMyRoom[] = array('sessionid'=>$userInMyRoom, 'name'=>$userData['name']);
					}
				}
			}
		} else {
			$usersInMyRoom = false;
		}
		
		// Inject into client response
		$this->response['users_inmyroom'] = $usersInMyRoom;
	}
	
	/**
	 * Initialize my chat room id if chatroom mode is enabled
	 * 
	 * @access protected
	 * @return int chatroom ID if any
	 */
	protected function getMyChatRoom() {
		// Assume user is not joined in any chatroom by default
		$this->myChatRoom = false;
		
		// If chatroom mode is enabled, try to check if current user has joined to a chatroom, both as session guest or logged user
		if($this->componentParams->get('groupchatmode', 'chatroom') == 'chatroom') {
			// Limit received messages to only users that belongs to same my chatroom
			$queryMyChatRoom = 	"SELECT (SELECT userstate.roomid" .
								"\n FROM #__jchat_userstatus AS userstate" .
								"\n WHERE userstate.userid = " . (int)$this->myUser->id . ") AS user_roomid," .
								"\n (SELECT sessionstate.roomid" .
								"\n FROM #__jchat_sessionstatus AS sessionstate" .
								"\n WHERE sessionstate.sessionid = " . $this->dbInstance->quote($this->userSessionTable->session_id) . ") AS session_roomid";
			$myChatRoomInfo = $this->dbInstance->setQuery($queryMyChatRoom)->loadObject();
			$this->myChatRoom = $myChatRoomInfo->user_roomid ? $myChatRoomInfo->user_roomid : $myChatRoomInfo->session_roomid;
		}
		
		return $this->myChatRoom;
	}
	
	/**
	 * Retrieve and inject into response the last read message ID based on users stream
	 *
	 * @access protected
	 * @return void
	 */
	protected function getLatestReadMessage() {
		$openChatBoxesString = isset($this->sessionName ['jchat_sessionvars']['activeChatboxes']) ? $this->sessionName ['jchat_sessionvars']['activeChatboxes'] : null ;
		if($openChatBoxesString) {
			// Initialize response array
			$this->response['lastreadmessages'] = array();

			// Parse the currently opened chatboxes
			$chatBoxesIDs = preg_split('/(\|\d+,*)/i', $openChatBoxesString);
			array_pop($chatBoxesIDs);

			// Cycle and retrieving of the last read message id
			foreach ($chatBoxesIDs as $chatBoxID) {
				// Select query for the latest read message for the currently opened chatboxes
				$query = "SELECT " .
						 $this->dbInstance->quoteName('id') .
						 "\n FROM " . $this->dbInstance->quoteName('#__jchat') .
						 "\n WHERE " . $this->dbInstance->quoteName('from') . " = " . $this->dbInstance->quote($this->userSessionTable->session_id) .
						 "\n AND " . $this->dbInstance->quoteName('to') . " = " . $this->dbInstance->quote($chatBoxID) .
						 "\n AND " . $this->dbInstance->quoteName('read') . " = 1" .
						 "\n ORDER BY " .
						 $this->dbInstance->quoteName('sent') . " DESC" . "," .
						 $this->dbInstance->quoteName('id') . " DESC" .
						 "\n LIMIT 1";
				$this->dbInstance->setQuery($query);
				$lastMessageID = $this->dbInstance->loadResult();

				if($lastMessageID) {
					$this->response['lastreadmessages'][$chatBoxID] = $lastMessageID;
				}
			}
		}
	}
	
	/**
	 * Manage users buddylist 
	 * 
	 * @access protected
	 * @return void
	 */
	protected function getBuddyList() { 
		$filter = InputFilter::getInstance();
		$buddyList = false;
		$userFieldName = $filter->clean($this->componentParams->get('usefullname', 'username'), 'word');
		$searchFilter = $this->getState('searchfilter');
		$forceRefresh = $this->getState('force_refresh') ? true : false;

		//Prendiamo il time per eventuale aggiornamento lista utenti buddylist
		$time = time();

		// Check if the meetings feature is enabled
		$meetingHash = null;
		if($this->meetingsFeatureEnabled) {
			$meetingHash = $this->getMeetingStateFromDB();
		}

		// A valid buddylist refresh timeout is detected, so go on
		if ((empty($this->sessionName['jchat_buddytime'])) || ($this->requestArray['initialize'] == 1 || ($forceRefresh)) ||
	       (!empty($this->sessionName['jchat_buddytime']) && ($time-$this->sessionName['jchat_buddytime'] > $this->componentParams->get('chatrefresh', 2) * 2.5))) {
			
       		$queryParts = array();
       		$queryParts['SELECT'] = '';
       		$queryPartsContacts['SELECT'] = '';
       		$queryParts['JOIN'] = '';
       		$accessLevels['JOIN'] = '';
       		$myUsersGroups['JOIN'] = '';
       		$queryPartsContacts['JOIN'] = '';
       		$additionalAND = null;
       		$accessLevelsAND = null;
       		$myUsersGroupsAND = null;
       		$meetingJoinedAND = null;
       		$livestreamingJoinedAND = null;
       		$this->response['my_avatar'] = JChatHelpersUsers::getAvatar($this->userSessionTable->session_id);
       		
	       	// LEFT JOIN per group chat status: validcontact = utente è un mio contatto, validowner = utente è owner del mio contatto, stabilisce lo stato/colore delle icone nella buddylist
       		// Group chat mode management
       		if($this->componentParams->get('groupchatmode', 'chatroom') == 'invite') {
				$queryPartsContacts['SELECT'] = "\n, fbc.contactid AS validcontact, fbch.ownerid AS validowner";
				$queryPartsContacts['JOIN'] = "\n LEFT JOIN #__jchat_public_sessionrelations AS fbc ON fbc.contactid = sess.session_id AND fbc.ownerid = " . $this->dbInstance->quote($this->userSessionTable->session_id) .
											  "\n LEFT JOIN #__jchat_public_sessionrelations AS fbch ON fbch.ownerid = sess.session_id AND fbch.contactid = " . $this->dbInstance->quote($this->userSessionTable->session_id);
       		}

       		// Logic for Guest users
	       	$guestMode = $this->componentParams->get('guestenabled', false);
	       	if($guestMode) {
	       		$logicJOIN = 'LEFT';
	       		$joinAND = 'AND u.block = 0';
	       		$logicAND = 'AND ' . $this->sessClientId;
	       		
	       		// If form join/meeting ensure to collect users having joined the chat component with a valid form username submission
	       		if($guestMode == 2) {
	       			$logicAND .= ' AND (!ISNULL(ccs.override_name) || sess.userid != 0)';
	       		}
	       	} else {
	       		$logicJOIN = 'INNER';
	       		$joinAND = 'AND u.block = 0';
	       		$logicAND = 'AND sess.guest = 0 AND ' . $this->sessClientId;
	       	}

			// Search filter for registered users / override name guests
       		if($searchFilter && $searchFilter != Text::_('COM_JCHAT_SEARCH')) {
       			$logicAND .= "\n AND(" .
			       			 "\n CASE" .
			       			 "\n WHEN (u.$userFieldName != '' OR (ccs.override_name != '' AND ccs.override_name IS NOT NULL))" .
			       			 "\n THEN (u.$userFieldName LIKE '%" . $searchFilter . "%'OR ccs.override_name LIKE '%" . $searchFilter . "%')" .
			       			 "\n ELSE sess.session_id != ''" .
			       			 "\n END)";
       		}
       		
			// Evaluate ONLY FRIENDS 3PD integration option
       		if($this->componentParams->get('3pdintegration', null) && $this->componentParams->get('filter_friendship', false)) {
       			$queryPartsFriends = $this->getQueryParts('buddylist');
       		} else {
       			$queryPartsFriends['JOIN'] = '';
       			$queryPartsFriends['WHERE'] = '';
       		}
       		
       		// Logic for banned users
       		if($this->componentParams->get('usersbanning', false)) {
       			$queryPartsBannedUsers['SELECT'] = "\n, bant.banned, ban.banning, bantids.banned AS bannedid, banids.banning AS banningid";
       			$queryPartsBannedUsers['JOIN'] = "\n LEFT JOIN #__jchat_banned_users AS bant ON bant.banned = sess.session_id" .
												 "\n AND bant.banning = " . $this->dbInstance->quote($this->userSessionTable->session_id) . // Users i'm banning to
												 "\n LEFT JOIN #__jchat_banned_users AS ban ON ban.banning = sess.session_id" .
												 "\n AND ban.banned = " . $this->dbInstance->quote($this->userSessionTable->session_id) .
												 "\n LEFT JOIN #__jchat_banned_users_ids AS bantids ON bantids.banned = u.id" .
												 "\n AND bantids.banning = " . $this->dbInstance->quote($this->myUser->id) . // Users i'm banning to
												 "\n LEFT JOIN #__jchat_banned_users_ids AS banids ON banids.banning = u.id" .
												 "\n AND banids.banned = " . $this->dbInstance->quote($this->myUser->id); // Users i'm banned from
       		} else {
       			$queryPartsBannedUsers['SELECT'] = '';
       			$queryPartsBannedUsers['JOIN'] = '';
       		}
       		
       		// Manage live support mode filtering 
       		list($queryParts['JOIN'], $additionalAND) = $this->getQueryLiveSupport ();

       		// Manage chat filtering by access levels
       		list($accessLevels['JOIN'], $accessLevelsAND) = $this->getQueryAccessLevels();

       		// Manage chat filtering by same users groups of the current user
       		if($this->componentParams->get('limit_my_users_groups', 0)) {
				list($myUsersGroups['JOIN'], $myUsersGroupsAND) = $this->getQueryMyUsersGroups();
       		}

       		// Limit the buddylist to users joining the same meeting
       		if($this->meetingsFeatureEnabled) {
				if($meetingHash) {
					// Found a meeting hash for this user? Show only users that are participants in the same meeting
					$meetingJoinedAND = "\n AND ccs.meeting_hash = " . $this->dbInstance->quote($meetingHash);
				} else {
					// Not found a meeting hash for this user? Exclude all users that are participants in a meeting
					$meetingJoinedAND = "\n AND ISNULL(ccs.meeting_hash)";
				}
       		}

       		// Limit the buddylist to users joining the live conference
       		if($this->livestreamingFeatureEnabled) {
				if($liveStreamingHash = $this->app->input->get('livestreaming_identifier')) {
					// Found a livestreaming hash? Show only users that are participants in the same live
					$livestreamingJoinedAND = "\n AND ccs.livestreaming_hash = " . $this->dbInstance->quote($liveStreamingHash);
				} else {
					$sql = "UPDATE #__jchat_sessionstatus SET" .
						   "\n " . $this->_db->quoteName('livestreaming_hash') . " = NULL " .
						   "\n WHERE " . $this->dbInstance->quoteName('sessionid') . " = " . $this->dbInstance->quote($this->userSessionTable->session_id);
					$this->dbInstance->setQuery($sql);
					$this->dbInstance->execute();

					// Not found a livestreaming hash? Exclude all users that are participants in a live
					$livestreamingJoinedAND = "\n AND ISNULL(ccs.livestreaming_hash)";
				}
       		}

	  		$sql = 	"SELECT u.id, u.$userFieldName, sess.time AS lastactivity, sess.session_id AS loggedin, ccs.status AS session_status, ccs.livestreaming_watch, su.status AS user_status," .
	  				"\n ccs.sessionid AS session_sessid, ccs.roomid AS session_roomid, ccs.override_name, ccs.geoip, su.roomid AS user_roomid," .
	  				"\n CASE WHEN su.skypeid IS NOT NULL THEN su.skypeid ELSE ccs.skypeid END AS skypeid," .
					"\n MAX( fb.sent) AS lastmessagetime" . 
					$queryPartsContacts['SELECT'] .
					$queryPartsBannedUsers['SELECT'] .
					"\n FROM #__session AS sess" .
	  				"\n $logicJOIN JOIN #__users AS u ON sess.userid = u.id $joinAND".
	  				$queryPartsFriends['JOIN'] .
					"\n LEFT JOIN #__jchat_sessionstatus AS ccs ON sess.session_id = ccs.sessionid".
					"\n LEFT JOIN #__jchat_userstatus AS su ON u.id = su.userid".
					"\n LEFT JOIN #__jchat AS fb ON sess.session_id = fb.from".
	  				$queryParts['JOIN'] .
	  				$accessLevels['JOIN'] .
	  				$myUsersGroups['JOIN'] .
	  				$queryPartsContacts['JOIN'] .
	  				$queryPartsBannedUsers['JOIN'] .
					"\n WHERE sess.session_id <> " . $this->dbInstance->quote($this->userSessionTable->session_id) .
					" $logicAND " .
					$additionalAND .
					$accessLevelsAND .
					$myUsersGroupsAND .
					$meetingJoinedAND .
					$livestreamingJoinedAND .
					$queryPartsFriends['WHERE'] .
					"\n AND ($time - sess.time) < " . (int)$this->componentParams->get('maxinactivitytime', 30) . 
					"\n AND (ISNULL(su.banstatus) OR su.banstatus = 0)" .
					"\n AND (ISNULL(ccs.banstatus) OR ccs.banstatus = 0)" .
					"\n GROUP BY sess.session_id" .
					"\n ORDER BY u.$userFieldName ASC";
	
			$this->dbInstance->setQuery($sql);
			$rows = $this->dbInstance->loadAssocList();
			
			// Duplicated helper array
			$duplicatedHelperArray = array();
			$removeDuplicatedUsers = $this->componentParams->get('remove_duplicated_users', 0);
			
			if(is_array($rows) && count($rows)) {
				foreach ($rows as $chat) { 
					// Bypass skipping users not joined nor as members and nor as form if guest mode is based on form join
					if($guestMode == 2 && !$chat['id'] && !$chat['session_sessid']) {continue;}
					
					// LOGIC OVERRIDES dello status utente
					$chat['status'] = $chat['user_status'] ? $chat['user_status'] : $chat['session_status'];
					if(!is_null($chat['status']) && $chat['status'] == 'offline') {
						$chat['status'] = 'offline'; 
					} elseif (!$this->componentParams->get('forceavailable', 0) && (($time-$chat['lastmessagetime']) > $this->componentParams->get('lastmessagetime', 60)) && ($chat['status'] == 'available' || is_null($chat['status'])) && $chat['lastmessagetime']) {
						// lo consideriamo offline anche se è inattivo da un periodo di tempo e lo status sarebbe available o neutro
						$chat['status'] = 'away|' . $this->convertToDaysHoursMins($time-$chat['lastmessagetime']); 
					} else {
						// Se il forceavailable è on si imposta a available per default se non già presente
						if(is_null($chat['status'])) {
							$chat['status'] = 'available'; 
						}
					}
					
					// Get current cycled user avatar
					$chat['avatar'] = JChatHelpersUsers::getAvatar($chat['loggedin']);
					
					// Guest name override: user field name -> override name -> auto generated
					if(!$chat[$userFieldName]) {
						if(!$chat['override_name']) {
							$chat[$userFieldName] = JChatHelpersUsers::generateRandomGuestNameSuffix($chat['loggedin'], $this->componentParams);
						} else {
							$chat[$userFieldName] = $chat['override_name'];
						}
					}
					
					// Search filter for auto generated guest names
					if($searchFilter && $searchFilter != Text::_('COM_JCHAT_SEARCH')) {
						if(stripos($chat[$userFieldName], $searchFilter) === false) {
							continue;
						}
					}
					
					// If there is a user id keep track of duplicated records, make sure that only the max lastactivity one is finally included in the buddylist
					if($removeDuplicatedUsers && @$chat['id']) {
						// Skip themselves users just another instance of the same
						if($chat['id'] == $this->myUser->id) {
							continue;
						}
						// Is there a user already included in the duplicatedHelperArray?
						if(array_key_exists($chat['id'], $duplicatedHelperArray)) {
							$existingLastActivity = $duplicatedHelperArray[$chat['id']]['lastactivity'];
							$currentLastActivity = $chat['lastactivity'];
							// Compare the existing and current lastactivity and if there is a greater one skip the record
							if($currentLastActivity < $existingLastActivity) {
								continue;
							} elseif($currentLastActivity > $existingLastActivity) {
								// If instead there is a greater one unset the existing one from the buddylist
								$existingSessionId =  $duplicatedHelperArray[$chat['id']]['session_id'];
								unset($buddyList[$existingSessionId]);
								// Then update the duplicatedHelperArray and go on to add this one to the buddylist
								$duplicatedHelperArray[$chat['id']] = array('lastactivity'=>$chat['lastactivity'], 'session_id'=>$chat['loggedin']);
							}
						} else {
							// Just add a record to the duplicatedHelperArray to keep track at next iteration
							$duplicatedHelperArray[$chat['id']] = array('lastactivity'=>$chat['lastactivity'], 'session_id'=>$chat['loggedin']);
						}
					}
					
					$buddyList[$chat['loggedin']] = array('id' => $chat['loggedin'],
														  'name' => $chat[$userFieldName],
														  'avatar' => $chat['avatar'],
														  'status' => $chat['status'],
														  'time' => $chat['lastactivity'], 
														  'iscontact' => @$chat['validcontact'],
														  'isowner' => @$chat['validowner'],
														  'isbanned' => @(bool)($chat['banned'] || $chat['bannedid']),
														  'imbanned' => @(bool)($chat['banning'] || $chat['banningid']),
														  'skypeid' => $chat['skypeid'],
														  'isguest' => @!$chat['id'],
														  'loggedid' => @$chat['id'],
														  'iswatcher' => @$chat['livestreaming_watch'],
														  'hasroomid' => @(bool)($chat['session_roomid'] || $chat['user_roomid']),
														  'lastmessagetime' => $chat['lastmessagetime'],
														  'geoip' => @$chat['geoip'],
														  'profilelink' => $this->getUserProfileLink($chat['id'], $chat[$userFieldName], $this->componentParams)
					 );
			 	} 
			}
			
			// Regular case, identical assignment and no additional queries
			$publicBuddyList = $buddyList;
			
			// Exclude the public buddylist from the friendship filtering so override the $publicBuddyList
			if($this->componentParams->get('3pdintegration', null) && $this->componentParams->get('filter_friendship', false) == 2) {
				$publicSql = "SELECT sess.session_id AS loggedin, u.$userFieldName" .
							 "\n FROM #__session AS sess" .
							 "\n $logicJOIN JOIN #__users AS u ON sess.userid = u.id $joinAND".
							 "\n LEFT JOIN #__jchat_sessionstatus AS ccs ON sess.session_id = ccs.sessionid".
							 "\n LEFT JOIN #__jchat_userstatus AS su ON u.id = su.userid".
							 $queryParts['JOIN'] .
							 $accessLevels['JOIN'] .
							 $myUsersGroups['JOIN'] .
							 "\n WHERE sess.session_id <> " . $this->dbInstance->quote($this->userSessionTable->session_id) .
							 " $logicAND " .
							 $additionalAND .
							 $accessLevelsAND .
							 $myUsersGroupsAND .
							 "\n AND ($time - sess.time) < " . (int)$this->componentParams->get('maxinactivitytime', 30) .
							 "\n AND (ISNULL(su.banstatus) OR su.banstatus = 0)" .
							 "\n AND (ISNULL(ccs.banstatus) OR ccs.banstatus = 0)" .
							 "\n GROUP BY sess.session_id" .
							 "\n ORDER BY u.$userFieldName ASC";
				
				$this->dbInstance->setQuery($publicSql);
				$publicRows = $this->dbInstance->loadAssocList();
				
				if(is_array($publicRows) && count($publicRows)) {
					foreach ($publicRows as $publicChat) {
						$publicBuddyList[$publicChat['loggedin']] = array(
									'id' => $publicChat['loggedin'],
									'name' => $publicChat[$userFieldName]
								);
					}
				}
			}
			
		 	//Riaggiorniamo il time in sessione dell'ultimo refresh lista utenti
			$this->sessionName['jchat_buddytime'] = $time;
	
			if (!empty($buddyList)) {
				$this->response['buddylist'] = $buddyList;
					// Iniettiamo anche un array di ID crudo
				if(is_array($buddyList) && count($buddyList)) {
					foreach ($this->response['buddylist'] as $value) {
						$this->response['buddylist_ids'][] = $value['id'];
					} 
				}
			} else {
				$this->response['buddylist'] = false;
			}
		    // Top scope JS side - Evaluate if user is logged in and has a username from db
		    if(!$this->myUser->$userFieldName) {
				$this->response['my_username'] = JChatHelpersUsers::generateRandomGuestNameSuffix($this->userSessionTable->session_id, $this->componentParams);
		    } else {
				$this->response['my_username'] = $this->myUser->$userFieldName;  
		    }
		    $this->response['my_email'] = $this->myUser->email;
		    
		    // Refresh chatrooms list with users number currently joined
		    $this->getChatRoomsList($publicBuddyList);
		    
		    // Generate a chatroom users list to show under tooltip
		    $this->getMyChatRoomUsers($publicBuddyList);

		    // Refresh latest read message for currently opened chatboxes
		    if($this->componentParams->get('lastreadmessage', true)) {
				$this->getLatestReadMessage();
		    }
		    
		    // If messages deletion is enabled go on to retrieve and injetc them to clients
		    if($this->componentParams->get('messages_deletion', false)) {
		    	$this->fetchDeletedMessages();
		    }
		}
	}
	
	/**
	 * Fetch private messages
	 * 
	 * @access protected
	 * @return void
	 */
	protected function fetchMessages() {
		$toOpenChatBoxesArray = array();
		$toOpenChatBoxes = null;
		$languageTranslatorEnabled = false;
		$openChatBoxesString = isset($this->sessionName ['jchat_sessionvars']['activeChatboxes']) ? $this->sessionName ['jchat_sessionvars']['activeChatboxes'] : null ;
		if($openChatBoxesString) {
			$toOpenChatBoxes = array();
			$chunks = explode(',', $openChatBoxesString);
			foreach ($chunks as $chunk) {
				$toOpenChatBoxes[] = @$this->dbInstance->quote(array_shift(explode('|', $chunk)));
			}
			$toOpenChatBoxesArray = $toOpenChatBoxes;
			if($toOpenChatBoxes) {
				$toOpenChatBoxes = implode (',', $toOpenChatBoxes);
			}
		}
		$initialize = $this->getState('initialize');
		$lastNewMessageID = null;
		$lastReceivedMsgID = $this->getState('last_received_msg_id');
		 
		$filter = InputFilter::getInstance();
		$userFieldName = $filter->clean($this->componentParams->get('usefullname', 'username'), 'word');
		 
		$queryParts = array();
		$queryParts['SELECT'] = '';
		$queryParts['JOIN'] = '';
		$queryParts['WHERE'] = '';
		
		// Logic for banned users
		if($this->componentParams->get('usersbanning', false)) {
			$queryParts['WHERE'] = "\n AND cchat.from NOT IN(" .
								   "\n SELECT " . $this->dbInstance->quoteName('banned') .
								   "\n FROM " . $this->dbInstance->quoteName('#__jchat_banned_users') .
								   "\n WHERE " . $this->dbInstance->quoteName('banning') . " = " . $this->dbInstance->quote($this->userSessionTable->session_id) . ")" .
								   "\n AND cchat.fromuser NOT IN(" .
								   "\n SELECT " . $this->dbInstance->quoteName('banned') .
								   "\n FROM " . $this->dbInstance->quoteName('#__jchat_banned_users_ids') .
								   "\n WHERE " . $this->dbInstance->quoteName('banning') . " = " . $this->dbInstance->quote($this->myUser->id) . ")";
		}

		// Multitabs download messages mode
		$downloadNewMsgsMode = " AND cchat.read != 1";
		if($this->componentParams->get('download_msgs_multitabs_mode', false) && $lastReceivedMsgID > 0) {
			$downloadNewMsgsMode = " AND cchat.id > " . (int)$lastReceivedMsgID;
		}

		$sql = 	"SELECT cchat.id, cchat.from, cchat.to, cchat.message," .
				"\n cchat.sent, cchat.read, cchat.type, cchat.status, u.id AS userid, sess.session_id AS loggedin, u.$userFieldName AS fromuser" . $queryParts['SELECT'] .
				"\n FROM #__jchat AS cchat" .
				"\n INNER JOIN #__session AS sess ON cchat.from = sess.session_id" .
				"\n LEFT JOIN #__users AS u ON sess.userid = u.id" .
				$queryParts['JOIN'] .
				"\n WHERE (cchat.to = ". $this->dbInstance->quote($this->userSessionTable->session_id) . $downloadNewMsgsMode . $queryParts['WHERE'] . ")";
				if (!$initialize && $toOpenChatBoxes && $lastReceivedMsgID > 0) {
					$sql .= "\n OR (cchat.from = ". $this->dbInstance->quote($this->userSessionTable->session_id) . " AND cchat.to IN ( " . $toOpenChatBoxes . " ) " .
							"\n AND cchat.id > $lastReceivedMsgID AND cchat.type='file' AND cchat.clientdeleted = 0)";
				}
				$sql .="\n ORDER BY cchat.id";
	 
		$this->dbInstance->setQuery($sql);
	 	$rows = $this->dbInstance->loadAssocList();

		// Cycle old messages in session and check if avatars still exist or have been deleted in the meanwhile
	 	if($this->componentParams->get('advanced_avatars_mgmt', false)) {
		 	$this->refreshSessionMessagesAvatars($this->messages);
	 	}

	 	if(is_array($rows) && count($rows)) {
	 		// Language translation of the message
	 		if($this->componentParams->get('language_translation_enabled', 0)) {
	 			if(version_compare(PHP_VERSION, '5.4', '>=')) {
	 				$languageTranslatorEnabled = true;
	 				// Composer autoloader
	 				require_once JPATH_COMPONENT_ADMINISTRATOR . '/Framework/composer/autoload_real.php';
	 			}
	 		}
	 		
		 	// Add new received messages on stream if any
		 	foreach ($rows as $chatmessage) {
		 		$self = 0;
				$old = 0;
				if ($chatmessage['from'] == $this->userSessionTable->session_id) {
					$chatmessage['from'] = $chatmessage['to'];
					$self = 1;
					$old = 1;
				}
					
				// Translate incoming messages based on current language translator settings for this chatbox user. Discard self messages.
				if($languageTranslatorEnabled && isset($this->sessionName ['jchat_sessionvars']['langvars']->{$chatmessage['from']})) {
					// Get post informations and ensure that the language translation request is valid
					$sourceLanguage = $this->sessionName ['jchat_sessionvars']['langvars']->{$chatmessage['from']}->sourcelanguage;
					$targetLanguage =  $this->sessionName ['jchat_sessionvars']['langvars']->{$chatmessage['from']}->targetlanguage;
					$langSwitchEnabled =  $this->sessionName ['jchat_sessionvars']['langvars']->{$chatmessage['from']}->translatorstatus;

					if($langSwitchEnabled && ($sourceLanguage != $targetLanguage)) {
						try {
							$translatedMessage = \Stichoza\GoogleTranslate\TranslateClient::translate($targetLanguage, $sourceLanguage, $chatmessage['message']);
							$chatmessage['message'] = $translatedMessage ? $translatedMessage : $chatmessage['message'];
						} catch(\Exception $e) {/*Do nothing, leave the original text message unaltered*/}
					}
				}
				
				// Get user avatar on the fly for
				$chatmessage['avatar'] = JChatHelpersUsers::getAvatar($chatmessage['loggedin']);
				
				// Get profile link
				$chatmessage['profilelink'] = $this->getUserProfileLink($chatmessage['userid'], $chatmessage['fromuser'], $this->componentParams);
				
				// Guest name override: user field name -> override name -> auto generated
				if(!$chatmessage['fromuser']) {
					$chatmessage['fromuser'] = JChatHelpersUsers::generateRandomGuestNameSuffix($chatmessage['loggedin'], $this->componentParams);
				}
				
				$messageUserTime = HTMLHelper::_('date', $chatmessage['sent'], Text::_('DATE_FORMAT_LC2'));
				$this->messages[] = array( 'id' => $chatmessage['id'],
										   'from' => $chatmessage['from'], 
										   'fromuser' => @$chatmessage['fromuser'],
										   'avatar' => $chatmessage['avatar'],
										   'profilelink' => @$chatmessage['profilelink'],
										   'message' => stripslashes($chatmessage['message']),
										   'type' => @$chatmessage['type'],
										   'status' => @$chatmessage['status'],
										   'time' => $messageUserTime,
										   'self' => $self,
										   'old' => $old,
										   'idregistered' => $chatmessage['userid']);
				
				// Store new streamed messages into session if not own messages, old messages and already read
				if ($self == 0 && $old == 0 && $chatmessage['read'] != 1) {
					$this->sessionName['jchat_user_'.$chatmessage['from']][$chatmessage['id']] = array('id' => $chatmessage['id'],
																							  'from' => $chatmessage['from'],
																							  'fromuser' => @$chatmessage['fromuser'],
																							  'avatar' => $chatmessage['avatar'],
																							  'userid' => @$chatmessage['loggedin'],
																							  'profilelink' => @$chatmessage['profilelink'],
																							  'message' => stripslashes($chatmessage['message']),
																							  'type' => @$chatmessage['type'],
																							  'status' => @$chatmessage['status'],
																							  'time' => $messageUserTime,
																							  'self' => 0,
																							  'old' => 1);
				}
				$lastNewMessageID = $chatmessage['id'];
		 	}
	 	}
	 
	 	// Now update status of all messages received till latest new message as read status
		if ($lastNewMessageID) {
			$sql = "UPDATE #__jchat SET `read` = '1' WHERE `to` = " .
				    $this->dbInstance->quote($this->userSessionTable->session_id) . " and `id` <= " . $this->dbInstance->quote($lastNewMessageID); 
				 
			$this->dbInstance->setQuery($sql); 
			$this->dbInstance->execute();
		}
		
		// Recover old messages from history for opened chatboxes
		if(($historyAutoloadValue = $this->componentParams->get('history_autoload', null)) && !empty($toOpenChatBoxesArray) && $this->myUser->id) {
			foreach ($toOpenChatBoxesArray as $chatBoxOpened) {
				$chatBoxOpened = trim($chatBoxOpened, "'");
				// Only recover old messages if there is nothing from this user in the current session
				if(!isset($this->sessionName['jchat_user_'.$chatBoxOpened])) {
					$sessionSql = 	"SELECT" .
									"\n " . $this->dbInstance->quoteName('userid') .
									"\n FROM #__session" .
									"\n WHERE" .
									"\n " . $this->dbInstance->quoteName('session_id') . " = " . $this->dbInstance->quote($chatBoxOpened) .
									"\n ORDER BY " . $this->dbInstance->quoteName('time') . " DESC" .
									"\n LIMIT 1";
					$this->dbInstance->setQuery($sessionSql);
					$userLoggedId = $this->dbInstance->loadResult();
					if($userLoggedId) {
						$this->fetchHistoryMessages($userLoggedId, $chatBoxOpened, $historyAutoloadValue, null);
					}
				}
			}
		}
		
		// Do autorefresh realtime for messages type=file status
		$this->refreshMsgFileSessionStatus($this->response);
	}
	
	
	/**
	 * Fetch public group messages
	 * 
	 * @access protected
	 * @return void
	 */
	protected function fetchWallMessages() {
		$filter = InputFilter::getInstance();
		$userFieldName = $filter->clean($this->componentParams->get('usefullname', 'username'), 'word');
		
		// Check if the meetings feature is enabled
		$meetingHash = null;
		if($this->meetingsFeatureEnabled) {
			$meetingHash = $this->getMeetingStateFromDB();
		}
		
		$wallHistory = $this->getState('wallhistory', false);
		$chatRoomDelay = 0;
		$queryParts = array();
		$logicAND = null;
		$myUsersGroupsAND = null;
		$queryPartsMeeting['JOIN'] = '';
		$queryPartsMeeting['WHERE'] = '';
		$queryParts['SELECT'] = '';
		$queryParts['JOIN'] = '';
		$myUsersGroups['JOIN'] = '';
		$queryParts['WHERE'] = '';
		$joinSession = 'INNER';
		$excludeMyMessageAND = " AND cchat.from != " . $this->dbInstance->quote($this->userSessionTable->session_id);
		$excludeReadMessageAND = "\n AND cchat.id NOT IN (SELECT messageid FROM #__jchat_public_readmessages WHERE sessionid = " . $this->dbInstance->quote($this->userSessionTable->session_id) . ")";
		
		// Check additional timing for just joined to chatroom users
		if(isset($this->sessionName['jchat_justjoined']) && $this->componentParams->get('chatrooms_latest', 1)) {
			$chatRoomDelay = $this->componentParams->get('chatrooms_latest_interval', 120);
			unset($this->sessionName['jchat_justjoined']);
		}
		
		// Add special chat room delay if wall history is requested
		if($wallHistory) {
			$chatRoomDelay = $this->componentParams->get('wall_history_delay', 1) * 60 * 60 * 24;
			$joinSession = 'LEFT';
			$excludeMyMessageAND = null;
			$excludeReadMessageAND = null;
		}
		
		// Multitabs download messages mode
		if($this->componentParams->get('download_msgs_multitabs_mode', false)) {
			$excludeReadMessageAND = null;
		}
		
		// Reserved to chatroom filtering
		$queryParts['CHATROOM_AND'] = '';
		
		// Group chat mode management
		if($this->componentParams->get('groupchatmode', 'chatroom') == 'invite') {
			$logicAND = " AND cchat.from IN (SELECT contactid FROM #__jchat_public_sessionrelations" .
						"\n WHERE ownerid = " . $this->dbInstance->quote($this->userSessionTable->session_id) . ")" .
						" AND cchat.from IN (SELECT ownerid FROM #__jchat_public_sessionrelations" .
						"\n WHERE contactid = " . $this->dbInstance->quote($this->userSessionTable->session_id) . ")";
		} elseif($this->componentParams->get('groupchatmode', 'chatroom') == 'chatroom') {
			// If detected a valid user chatroom that belongs to, proceed with filtering of incoming messages from other users in same chatroom
			if($this->myChatRoom) {
				$queryParts['CHATROOM_AND'] = "\n AND cchat.sentroomid = " . (int)$this->myChatRoom;
				// Filter also by current users in this chatroom, let download chatroom messages only from user still in that chatroom
				if($this->componentParams->get('chatrooms_messages_stillinroom', 0)) {
					$queryParts['CHATROOM_AND'] .=  "\n AND (cchat.from IN(SELECT DISTINCT sessionstate.sessionid" .
													"\n FROM #__jchat_sessionstatus AS sessionstate" .
													"\n WHERE sessionstate.roomid = " . (int)$this->myChatRoom . ")" .
													"\n OR cchat.from IN(SELECT DISTINCT session.session_id" .
													"\n FROM #__session AS session" .
													"\n INNER JOIN #__jchat_userstatus AS userstate" .
													"\n ON session.userid = userstate.userid" .
													"\n WHERE " . $this->sessionClientId . " AND userstate.roomid = " . (int)$this->myChatRoom . "))";
				}
			} else {
				// If user doesn't belong to any chatroom, proceed with filtering of incoming messages from other users in no one chatrooms
				$queryParts['CHATROOM_AND'] = "\n AND (cchat.sentroomid = 0 OR cchat.sentroomid IS NULL)";
				// Filter also by current users in no chatroom, let download messages only from user still in no chatrooms
				if($this->componentParams->get('chatrooms_messages_stillinroom', 0)) {
					$queryParts['CHATROOM_AND'] .= "\n AND (cchat.from NOT IN(SELECT DISTINCT sessionstate.sessionid" .
												   "\n FROM #__jchat_sessionstatus AS sessionstate" .
												   "\n WHERE sessionstate.roomid > 0)" .
												   "\n AND cchat.from NOT IN(SELECT DISTINCT session.session_id" .
												   "\n FROM #__session AS session" .
												   "\n INNER JOIN #__jchat_userstatus AS userstate" .
												   "\n ON session.userid = userstate.userid" .
												   "\n WHERE " . $this->sessionClientId . " AND userstate.roomid > 0))";
				}
			}
			
			// Limit stream of messages to live support admin if active
			if($this->componentParams->get('affect_public_chat', 1)) {
				list($queryParts['JOIN'], $logicAND) = $this->getQueryLiveSupport ();
			}
			
			// Manage chat filtering by same users groups of the current user
			if($this->componentParams->get('limit_my_users_groups', 0)) {
				list($myUsersGroups['JOIN'], $myUsersGroupsAND) = $this->getQueryMyUsersGroups();
			}
		} else {
			// Limit stream of messages to live support admin if active
			if($this->componentParams->get('affect_public_chat', 1)) {
				list($queryParts['JOIN'], $logicAND) = $this->getQueryLiveSupport ();
			}
			
			// Manage chat filtering by same users groups of the current user
			if($this->componentParams->get('limit_my_users_groups', 0)) {
				list($myUsersGroups['JOIN'], $myUsersGroupsAND) = $this->getQueryMyUsersGroups();
			}
		}

		// Limit the buddylist to users joining the same meeting
		if($this->meetingsFeatureEnabled) {
			if($meetingHash) {
				// Found a meeting hash for this user? Get only public messages from users that are participants in the same meeting
				$queryPartsMeeting['JOIN'] = "\n LEFT JOIN #__jchat_sessionstatus AS ccs ON cchat.from = ccs.sessionid";
				$queryPartsMeeting['WHERE'] = "\n AND ccs.meeting_hash = " . $this->dbInstance->quote($meetingHash);
			} else {
				// Not found a meeting hash for this user? Exclude all messages from users that are participants in a meeting
				$queryPartsMeeting['JOIN'] = "\n LEFT JOIN #__jchat_sessionstatus AS ccs ON cchat.from = ccs.sessionid";
				$queryPartsMeeting['WHERE'] = "\n AND ISNULL(ccs.meeting_hash)";
			}
		}
		
		// Evaluate ONLY FRIENDS 3PD integration option
		if($this->componentParams->get('3pdintegration', null) && $this->componentParams->get('filter_friendship', false) == 1) {
			$queryPartFriends = $this->getQueryParts('groupmessages');
			$logicAND .= $queryPartFriends['AND'];
		}
		
		// Logic for banned users
		if($this->componentParams->get('usersbanning', false) && $this->componentParams->get('usersbanning_mode', 'private') == 'private_public') {
			$queryParts['WHERE'] =  "\n AND cchat.from NOT IN(" .
									"\n SELECT " . $this->dbInstance->quoteName('banned') .
									"\n FROM " . $this->dbInstance->quoteName('#__jchat_banned_users') .
									"\n WHERE " . $this->dbInstance->quoteName('banning') . " = " . $this->dbInstance->quote($this->userSessionTable->session_id) . ")" .
									"\n AND cchat.fromuser NOT IN(" .
									"\n SELECT " . $this->dbInstance->quoteName('banned') .
									"\n FROM " . $this->dbInstance->quoteName('#__jchat_banned_users_ids') .
									"\n WHERE " . $this->dbInstance->quoteName('banning') . " = " . $this->dbInstance->quote($this->myUser->id) . ")";
		}
		
		$sql = "SELECT DISTINCT cchat.id, cchat.from, cchat.from AS loggedin, cchat.to, cchat.message," .
				"\n cchat.sent, cchat.read, u.id AS userid, u.$userFieldName AS fromuser, cchat.actualfrom" . $queryParts['SELECT'] .
				"\n FROM #__jchat AS cchat" .
				"\n " . $joinSession .  " JOIN #__session AS sess ON cchat.from = sess.session_id" .
				"\n LEFT JOIN #__users AS u ON sess.userid = u.id" .
				$queryParts['JOIN'] .
				$myUsersGroups['JOIN'] .
				$queryPartsMeeting['JOIN'] .
				"\n WHERE cchat.to = " . $this->dbInstance->quote(0) .
				$excludeMyMessageAND .
				$queryPartsMeeting['WHERE'] .
				$logicAND .
				$myUsersGroupsAND .
				$queryParts['CHATROOM_AND'] .
				"\n AND cchat.sent > " . (time() - $this->componentParams->get('maxtimeinterval_groupmessages', 12) - $chatRoomDelay) .
				$excludeReadMessageAND .
				$queryParts['WHERE'] .
				"\n ORDER BY cchat.id"; 
		$this->dbInstance->setQuery($sql);
	 	$rows = $this->dbInstance->loadAssocList();
	  
	 	// Cycle old messages in session and check if avatars still exist or have been deleted in the meanwhile
	 	if($this->componentParams->get('advanced_avatars_mgmt', false)) {
		 	$this->refreshSessionMessagesAvatars($this->wallMessages, 'wall');
	 	}
	 	
	 	if(is_array($rows) && count($rows)) {
		 	// Add new received messages on stream if any
		 	foreach ($rows as $chatmessage) {
		 		$self = 0;
				$old = 0; 

				if ($chatmessage['from'] == $this->userSessionTable->session_id) {
					$self = 1;
				}
				
				// Get user avatar
				$chatmessage['avatar'] = JChatHelpersUsers::getAvatar($chatmessage['loggedin']);
				
				// Get profile link
				$chatmessage['profilelink'] = $this->getUserProfileLink($chatmessage['userid'], $chatmessage['fromuser'], $this->componentParams);
				
				// Guest name override: user field name -> override name -> auto generated
				if(!$chatmessage['fromuser']) {
					if($chatmessage['actualfrom']) {
						$chatmessage['fromuser'] = $chatmessage['actualfrom'];
					} else {
						$chatmessage['fromuser'] = JChatHelpersUsers::generateRandomGuestNameSuffix($chatmessage['loggedin'], $this->componentParams);
					}
				}
				
				$messageUserTime = HTMLHelper::_('date', $chatmessage['sent'], Text::_('DATE_FORMAT_LC2'));

				$this->wallMessages[] = array(  'id' => $chatmessage['id'],
										 		'from' => 'wall',
										 		'fromuserid' => @$chatmessage['from'], 
										 		'fromuser' => @$chatmessage['fromuser'],
												'avatar' => @$chatmessage['avatar'],
										 		'profilelink' => @$chatmessage['profilelink'],
										 		'message' => stripslashes($chatmessage['message']),
												'time' => $messageUserTime,
										 		'self' => $self,
										 		'old' => $old); 
				
				$this->sessionName['jchat_user_wall'][$chatmessage['id']] = array ( 
																		   'id' => $chatmessage['id'],
																		   'from' => 'wall',
																		   'fromuserid' => @$chatmessage['from'],
																		   'fromuser' => @$chatmessage['fromuser'],
																		   'avatar' => @$chatmessage['avatar'],
																		   'userid' => @$chatmessage['loggedin'],
																		   'profilelink' => @$chatmessage['profilelink'],
																		   'message' => stripslashes($chatmessage['message']),
																		   'time' => $messageUserTime,
																		   'self' => $self,
																		   'old' => 1);
				// Store new public streamed messages into session
				$sql = "INSERT IGNORE INTO #__jchat_public_readmessages VALUES(" . (int)$chatmessage['id'] . "," . $this->dbInstance->quote($this->userSessionTable->session_id) . ")"; 
				$this->dbInstance->setQuery($sql); 
				$this->dbInstance->execute();  
		 	} 
	 	}
	}
	
	/**
	 * Get typing status for chatbox users that are writing to this me user
	 *
	 * @access protected
	 * @return void
	 */
	protected function fetchTypingStatus() {
		// Load all typing users to my session id, currently active, if none save bandwidth and avoid response
		$query = "SELECT " .
				 $this->dbInstance->quoteName('sessionid') . "," .
				 $this->dbInstance->quoteName('typing_to') .
				 "\n FROM " . $this->dbInstance->quoteName('#__jchat_sessionstatus') .
				 "\n WHERE" .
				 "\n " . $this->dbInstance->quoteName('typing') . " = 1" .
				 "\n AND " . $this->dbInstance->quoteName('typing_to') . " = " . $this->dbInstance->quote($this->userSessionTable->session_id);
		$this->dbInstance->setQuery($query);
		$results = $this->dbInstance->loadAssocList('sessionid');
	
		$this->response['typing_status'] = $results;
	}
	
	/**
	 * Get deleted messages to this me user or to the generic wall group chat
	 *
	 * @access protected
	 * @return void
	 */
	protected function fetchDeletedMessages() {
		// Load all deleted messages that are sent to me this session user or to the generic wall and multiple users
		$query = "SELECT " .
				 $this->dbInstance->quoteName('messageid') . "," .
				 $this->dbInstance->quoteName('from') . "," .
				 $this->dbInstance->quoteName('to') .
				 "\n FROM " . $this->dbInstance->quoteName('#__jchat_deleted_messages') .
				 "\n WHERE" .
				 "\n (" . $this->dbInstance->quoteName('to') . " = " . $this->dbInstance->quote($this->userSessionTable->session_id) .
				 "\n OR " . $this->dbInstance->quoteName('to') . " = 'wall')" ;
		$this->dbInstance->setQuery($query);
		$results = $this->dbInstance->loadAssocList('messageid');
		
		// The total amount of messages are injected in the JS client domain
		$this->response['deleted_messages'] = $results;
		
		// Go on to delete private messages from the current SESSION for this user
		if(count($results)) {
			$resultIds = array(-1); // This -1 is done if there are no private messages but only public wall messages
			foreach ($results as $messageID=>$conversation) {
				if (isset($this->sessionName['jchat_user_'.$conversation['from']])) {
					// Delete the private user message from the session
					if(is_array($this->sessionName['jchat_user_'.$conversation['from']]) && isset($this->sessionName['jchat_user_'.$conversation['from']][$messageID])) {
						// Unset this message from the session array
						unset($this->sessionName['jchat_user_'.$conversation['from']][$messageID]);
						$resultIds[] = $messageID;
					}
				}
				if ($conversation['to'] == 'wall') {
					// Delete the private user message from the session
					if(is_array($this->sessionName['jchat_user_wall']) && isset($this->sessionName['jchat_user_wall'][$messageID])) {
						// Unset this message from the session array
						unset($this->sessionName['jchat_user_wall'][$messageID]);
					}
				}
			}
			
			// Delete past messages sent to single users that is not wall, the wall is deleted by expiring time
			$time = time();
			$query = "DELETE FROM #__jchat_deleted_messages" .
					 "\n WHERE (" .
					 $this->dbInstance->quoteName('messageid') . " IN (" . implode(',', $resultIds) . ")" .
					 "\n OR (" .
					 "\n ($time - sent) > " . (int)$this->componentParams->get('maxinactivitytime', 30) .
					 "\n AND " . $this->dbInstance->quoteName('from') . " != " . $this->dbInstance->quote($this->userSessionTable->session_id) .
					 "\n AND " . $this->dbInstance->quoteName('to') . "= 'wall'))";
			$this->dbInstance->setQuery($query);
			$this->dbInstance->execute();
		}
	}
	
	/**
	 * Listening for WebRTC signaling channel and incoming
	 * video calls from remote peer
	 * This signaling channel response is bypassed when peer is a caller
	 * that has received an answer, or a callee that has received an offer
	 * The signaling channel query is ALWAYS executed to monitor call changes
	 *
	 * @access protected
	 * @return void
	 */
	protected function fetchSignalingChannel() {
		//  Listen for signaling channel, listen for both SDP messages or ICE candidate
		$query = "SELECT *" .
				 "\n FROM #__jchat_webrtc" .
				 "\n WHERE " . $this->dbInstance->quoteName('peer2') .  " = " .
				 $this->dbInstance->quote($this->userSessionTable->session_id);
		$dataObject = $this->dbInstance->setQuery($query)->loadObject();

		// Check if the peer1 is a valida caller. The callee could have close the call in the meanwhile
		$callerQuery = "SELECT " . $this->dbInstance->quoteName('peer1') .
					   "\n FROM #__jchat_webrtc" .
					   "\n WHERE " . $this->dbInstance->quoteName('peer1') .  " = " .
					   $this->dbInstance->quote($this->userSessionTable->session_id);
		$checkCallerPeerState = $this->dbInstance->setQuery($callerQuery)->loadResult();

		// Security safe: are both data for ICE frameword and sdp correctly available? Otherwise posticipate
		if(!is_object($dataObject) || !$dataObject->sdp || !$dataObject->icecandidate || $dataObject->icecandidate == '[]') {
			$this->response['webrtc_signaling_channel']['call_status'] = 0;
			$this->response['webrtc_signaling_channel']['caller_peer_state'] = $checkCallerPeerState;

			// No peer data found this means that the call is not valid or ended
			if(isset($this->sessionName['jchat_webrtc_datareceived'])) {
				unset($this->sessionName['jchat_webrtc_datareceived']);
			}
			return;
		}

		// Are we dealing with a valid incoming call or a peer response?
		// This is the first select query, so answer with full SDP and ICE data
		if(!isset($this->sessionName['jchat_webrtc_datareceived'])) {
			$dataObject->call_status = 1;
			$dataObject->caller_peer_state = $checkCallerPeerState;
			$this->response['webrtc_signaling_channel'] = $dataObject;

			// Store session for this peer as data received, this will stop full response data sending only call status
			$this->sessionName['jchat_webrtc_datareceived'] = true;
		} else {
			$this->response['webrtc_signaling_channel']['call_status'] = 1;
			$this->response['webrtc_signaling_channel']['caller_peer_state'] = $checkCallerPeerState;
			$this->response['webrtc_signaling_channel']['videocam'] = $dataObject->videocam;
		}
	}
	
	/**
	 * Listening for WebRTC signaling channel and incoming
	 * video calls from remote peer during a conference and multi users
	 * This signaling channel response is bypassed when peer is a caller
	 * that has received an answer, or a callee that has received an offer
	 * The signaling channel query is ALWAYS executed to monitor call changes
	 *
	 * @access protected
	 * @return void
	 */
	protected function fetchConferenceSignalingChannel() {
		// Listen for signaling channel, listen for both SDP messages or ICE candidate
		$query = "SELECT *" .
				 "\n FROM #__jchat_webrtc_conference" .
				 "\n WHERE " . $this->dbInstance->quoteName('peer2') . " = " .
				 $this->dbInstance->quote($this->userSessionTable->session_id);
		$dataObjects = $this->dbInstance->setQuery($query)->loadObjectList();

		// Check if valid channels have been retrieved
		if(!empty($dataObjects)) {
			foreach ($dataObjects as $numSessionIndex=>$dataObject) {
				// Check if the peer1 is a valid caller. The callee could have close the call in the meanwhile
				$callerQuery = 	"SELECT " . $this->dbInstance->quoteName('peer1') .
								"\n FROM #__jchat_webrtc_conference" .
								"\n WHERE " . $this->dbInstance->quoteName('peer1') . " = " . $this->dbInstance->quote($this->userSessionTable->session_id) .
								"\n AND " . $this->dbInstance->quoteName('peer2') . " = " . $this->dbInstance->quote($dataObject->peer1);
				$checkCallerPeerState = $this->dbInstance->setQuery($callerQuery)->loadResult();

				// Estabilish a session exchange hash as unique identifier
				$sessionHash = md5($dataObject->peer1 . $dataObject->peer2);

				// Security safe: are both data for ICE frameword and sdp correctly available? Otherwise posticipate
				if(!is_object($dataObject) || !$dataObject->sdp || !$dataObject->icecandidate || $dataObject->icecandidate == '[]') {
					$dataObject->call_status = 0;
					$dataObject->caller_peer_state = $checkCallerPeerState;
					$this->response['webrtc_conference_signaling_channel'][] = $dataObject;

					// No peer data found this means that the call is not valid or ended
					if(isset($this->sessionName['jchat_conference_webrtc_datareceived'][$sessionHash])) {
						unset($this->sessionName['jchat_conference_webrtc_datareceived'][$sessionHash]);
					}
					return;
				}

				// Are we dealing with a valid incoming call or a peer response?
				// This is the first select query, so answer with full SDP and ICE data
				if(!isset($this->sessionName['jchat_conference_webrtc_datareceived'][$sessionHash])) {
					$dataObject->call_status = 1;
					$dataObject->caller_peer_state = $checkCallerPeerState;
					$this->response['webrtc_conference_signaling_channel'][] = $dataObject;

					// Store session for this peer as data received, this will stop full response data sending only call status
					$this->sessionName['jchat_conference_webrtc_datareceived'][$sessionHash] = true;
				} else {
					$dataObject->call_status = 1;
					$dataObject->caller_peer_state = $checkCallerPeerState;
					// Avoid doubling full start sessions
					unset($dataObject->sdp);
					unset($dataObject->icecandidate);
					unset($dataObject->other_peers);
					$this->response['webrtc_conference_signaling_channel'][] = $dataObject;
				}

				// Swap $dataObjects index from numeric to hash based
				$dataObjects[$sessionHash] = $dataObject;
				unset($dataObjects[$numSessionIndex]);
			}
		}

		// Store, subtract and reset previous connection sessions no more available, find previous stale sessions
		$staleSessions = array();
		if(isset($this->sessionName['jchat_conference_webrtc_sessions']) && count($this->sessionName['jchat_conference_webrtc_sessions'])) {
			foreach ($this->sessionName['jchat_conference_webrtc_sessions'] as $staleSessionHash=>$staleSession) {
				if(!array_key_exists($staleSessionHash, $dataObjects)) {
					$staleSessions[] = $staleSession;
				}
			}

			if(!empty($staleSessions)) {
				foreach($staleSessions as $numSessionIndex=>$sessionStaleDataObject) {
					$sessionStaleDataObject->call_status = 0;
					$sessionStaleDataObject->caller_peer_state = 0;
					// Avoid doubling full start sessions
					unset($sessionStaleDataObject->sdp);
					unset($sessionStaleDataObject->icecandidate);
					unset($sessionStaleDataObject->other_peers);
					$this->response['webrtc_conference_signaling_channel'][] = $sessionStaleDataObject;

					// No peer data found this means that the call is not valid or ended
					$sessionHash = md5($sessionStaleDataObject->peer1 . $sessionStaleDataObject->peer2);
					if(isset($this->sessionName['jchat_conference_webrtc_datareceived'][$sessionHash])) {
						unset($this->sessionName['jchat_conference_webrtc_datareceived'][$sessionHash]);
					}
				}
			}
		}
		// Regular assignments refresh for the next execution
		$this->sessionName['jchat_conference_webrtc_sessions'] = $dataObjects;

		// Listen for declined calls by the callee, caller is no more valid caller_peer_state
		$query = "SELECT *" .
				 "\n FROM #__jchat_webrtc_conference" .
				 "\n WHERE " . $this->dbInstance->quoteName('peer1') . " = " .
				 $this->dbInstance->quote($this->userSessionTable->session_id);
		$dataCallerObjects = $this->dbInstance->setQuery($query)->loadObjectList('peer2');
		$staleCallerSessions = array();

		// Store, subtract and reset previous connection sessions no more available
		if(isset($this->sessionName['jchat_conference_webrtc_caller_sessions']) && count($this->sessionName['jchat_conference_webrtc_caller_sessions'])) {
			foreach ($this->sessionName['jchat_conference_webrtc_caller_sessions'] as $callerSession) {
				if(!array_key_exists($callerSession->peer2, $dataCallerObjects)) {
					$staleCallerSessions[] = $callerSession;
				}
			}

			if(!empty($staleCallerSessions)) {
				foreach($staleCallerSessions as $numSessionIndex=>$sessionCallerStaleDataObject) {
					$sessionCallerStaleDataObject->call_status = 0;
					$sessionCallerStaleDataObject->caller_peer_state = 0;
					$sessionCallerStaleDataObject->videocam = 0;
					$peer1Caller = $sessionCallerStaleDataObject->peer1;
					$peer2Callee = $sessionCallerStaleDataObject->peer2;
					// Inversion: the peer1 must become the callee peer2 from the caller perspective in the JS client app
					$sessionCallerStaleDataObject->peer1 = $peer2Callee;
					$sessionCallerStaleDataObject->peer2 = $peer1Caller;
					// Avoid doubling full start sessions
					unset($sessionCallerStaleDataObject->sdp);
					unset($sessionCallerStaleDataObject->icecandidate);
					unset($sessionCallerStaleDataObject->other_peers);
					$this->response['webrtc_conference_signaling_channel'][] = $sessionCallerStaleDataObject;
				}
			}
		}
		// Regular assignments for the next execution
		$this->sessionName['jchat_conference_webrtc_caller_sessions'] = $dataCallerObjects;
	}
	
	/**
	 * Listening for WebRTC signaling channel and incoming balckboard sharing sessions
	 * This signaling channel response is bypassed when peer is a caller
	 * that has received an answer, or a callee that has received an offer
	 * The signaling channel query is ALWAYS executed to monitor sharing session changes
	 *
	 * @access protected
	 * @return void
	 */
	protected function fetchBlackboardSignalingChannel() {
		//  Listen for signaling channel, listen for both SDP messages or ICE candidate
		$query = "SELECT *" .
				 "\n FROM #__jchat_webrtc_blackboard" .
				 "\n WHERE " . $this->dbInstance->quoteName('peer2') .  " = " .
				$this->dbInstance->quote($this->userSessionTable->session_id);
		$dataObject = $this->dbInstance->setQuery($query)->loadObject();
	
		// Check if the peer1 is a valida caller. The callee could have close the call in the meanwhile
		$callerQuery = "SELECT " . $this->dbInstance->quoteName('peer1') .
					   "\n FROM #__jchat_webrtc_blackboard" .
					   "\n WHERE " . $this->dbInstance->quoteName('peer1') .  " = " .
		$this->dbInstance->quote($this->userSessionTable->session_id);
		$checkCallerPeerState = $this->dbInstance->setQuery($callerQuery)->loadResult();
	
		// Security safe: are both data for ICE frameword and sdp correctly available? Otherwise posticipate
		if(!is_object($dataObject) || !$dataObject->sdp || !$dataObject->icecandidate || $dataObject->icecandidate == '[]') {
			$this->response['webrtc_blackboard_signaling_channel']['call_status'] = 0;
			$this->response['webrtc_blackboard_signaling_channel']['caller_peer_state'] = $checkCallerPeerState;
	
			// No peer data found this means that the call is not valid or ended
			if(isset($this->sessionName['jchat_webrtc_blackboard_datareceived'])) {
				unset($this->sessionName['jchat_webrtc_blackboard_datareceived']);
			}
			return;
		}
	
		// Are we dealing with a valid incoming call or a peer response?
		// This is the first select query, so answer with full SDP and ICE data
		if(!isset($this->sessionName['jchat_webrtc_blackboard_datareceived'])) {
			$dataObject->call_status = 1;
			$dataObject->caller_peer_state = $checkCallerPeerState;
			$this->response['webrtc_blackboard_signaling_channel'] = $dataObject;
	
			// Store session for this peer as data received, this will stop full response data sending only call status
			$this->sessionName['jchat_webrtc_blackboard_datareceived'] = true;
		} else {
			$this->response['webrtc_blackboard_signaling_channel']['call_status'] = 1;
			$this->response['webrtc_blackboard_signaling_channel']['caller_peer_state'] = $checkCallerPeerState;
		}
	}
	
	/**
	 * Ensure to flush the signaling channel if user refresh browser and
	 * start a new page load.
	 * Old sessions messages have to be reset and cleared
	 *
	 * @access protected
	 * @return void
	 */
	protected function clearSignalingChannel() {
		// Normal channel clearing
		$query = "DELETE FROM #__jchat_webrtc" .
				 "\n WHERE " . $this->dbInstance->quoteName('peer1') . " = " .
				 $this->dbInstance->quote($this->userSessionTable->session_id) .
				 "\n OR " . $this->dbInstance->quoteName('peer2') .  " = " .
				 $this->dbInstance->quote($this->userSessionTable->session_id);
		$this->dbInstance->setQuery($query)->execute();
		
		// Conference channel clearing
		$query = "DELETE FROM #__jchat_webrtc_conference" .
				 "\n WHERE " . $this->dbInstance->quoteName('peer1') . " = " .
				 $this->dbInstance->quote($this->userSessionTable->session_id) .
				 "\n OR " . $this->dbInstance->quoteName('peer2') .  " = " .
				 $this->dbInstance->quote($this->userSessionTable->session_id);
		$this->dbInstance->setQuery($query)->execute();
		
		// Blackboard channel clearing
		$query = "DELETE FROM #__jchat_webrtc_blackboard" .
				 "\n WHERE " . $this->dbInstance->quoteName('peer1') . " = " .
				 $this->dbInstance->quote($this->userSessionTable->session_id) .
				 "\n OR " . $this->dbInstance->quoteName('peer2') .  " = " .
				 $this->dbInstance->quote($this->userSessionTable->session_id);
		$this->dbInstance->setQuery($query)->execute();
		
		// Clear session statues
		unset($this->sessionName['jchat_conference_webrtc_datareceived']);
		unset($this->sessionName['jchat_conference_webrtc_sessions']);
		unset($this->sessionName['jchat_conference_webrtc_caller_sessions']);
	}
	
	/**
	 * Refresh session messages avatars, based on avatar changes managed by frontend users,
	 * both avatar changes or avatar delete
	 *
	 * @access protected
	 * @param array& $messages
	 * @param string $messageType Switch to evaluate single user or wall messages in session
	 * @return void
	 */
	protected function refreshSessionMessagesAvatars(&$messages, $messageType = 'user') {
		$avatarCache = array();
		$destination = null;
		if(is_array($messages) && count($messages)) {
			// Cycle on all messages
			foreach ($messages as $msgIndex=>&$sessionMessage) {
				// Forcing e smistamento in base al message type
				if($messageType == 'wall') {
					// Messagetype uguale a private user
					if($sessionMessage['from'] != 'wall') {
						continue;
					}
					$destination = 'wall';
				} else {
					// Messagetype uguale a private user
					if($sessionMessage['from'] == 'wall') {
						continue;
					}
					if(isset($sessionMessage['userid'])) {
						$destination = $sessionMessage['userid'];
					}
				}

				// Ignoriamo self message o messaggi senza to
				if(!is_null($destination) && $sessionMessage['self'] != 1) {
					// Controllo esistenza immagine SOLO SE c'è un'immagine avatar
					if(isset($sessionMessage['avatar'])) {
						if(!isset($avatarCache[$sessionMessage['userid']])) {
							if(!@file_get_contents($sessionMessage['avatar'])) {
								$sessionMessage['avatar'] = JChatHelpersUsers::getAvatar($sessionMessage['userid']);
								// Update $this->sessionName
								$this->sessionName ['jchat_user_' . $destination][$msgIndex]['avatar'] = $sessionMessage['avatar'];
							}
							// Cache storing
							$avatarCache[$sessionMessage['userid']] = $sessionMessage['avatar'];
						} else {
							if($sessionMessage['avatar'] != $avatarCache[$sessionMessage['userid']]) {
								$sessionMessage['avatar'] = $avatarCache[$sessionMessage['userid']];
								// Update $this->sessionName
								$this->sessionName ['jchat_user_' . $destination][$msgIndex]['avatar'] = $avatarCache[$sessionMessage['userid']];
							}
						}
					} else {
						if(!isset($avatarCache[$sessionMessage['userid']])) {
							// Perform di un controllo dei messaggi in sessione inviati senza avatar, ma adesso con avatar aggiunto dall'utente
							$isNowNewAvatar = JChatHelpersUsers::getAvatar($sessionMessage['userid']);
							$sessionMessage['avatar'] = $isNowNewAvatar;
							// Update $this->sessionName
							$this->sessionName ['jchat_user_' . $destination][$msgIndex]['avatar'] = $sessionMessage['avatar'];
							// Cache storing
							$avatarCache[$sessionMessage['userid']] = $sessionMessage['avatar'];
						} else {
							if($sessionMessage['avatar'] != $avatarCache[$sessionMessage['userid']]) {
								$sessionMessage['avatar'] = $avatarCache[$sessionMessage['userid']];
								// Update $this->sessionName
								$this->sessionName ['jchat_user_' . $destination][$msgIndex]['avatar'] = $avatarCache[$sessionMessage['userid']];
							}
						}
					}
				}
			}
		}
	}

	/**
	 * Output response to be formtatted as application/json for JS client
	 * 
	 * @access protected
	 * @param array& $messages
	 * @param array& $wallMessages
	 * @return void
	 */
	protected function returnFormattedResponse(&$messages, &$wallMessages = array()) { 
		if (! empty ( $messages )) {
			$this->response ['messages'] = $messages;
		}
		
		if(! empty ( $wallMessages)) {
			$this->response ['wallmessages'] = $wallMessages;
		}
		
		return $this->response;
	}
	
	/**
	 * Updates the user status for typing
	 *
	 * @access protected
	 * @param boolean $typingStatus
	 * @param string $typingTo
	 * @return boolean
	 */
	protected function updateTypingStatus($typingStatus, $typingTo) {
		$query = "INSERT INTO #__jchat_sessionstatus (sessionid, " .
				$this->dbInstance->quoteName('typing') . "," .
				$this->dbInstance->quoteName('typing_to') . ") VALUES (" .
				$this->dbInstance->quote($this->userSessionTable->session_id) . ", " .
				$this->dbInstance->quote($typingStatus) . ", " .
				$this->dbInstance->quote($typingTo) . ") " .
				"\n ON DUPLICATE KEY UPDATE " .
				$this->dbInstance->quoteName('typing') . " = " . $this->dbInstance->quote($typingStatus) . "," .
				$this->dbInstance->quoteName('typing_to') . " = " . $this->dbInstance->quote($typingTo);
		$this->dbInstance->setQuery($query);
	
		try {
			$this->dbInstance->execute ();
		} catch (JChatException $e) {
			return false;
		} catch (\Exception $e) {
			return false;
		}
	
		return true;
	}
	
	/**
	 * Detect if live support mode is active and returns query chunks to
	 * filter users/messages if users is not a chat admins
	 *
	 * @access public
	 * @return void
	 */
	protected function updateGeolocationIpAddress() {
		$isGeolocatedUser = false;
		// Check for live support mode active
		$geolocationGids = $this->componentParams->get('geolocation_gids', array('0'));
		// Live support active!
		if(is_array($geolocationGids) && !in_array(0, $geolocationGids, false)) {
			// Check for user groups current user belong to
			$userGroups = $this->myUser->getAuthorisedGroups();
			// Intersect to recognize chat admins
			$intersectResult = array_intersect($userGroups, $geolocationGids);
			$isGeolocatedUser = (bool)(count($intersectResult));
		}

		// Eventually limit query to users that belong to chat admins
		if($isGeolocatedUser || in_array(0, $geolocationGids, false)) {
			$this->storeUserStatus('geoip', $_SERVER['REMOTE_ADDR'], false);
			$this->response['geolocation'] = array('status'=>true);
		}
	}
	
	/**
	 * Main app dispatch public responsibility
	 * Execute stream logic and return data to be formatted and sent to JS client
	 * 
	 * @access public
	 * @return Object
	 */
	public function getChatData() {
		// Check if the user is not banned before going on
		if($this->getBannedStatus()) {
			$this->response['loggedout'] = '1';
			// Return JS client formatted response
			return $this->returnFormattedResponse($this->messages);
		}
		
		// Local initialization
		$fbchat_user_session_messages = array();
		$fbchat_wall_session_messages = array();
		
		// Initialize model state
		$forceParams = $this->getState('getparams');
		$chatbox = $this->getState('chatbox');
		$wallbox = $this->getState('wall');
		$wallHistory = $this->getState('wallhistory');
		$buddylist = $this->getState('buddylist');
		$initialize = $this->getState('initialize');
		$update_session = $this->getState('updatesession');
		$post_sessionvars = $this->getState('sessionvars');
		
		// Send params back only on initialize AKA first ajax call
		if(!empty($this->componentParams) && $initialize) {
			$this->componentParams->set('isguest',  strval((int)!$this->myUser->id));

			// Manage pre-serialization for STUN/TURN servers parameters
			$stunServers = $this->componentParams->get('stun_servers', null);
			if(!empty($stunServers)) {
				$stunServers = explode(PHP_EOL, $stunServers);
				$keys = array('urls');
				foreach ($stunServers as &$stunServer) {
					$stunServer = array_combine($keys, array($stunServer));
				}
			} else {
				$stunServers = array();
			}

			$turnServers = $this->componentParams->get('turn_servers', null);
			if($this->componentParams->get ( 'turn_servers_enabled', 0) && !empty($turnServers)) {
				$turnServers = explode(PHP_EOL, $turnServers);
				$keys = array('urls', 'username', 'credential');
				foreach ($turnServers as &$turnServer) {
					$turnServer = explode(',', $turnServer);
					if(is_array($turnServer) && count($turnServer) == 3) {
						$turnServer = array_combine($keys, $turnServer);
					}
				}
			} else {
				$turnServers = array();
			}

			// Get 24h credentials for Twilio TURN servers
			$turnServersAF = array();
			$currentTime = time ();
			$anyFirewallEnabled = $this->componentParams->get ( 'turn_anyfirewall_enabled', 1);
			// Go on only if Twilio TURN is enabled
			if ($anyFirewallEnabled && (! isset ( $this->sessionName ['jchat_turn_anyfirewall_ttl'] ) || $currentTime > @$this->sessionName ['jchat_turn_anyfirewall_ttl'])) {
				// Check for AnyFirewall valid credentials
				$anyFirewallAppname = $this->componentParams->get ( 'turn_twilio_sid', 'AC6f94f1e056ab8560aadc1eace975a251' );
				$anyFirewallPassword = $this->componentParams->get ( 'turn_twilio_auth_token', '8cdf069cdc9c5785b5c2ae51457b6567' );
				// No TURN servers found or it's expired, so go on with a new request
				$turnServerCredentials = $this->getTURNServer ( $anyFirewallAppname, $anyFirewallPassword );
				// Valid response found
				if ($turnServerCredentials) {
					// Check if no errors are detected from API
						// Store/refresh session lifetime ttl
						$this->sessionName ['jchat_turn_anyfirewall_ttl'] = $currentTime + $turnServerCredentials->ttl;
						$turnServersAF = $turnServerCredentials->iceServers;
						foreach ($turnServersAF as &$turnServerAF) {
							$turnServerAF['urls'] = $turnServerAF['url'];
							unset($turnServerAF['url']);
						}
						// Store the retrieved turn server credentials
						$this->sessionName ['jchat_turn_anyfirewall'] = $turnServersAF;
				}
			} elseif ($anyFirewallEnabled && isset ( $this->sessionName ['jchat_turn_anyfirewall'] )) {
				// Retrieve from session
				$turnServersAF = $this->sessionName ['jchat_turn_anyfirewall'];
			}

			// Final ICE servers merge
			$this->componentParams->set('ice_servers', array_merge($stunServers, $turnServers, $turnServersAF));

			// Set user groups for client side ACL permissions
			$userGroups = $this->myUser->getAuthorisedGroups();
			$this->componentParams->set('usergroups', $userGroups);

			// Now translates ACL parameters including child groups
			$this->translatesACLParameters(array(
					'allow_videochat', 
					'allow_media_recorder', 
					'allow_media_recorder_save',
					'allow_blackboard',
					'allow_livestreaming',
					'moderation_groups'
			));

			// Assign super user ACL action, if super user all permissions are allowed
			$this->componentParams->set('superuser', $this->myUser->authorise('core.admin'));

			$this->componentParams->set('chatform_link', Route::_('index.php?option=com_jchat&view=form'));

			// Check access levels intersection to ensure that users has access to add chatrooms
			$userGroups = $this->myUser->getAuthorisedGroups();
			$this->translatesACLParameters(array('addchatroom_groups', 'deletechatroom_groups'));
			$addChatroomGroups = $this->componentParams->get('addchatroom_groups', array(8));
			if(is_array($addChatroomGroups) && !in_array(0, $addChatroomGroups, false)) {
				$intersectResult = array_intersect($userGroups, $addChatroomGroups);
				$hasAddChatroomPermission = (int)(count($intersectResult));
				$this->componentParams->set('addchatroom_permission', $hasAddChatroomPermission);
			} else {
				$this->componentParams->set('addchatroom_permission', 1);
			}
			$deleteChatroomGroups = $this->componentParams->get('deletechatroom_groups', array(8));
			if(is_array($deleteChatroomGroups) && !in_array(0, $deleteChatroomGroups, false)) {
				$intersectResult = array_intersect($userGroups, $deleteChatroomGroups);
				$hasDeleteChatroomPermission = (int)(count($intersectResult));
				$this->componentParams->set('deletechatroom_permission', $hasDeleteChatroomPermission);
			} else {
				$this->componentParams->set('deletechatroom_permission', 1);
			}
			$this->componentParams->set('user_access_viewlevels', $this->myUser->getAuthorisedViewLevels());
			$this->componentParams->set('total_access_viewlevels', JChatHelpersUsers::getTotalAccessLevels($this->dbInstance));
			
			// If the rooms creation in frontend is enabled inject even the menu tree structure for chatrooms assignments
			if($this->componentParams->get('addchatroom', 0) && $this->componentParams->get('addchatroom_permission', 0)) {
				$this->componentParams->set('total_menuitems_tree', JChatHtmlMenu::getMenuItems());
			}
			
			// Language translation enabled, send the default language iso code for the default fallback
			if($this->componentParams->get('language_translation_enabled', 0)) {
				if(version_compare(PHP_VERSION, '5.4', '>=')) {
					$this->componentParams->set('default_fallback_language', JChatHelpersLanguage::getCurrentSefLanguage());

					$languageTranslationGroups = $this->componentParams->get('language_translation_groups', array(8));
					if(is_array($languageTranslationGroups) && !in_array(0, $languageTranslationGroups, false)) {
						$intersectResult = array_intersect($userGroups, $languageTranslationGroups);
						$hasLanguageTranslationPermission = (int)(count($intersectResult));
						$this->componentParams->set('language_translation_enabled', $hasLanguageTranslationPermission);
					}
				} else {
					$this->componentParams->set('language_translation_enabled', 0);
				}
			}
			
			// Final rendering of params to JS domain
			$this->response['paramslist'] = $this->componentParams->toObject();
			
			// Remove not needed and params and save bandwidth
			unset($this->response['paramslist']->registration_email);
			unset($this->response['paramslist']->notification_email);
			unset($this->response['paramslist']->email_subject);
			unset($this->response['paramslist']->includeevent);
			unset($this->response['paramslist']->offline_message);
			unset($this->response['paramslist']->ticket_notify_emails);
			unset($this->response['paramslist']->tickets_fromname);
			unset($this->response['paramslist']->tickets_mailfrom);
			unset($this->response['paramslist']->turn_twilio_sid);
			unset($this->response['paramslist']->turn_twilio_auth_token);
			unset($this->response['paramslist']->addchatroom_groups);
			unset($this->response['paramslist']->deletechatroom_groups);
		}
		
		if (isset($this->sessionName['jchat_user_' . $chatbox])) {
			$fbchat_user_session_messages = $this->sessionName['jchat_user_' . $chatbox];
		}
		if (isset($this->sessionName['jchat_user_wall'])) {
			$fbchat_wall_session_messages = $this->sessionName['jchat_user_wall'];
		}
		
		// Assign existant session vars
		if(isset($this->sessionName['jchat_sessionvars'])) {
			$fbchat_sessionvars = $this->sessionName['jchat_sessionvars'];
		}
		
		// Go on with chat data retrieval
		if ($this->myUser->id || $this->componentParams->get('guestenabled', false)) {
			// Request for a specific chatbox message list
			if (!empty($chatbox)) {
				if (!empty($fbchat_user_session_messages)) {
					if($this->componentParams->get('advanced_avatars_mgmt', false)) {
						$this->refreshSessionMessagesAvatars($fbchat_user_session_messages);
					}
					$this->messages = $fbchat_user_session_messages;
				}
				
				// Return JS client formatted response
				return $this->returnFormattedResponse($this->messages);
			} elseif (!empty($wallbox) && !$wallHistory) {
				// Request for public chat messages
				if (!empty($fbchat_wall_session_messages)) {
					if($this->componentParams->get('advanced_avatars_mgmt', false)) {
						$this->refreshSessionMessagesAvatars($fbchat_wall_session_messages, 'wall');
					}
					$this->wallMessages = $fbchat_wall_session_messages;
				}
				
				// Return JS client formatted response
				return $this->returnFormattedResponse($this->messages, $this->wallMessages);
			} else {
				// All other regular requests
				if (!empty($buddylist) && $buddylist == 1) {
					$this->getBuddyList($initialize);
				}
		
				if (!empty($initialize) && $initialize == 1) {
					$this->getUserStateFromDB();
					// Force start opening mode
					if (empty($fbchat_sessionvars) || !isset($fbchat_sessionvars['buddylist'])) {
						$fbchat_sessionvars = array();
						$startOpenMode = $this->componentParams->get('start_open_mode', 1);
						$fbchat_sessionvars['buddylist'] = $startOpenMode;
					}
		
					if(!$this->componentParams->get('default_private_chat_sound', 1) && !isset($fbchat_sessionvars['audio'])) {
						$fbchat_sessionvars['audio'] = 0;
					}
					
					if(!$this->componentParams->get('default_public_chat_sound', 1) && !isset($fbchat_sessionvars['wallaudio'])) {
						$fbchat_sessionvars['wallaudio'] = 0;
					}
					
					if (!empty($fbchat_sessionvars)) {
						$this->response['initialize'] = $fbchat_sessionvars;
						$this->wallMessages = $fbchat_wall_session_messages;
					}

					// Listening for incoming video call from WebRTC signaling channel
					if($this->componentParams->get('webrtc_enabled', false) || $this->componentParams->get('enable_blackboard', false) || $this->getState('conferenceview', false)) {
						$this->clearSignalingChannel();
					}

					// Manage the geolcation IP tracking if enabled
					if($this->componentParams->get('geolocation_enabled', 0)) {
						$this->updateGeolocationIpAddress();
					}
				} else {
					if (empty($fbchat_sessionvars)) {
						$fbchat_sessionvars = array();
					}
		
					if (!empty($post_sessionvars)) {
						ksort($post_sessionvars);
					} else {
						$post_sessionvars = array();
					}
		
					// Check if typing enabled and status has changed
					if($this->typingEnabled) {
						$sessionTyping = isset($fbchat_sessionvars['typing']) ? $fbchat_sessionvars['typing'] : null;
						$postTyping = isset($post_sessionvars['typing']) ? $post_sessionvars['typing'] : null;
						$postTypingTo = isset($post_sessionvars['typing_to']) ? $post_sessionvars['typing_to'] : null;
						if(!is_null($postTyping) && $sessionTyping != $postTyping) {
							$this->updateTypingStatus($postTyping, $postTypingTo);
						}
					}
					
					// Always update the session vars for languages if informations are sent from the client side in realtime
					if(isset($post_sessionvars['langvars'])) {
						$this->sessionName['jchat_sessionvars']['langvars'] = $post_sessionvars['langvars'] = json_decode($post_sessionvars['langvars']);
					} else {
						$this->sessionName['jchat_sessionvars']['langvars'] = null;
					}
					
					if (!empty($update_session) && $update_session == 1) {
						$this->sessionName['jchat_sessionvars'] = array_merge($fbchat_sessionvars, $post_sessionvars);
					}
		
					if ($forceParams) {
						$this->response['paramslist'] = $this->componentParams->toObject();
					}
				}
		
				$this->fetchMessages();
				$this->fetchWallMessages();
				
				if($this->typingEnabled) {
					$this->fetchTypingStatus();
				}
				
				// Listening for incoming video call from WebRTC signaling channel
				if($this->componentParams->get('webrtc_enabled', false)) {
					$this->fetchSignalingChannel();
				}
				// Fetch the conference signaling channel only if on the conference view page
				if($this->getState('conferenceview', false)) {
					$this->fetchConferenceSignalingChannel();
				}
				// Listening for incoming blackboard shares from WebRTC signaling channel
				if($this->componentParams->get('enable_blackboard', false)) {
					$this->fetchBlackboardSignalingChannel();
				}
				
				// Return JS client formatted response
				return $this->returnFormattedResponse($this->messages, $this->wallMessages);
			}
		} else {
			$this->response['loggedout'] = '1';
			if ($forceParams) {
				$this->response['paramslist'] = $this->componentParams->toObject();
			}
			
			// Return JS client formatted response
			return $this->returnFormattedResponse($this->messages);
		}
	}
	
	/**
	 * Detect if live support mode is active and returns query chunks to
	 * filter users/messages if users is not a chat admins
	 *
	 * @access public
	 * @return array
	 */
	public function getQueryLiveSupport($joinTable = 'sess.userid') {
		$arrayQueries = array(0=>null, 1=>null);
		// Check for live support mode active
		$chatAdminsGids = $this->componentParams->get('chatadmins_gids', array('0'));
		// Live support active!
		if(is_array($chatAdminsGids) && !in_array(0, $chatAdminsGids, false)) {
			// Check for user groups current user belong to
			$userGroups = $this->myUser->getAuthorisedGroups();
			// Intersect to recognize chat admins
			$intersectResult = array_intersect($userGroups, $chatAdminsGids);
			$isChatAdmin = (bool)(count($intersectResult));
	
			// Eventually limit query to users that belong to chat admins
			if(!$isChatAdmin) {
				$arrayQueries[0] = "\n INNER JOIN #__user_usergroup_map AS map ON map.user_id = " . $joinTable;
				$arrayQueries[1] = "\n AND map.group_id IN (" . implode(',', $chatAdminsGids) . ")";
			}
			
			// Inject only on initialize calls
			if($this->getState('initialize') == 1) {
				$this->response['ischatadmin'] = (int)$isChatAdmin;
			}
		}
		return $arrayQueries;
	}
	
	/**
	 * Translate chat access levels into user groups and generates filtering query
	 * accordingly for the users taken into account by the buddylist, thus avoiding
	 * show disabled users into chat of enabled users refreshing session lifetime during navigation
	 *
	 * @access public
	 * @return array
	 */
	public function getQueryAccessLevels($joinTable = 'sess.userid') {
		$arrayQueries = array(0=>null, 1=>null);
		// Check for live support mode active
		$chatAccessGids = array();
		$chatAccessLevels = $this->componentParams->get('chat_accesslevels', array('0'));
		// Live support active!
		if(is_array($chatAccessLevels) && !in_array(0, $chatAccessLevels, false)) {
			// Translate the chat access levels to Joomla users groups sum
			$query = $this->dbInstance->getQuery(true)
								->select('rules')
								->from($this->dbInstance->quoteName('#__viewlevels'))
								->where('id IN (' . implode(',', $chatAccessLevels) . ')');
			// Set the query for execution
			$this->dbInstance->setQuery($query);
			// Build the view levels array.
			foreach ($this->dbInstance->loadColumn() as $levels) {
				$chatAccessGids = array_merge($chatAccessGids, (array) json_decode($levels));
			}
	
			// Limit query to users that belong to groups for the chosen chat access levels
			if(array_intersect(array(1,5), $chatAccessLevels)) {
				$arrayQueries[0] = "\n LEFT JOIN #__user_usergroup_map AS accessmap ON accessmap.user_id = " . $joinTable;
				$arrayQueries[1] = "\n AND (accessmap.group_id IN (" . implode(',', array_unique($chatAccessGids)) . ") OR ISNULL(accessmap.group_id))";
			} else {
				$arrayQueries[0] = "\n INNER JOIN #__user_usergroup_map AS accessmap ON accessmap.user_id = " . $joinTable;
				$arrayQueries[1] = "\n AND accessmap.group_id IN (" . implode(',', array_unique($chatAccessGids)) . ")";
			}
		}
		return $arrayQueries;
	}
	
	/**
	 * Filter the buddylist based on the current user groups belonging
	 * Users will be be able to chat only with users in the same users groups
	 *
	 * @access public
	 * @return array
	 */
	public function getQueryMyUsersGroups($joinTable = 'sess.userid') {
		$arrayQueries = array(0=>null, 1=>null);
	
		// Check for user groups current user belong to
		$userGroups = $this->myUser->getAuthorisedGroups();
	
		$arrayQueries[0] = "\n INNER JOIN #__user_usergroup_map AS mymap ON mymap.user_id = " . $joinTable;
		$arrayQueries[1] = "\n AND mymap.group_id IN (" . implode(',', $userGroups) . ")";
	
		return $arrayQueries;
	}
	
	/**
	 * Get query parts needed for SELECT & JOIN
	 * tables for database integration component
	 *
	 * @access public
	 * @param string $queryType
	 * @return array
	 */
	public function getQueryParts($queryType) {
		// Restituisce le query parts in accordo al tipo di integration richiesta
		$queryParts = array();
	
		switch ($queryType) {
			case 'buddylist' :
				switch ($this->integratedExtensions) {
					case 'jomsocial':
						$queryParts['JOIN'] = "\n INNER JOIN #__community_connection AS cc ON cc.connect_to = u.id";
						$queryParts['WHERE'] = 	"\n AND cc.connect_from = " . (int)$this->myUser->id .
												"\n AND cc.status = 1";
						break;
	
					case 'cbuilder':
						$queryParts['JOIN'] = "\n INNER JOIN #__comprofiler_members AS cm ON cm.memberid = u.id";
						$queryParts['WHERE'] = 	"\n AND cm.referenceid = " . (int)$this->myUser->id .
												"\n AND cm.accepted = 1 AND cm.pending = 0";
						break;
	
					case 'easysocial':
						$queryParts['JOIN'] = "\n LEFT JOIN #__social_friends AS sf ON (sf.actor_id = u.id OR sf.target_id = u.id)" .
						$queryParts['WHERE'] = 	"\n AND (sf.actor_id = " . (int)$this->myUser->id . " OR sf.target_id = " . (int)$this->myUser->id . ")" .
												"\n AND sf.state = 1";
								break;
					case 'k2user':
						$queryParts['JOIN'] = '';
						$queryParts['WHERE'] = '';
						break;
				}
				break;
	
	
			case 'groupmessages' :
				switch ($this->integratedExtensions) {
					case 'jomsocial':
						$queryParts['AND'] = " AND u.id IN (SELECT connect_to FROM #__community_connection" .
											 "\n WHERE connect_from = " . (int)$this->myUser->id . " AND status = 1)";
						break;
							
					case 'cbuilder':
						$queryParts['AND'] = " AND u.id IN (SELECT memberid FROM #__comprofiler_members" .
											 "\n WHERE referenceid = " . (int)$this->myUser->id . " AND accepted = 1 and pending = 0)";
						break;
	
					case 'easysocial':
						$queryParts['AND'] = " AND (u.id IN (SELECT actor_id FROM #__social_friends" .
											 "\n WHERE target_id = " . (int)$this->myUser->id . " AND state = 1)" .
											 "\n OR u.id IN (SELECT target_id FROM #__social_friends" .
											 "\n WHERE actor_id = " . (int)$this->myUser->id . " AND state = 1))";
						break;
					case 'k2user':
						$queryParts['AND'] = '';
						break;
				}
				break;
		}
	
		return $queryParts;
	}
	
	/**
	 * Write user status on Stream
	 * 
	 * @access public
	 * @param string $fieldName
	 * @param string $fieldValue
	 * @param boolean $injectInResponse
	 * @return array
	 */
	public function storeUserStatus($fieldName, $fieldValue, $injectInResponse = true) {
		$query = "INSERT INTO #__jchat_sessionstatus (sessionid, " . $this->dbInstance->quoteName($fieldName) . ") VALUES (" .
				$this->dbInstance->quote($this->userSessionTable->session_id) . ", " .
				$this->dbInstance->quote($fieldValue) . ") " .
				"\n ON DUPLICATE KEY UPDATE " . $this->dbInstance->quoteName($fieldName) . " = " . $this->dbInstance->quote($fieldValue);
		
		try {
			$this->dbInstance->setQuery($query);
			$this->dbInstance->execute ();
		} catch (JChatException $e) {
			$this->response['storing'] = array('status'=>false, 'details'=>$e->getMessage());
			return $this->response;
		} catch (\Exception $e) {
			$jchatException = new JChatException($e->getMessage(), 'error');
			$this->response['storing'] = array('status'=>false, 'details'=>$jchatException->getMessage());
			return $this->response;
		}
		
		if ($fieldName == 'status' && $fieldValue == 'offline') {
			$this->sessionName['jchat_sessionvars']['buddylist'] = 0;
		}
		
		if($injectInResponse) {
			$this->response['storing'] = array('status'=>true);
		}
		
		return $this->response;
	}
	
	/**
	 * Write user status on Stream, based on user logging state
	 * 
	 * @access public
	 * @param string $statusVarName
	 * @param string $statusVarValue
	 * @return array
	*/
	public function storeUserStateFromRequest($statusVarName, $statusVarValue) {
		if(!$this->myUser->id) {
			$query = "INSERT INTO #__jchat_sessionstatus (sessionid, " . $this->dbInstance->quoteName($statusVarName) . ") VALUES (" .
					 $this->dbInstance->quote($this->userSessionTable->session_id) . ", " .
					 $this->dbInstance->quote($statusVarValue) . ") " .
					 "ON DUPLICATE KEY UPDATE " . $this->dbInstance->quoteName($statusVarName) . " = " . $this->dbInstance->quote($statusVarValue);
		} else {
			$query = "INSERT INTO #__jchat_userstatus (userid, " . $this->dbInstance->quoteName($statusVarName) . ") VALUES (" .
					 $this->dbInstance->quote($this->myUser->id) . ", " .
					 $this->dbInstance->quote($statusVarValue) . ") " .
					 "ON DUPLICATE KEY UPDATE " . $this->dbInstance->quoteName($statusVarName) . " = " . $this->dbInstance->quote($statusVarValue);
		}
		$this->dbInstance->setQuery($query);
		
		try {
			$this->dbInstance->execute ();
			
			// If user is now logged in, delete/clean/discard all data in session state
			if($this->myUser->id) {
				$cleanQuery = "DELETE FROM #__jchat_sessionstatus" . 
							  "\n WHERE " . $this->dbInstance->quoteName('sessionid') . " = " . $this->dbInstance->quote($this->userSessionTable->session_id);
				$this->dbInstance->setQuery($cleanQuery)->execute ();
			}
			
			// Auto insert a message 'User xxx has joined/left chat room yyy' that will be showed to right users based on receiving logic
			$chatroomsNotifications = $this->componentParams->get('chatrooms_notifications', 1);
			if($statusVarName == 'roomid' && $statusVarValue > 0) {
				if(!$this->app->input->get('silentJoin')) {
					// User changed chatroom, add a left message for the previous chatroom
					if($this->myChatRoom && $this->myChatRoom != $statusVarValue && $chatroomsNotifications) {
						$this->storeGroupMessage('wall', Text::sprintf('COM_JCHAT_LEFT', '<img class="jchat_roomenter" src="' . Uri::base() . 'components/com_jchat/images/default/room_left.png"/>'), true);
					}
					$this->myChatRoom = $statusVarValue;
					if($chatroomsNotifications) {
						$this->storeGroupMessage('wall', Text::sprintf('COM_JCHAT_JOINED', '<img class="jchat_roomenter" src="' . Uri::base() . 'components/com_jchat/images/default/room_enter.png"/>'), true);
					}
				} else {
					$this->myChatRoom = $statusVarValue;
				}
				
				// Store just joined room state to recover latest xxx time messages of current conversation
				if($this->componentParams->get('chatrooms_latest', 1)) {
					$this->sessionName['jchat_justjoined'] = true;
				}
			} elseif($statusVarName == 'roomid' && $statusVarValue == 0) {
				if($chatroomsNotifications) {
					$this->storeGroupMessage('wall', Text::sprintf('COM_JCHAT_LEFT', '<img class="jchat_roomenter" src="' . Uri::base() . 'components/com_jchat/images/default/room_left.png"/>'), true);
				}
				$this->myChatRoom = 0;
			}
			
			// Ensure to clear the current public chat session messages
			if($statusVarName == 'roomid' && $this->componentParams->get('autoclear_conversation', 1)) {
				$this->sessionName['jchat_user_wall'] = array();
			}
		} catch (JChatException $e) {
			$this->response['storing'] = array('status'=>false,  'details'=>$e->getMessage());
			return $this->response;
				
		} catch (\Exception $e) {
			$jchatException = new JChatException($e->getMessage(), 'error');
			$this->response['storing'] = array('status'=>false,  'details'=>$jchatException->getMessage());
			return $this->response;
		}
				
		$this->response['storing'] = array('status'=>true);
		
		return $this->response;
	}
	
	/**
	 * Store banning state for current session id user
	 *
	 * @param Object $bannedUserInfo
	 * @access public
	 * @return array
	 */
	public function storeBannedUsersState($bannedUserInfo) {
		if(!$bannedUserInfo->currentState) {
			$query = "DELETE FROM #__jchat_banned_users" .
					 "\n WHERE " .
					 $this->dbInstance->quoteName('banning') . " = " . $this->dbInstance->quote($this->userSessionTable->session_id) .
					 "\n AND " .
					 $this->dbInstance->quoteName('banned') . " = " . $this->dbInstance->quote($bannedUserInfo->userSessionId);
		} else {
			$query = "INSERT IGNORE INTO #__jchat_banned_users (" .
					 $this->dbInstance->quoteName('banning') . ", " .
					 $this->dbInstance->quoteName('banned') .
					 ") VALUES (" .
					 $this->dbInstance->quote($this->userSessionTable->session_id) . ", " .
					 $this->dbInstance->quote($bannedUserInfo->userSessionId) .
					 ")";
		}

		try {
			$this->dbInstance->setQuery($query);
			$this->dbInstance->execute ();
		} catch (JChatException $e) {
			$this->response['storing'] = array('status'=>false,  'details'=>$e->getMessage());
			return $this->response;
		} catch (\Exception $e) {
			$jchatException = new JChatException($e->getMessage(), 'error');
			$this->response['storing'] = array('status'=>false,  'details'=>$jchatException->getMessage());
			return $this->response;
		}
		
		// If this is a registered and logged in user, store also the real user id for propagate banning to future sessions
		if(isset($bannedUserInfo->userId) && $this->myUser->id) {
			if(!$bannedUserInfo->currentState) {
				$query = "DELETE FROM #__jchat_banned_users_ids" .
						 "\n WHERE " .
						 $this->dbInstance->quoteName('banning') . " = " . $this->dbInstance->quote($this->myUser->id) .
						 "\n AND " .
						 $this->dbInstance->quoteName('banned') . " = " . $this->dbInstance->quote($bannedUserInfo->userId);
			} else {
				$query = "INSERT IGNORE INTO #__jchat_banned_users_ids (" .
						 $this->dbInstance->quoteName('banning') . ", " .
						 $this->dbInstance->quoteName('banned') .
						 ") VALUES (" .
						 $this->dbInstance->quote($this->myUser->id) . ", " .
						 $this->dbInstance->quote($bannedUserInfo->userId) .
						 ")";
			}
			
			try {
				$this->dbInstance->setQuery($query);
				$this->dbInstance->execute ();
			} catch (JChatException $e) {
				$this->response['storing'] = array('status'=>false,  'details'=>$e->getMessage());
				return $this->response;
			} catch (\Exception $e) {
				$jchatException = new JChatException($e->getMessage(), 'error');
				$this->response['storing'] = array('status'=>false,  'details'=>$jchatException->getMessage());
				return $this->response;
			}
		}
		$this->response['storing'] = array('status'=>true);

		return $this->response;
	}
	
	/**
	 * Write user status on Stream, based on user logging state
	 *
	 * @access public
	 * @param Object $bannedUserInfo
	 * @return array
	 */
	public function storeBannedModeratedUsersState($bannedUserInfo) {
		if(!isset($bannedUserInfo->userId)) {
			$query = "INSERT INTO #__jchat_sessionstatus (sessionid, " . $this->dbInstance->quoteName('banstatus') . ") VALUES (" .
					 $this->dbInstance->quote($bannedUserInfo->userSessionId) . ", 1)" .
					 "ON DUPLICATE KEY UPDATE " . $this->dbInstance->quoteName('banstatus') . " = 1";
		} else {
			$query = "INSERT INTO #__jchat_userstatus (userid, " . $this->dbInstance->quoteName('banstatus') . ") VALUES (" .
					 $this->dbInstance->quote($bannedUserInfo->userId) . ", 1)" .
					 "ON DUPLICATE KEY UPDATE " . $this->dbInstance->quoteName('banstatus') . " = 1";
		}
	
		try {
			$this->dbInstance->setQuery($query);
			$this->dbInstance->execute ();
		} catch (JChatException $e) {
			$this->response['storing'] = array('status'=>false,  'details'=>$e->getMessage());
			return $this->response;
	
		} catch (\Exception $e) {
			$jchatException = new JChatException($e->getMessage(), 'error');
			$this->response['storing'] = array('status'=>false,  'details'=>$jchatException->getMessage());
			return $this->response;
		}
	
		$this->response['storing'] = array('status'=>true);
	
		return $this->response;
	}
	
	/**
	 * Write private message on Stream
	 * 
	 * @access public
	 * @param string $to
	 * @param int $tologged
	 * @param string $message
	 * @param Object $mailer
	 * @return array
	*/
	public function storePrivateMessage($to, $tologged, $message, $mailer) {
		if ($this->userSessionTable->session_id) {
			// Set initial state
			$pmStoreMessage = false;
			$translatedMessage = null;
			$selfTranslate = false;

			// Valid target user session id?
			if(!$to && $tologged) {
				$sessionSql =  "SELECT" .
							   "\n " . $this->dbInstance->quoteName('session_id') .
							   "\n FROM #__session" .
							   "\n WHERE" .
							   "\n " . $this->dbInstance->quoteName('userid') . " = " . (int)$tologged .
							   "\n ORDER BY " . $this->dbInstance->quoteName('time') . " DESC" .
							   "\n LIMIT 1";
				$this->dbInstance->setQuery($sessionSql);
				$sessionIDReceiver = $this->dbInstance->loadResult();
				$to = $sessionIDReceiver ? $sessionIDReceiver : -1;

				// Translate to a private message to an offline user
				$pmStoreMessage = true;
			}
			
			// Language translation of the message
			if($this->componentParams->get('language_translation_enabled', 0)) {
				if(version_compare(PHP_VERSION, '5.4', '>=')) {
					// Get post informations and ensure that the language translation request is valid
					$sourceLanguage = $this->app->input->get('sourcelang');
					$targetLanguage = $this->app->input->get('targetlang');
					$langSwitchEnabled = $this->app->input->getInt('lang_switch_enabled', 0);
					if($langSwitchEnabled && ($sourceLanguage != $targetLanguage)) {
						try {
							// Composer autoloader
							require_once JPATH_COMPONENT_ADMINISTRATOR . '/Framework/composer/autoload_real.php';
							$translatedMessage = \Stichoza\GoogleTranslate\TranslateClient::translate($sourceLanguage, $targetLanguage, $message);
							if($this->componentParams->get('language_translation_selfmessages', 1)) {
								$message = $translatedMessage ? $translatedMessage : $message;
								$selfTranslate = true;
							}
						} catch(\Exception $e) {/*Do nothing, leave the original text message unaltered*/}
					}
				}
			}
			
			// Get users actual names
			$actualNames = JChatHelpersUsers::getActualNames ( $this->userSessionTable->session_id, $to, $this->componentParams );
			
			$unixTimeStamp = time();
			$sql =  "INSERT INTO #__jchat" .
					"\n (#__jchat.from," .
					"\n #__jchat.to," .
					"\n #__jchat.fromuser," .
					"\n #__jchat.touser," .
					"\n #__jchat.message," .
					"\n #__jchat.sent," .
					"\n #__jchat.read," .
					"\n #__jchat.actualfrom," .
					"\n #__jchat.actualto," .
					"\n #__jchat.ipaddress) VALUES (".
					$this->dbInstance->quote($this->userSessionTable->session_id) . ", " .
					$this->dbInstance->quote($to) . "," .
					$this->dbInstance->quote($this->myUser->id) . "," .
					$this->dbInstance->quote($tologged) . "," .
					$this->dbInstance->quote($translatedMessage ? $translatedMessage : $message) . "," . 
					$this->dbInstance->quote($unixTimeStamp) . "," .
					"0" . "," . 
					$this->dbInstance->quote($actualNames['fromActualName']) . "," .
					$this->dbInstance->quote($actualNames['toActualName']) . ","  .
					$this->dbInstance->quote($_SERVER['REMOTE_ADDR']). ")";
			try {
			    $this->dbInstance->setQuery($sql);
				$this->dbInstance->execute ();
			} catch (JChatException $e) {
				$this->response['storing'] = array('status'=>false, 'details'=>$e->getMessage());
				return $this->response;
			} catch (\Exception $e) {
				$jchatException = new JChatException($e->getMessage(), 'error');
				$this->response['storing'] = array('status'=>false, 'details'=>$jchatException->getMessage());
				return $this->response;
			}
			
			// Send email notification if needed, AKA new conversation started and settings are required
			if(!isset($this->sessionName['jchat_user_'.$to]) && $this->componentParams->get('notification_email_switcher', false)) {
				// Format and send email
				$notificationsAddresses = $this->componentParams->get('notification_email', null);
				if($notificationsAddresses) {
					$mailer->IsHTML(true);
					
					// Get single email addresses
					$exploded = explode(',', $notificationsAddresses);
					foreach ($exploded as $recipient) {
						$mailer->addRecipient(trim($recipient));
					}
					
					// Set subject
					$mailer->setSubject($this->componentParams->get('email_subject', 'JChatSocial - New conversation started'));
					
					// Format body and message
					$body = Text::sprintf('COM_JCHAT_NEWUSER_CONVERSATION', $actualNames['fromActualName'], $actualNames['toActualName']);
					$customText =Text::sprintf('COM_JCHAT_STARTING_CUSTOMTEXT_FORMATTED', $this->componentParams->get('email_start_text', ''));
					$messageText = Text::sprintf('COM_JCHAT_STARTING_MESSAGE', $actualNames['fromActualName'], $message, $customText, Uri::root());
					$mailer->setBody($body . $messageText);
					$result = $mailer->sendUsingExceptions();
				}
			}
	
			// Send email notification if offline private message sent to offline user and the option is enabled
			if($pmStoreMessage) {
				$pmNotifications = $this->componentParams->get('private_messaging_notification_email', true);
				// Private messaging notifications enabled
				if($pmNotifications) {
					// Try to retrieve the user avatar
					$userAvatar = JChatHelpersUsers::getAvatar($this->userSessionTable->session_id);

					// Retrieve name of the sender and email of the target user
					$userEmailSql =  "SELECT" .
									 "\n " . $this->dbInstance->quoteName('email') .
									 "\n FROM" . $this->dbInstance->quoteName('#__users') .
									 "\n WHERE" .
									 "\n " . $this->dbInstance->quoteName('id') . " = " . (int)$tologged;
					$this->dbInstance->setQuery($userEmailSql);
					$recipient = $this->dbInstance->loadResult();

					$mailer->IsHTML(true);
					$mailer->addRecipient(trim($recipient));

					// Set subject
					$mailer->setSubject(Text::sprintf('COM_JCHAT_USER_SENT_PM_TOYOU', $actualNames['fromActualName']));

					// Format body and message
					$body = Text::sprintf('COM_JCHAT_USER_SENT_PM_INTRO', $userAvatar, $actualNames['fromActualName']);
					$messageText = Text::sprintf('COM_JCHAT_USER_SENT_PM_MESSAGE', $message);
					$messageReply = Text::sprintf('COM_JCHAT_USER_SENT_PM_MESSAGE_ANSWER', Uri::base(), Uri::base(), Uri::base());
					$mailer->setBody($body . $messageText . $messageReply);
					$result = $mailer->sendUsingExceptions();
				}
			}
			
			if (empty($this->sessionName['jchat_user_'.$to])) {
				$this->sessionName['jchat_user_'.$to] = array();
			}
			// Store local session message
			$lastInsertId = $this->dbInstance->insertid();
			$insertTime = HTMLHelper::_('date', $unixTimeStamp, Text::_('DATE_FORMAT_LC2'));
			$this->sessionName['jchat_user_'.$to][$lastInsertId] = array(
																"id" => $lastInsertId, 
																"from" => $to, 
																"message" => $message, 
																"time" => $insertTime, 
																"self" => 1, 
																"old" => 1);
			$this->response['storing'] = array('status'=>true, 'details'=>array('id'=>$lastInsertId, 'time'=>$insertTime));
			if($selfTranslate) {
				$this->response['storing']['translatedmessage'] = $translatedMessage ? $translatedMessage : $message;
			}
			
			// Ensure no typing status are more active
			$this->updateTypingStatus(null, null);
			
			return $this->response;
		} 
	}
	
	/**
	 * Write group message on Stream
	 * 
	 * @access public
	 * @param int $to
	 * @param string $message
	 * @param boolean $skipSession
	 * @return array
	*/
	public function storeGroupMessage($to, $message, $skipSession = false) {
		if ($this->userSessionTable->session_id) {
			// Get users actual names
			$actualNames = JChatHelpersUsers::getActualNames ( $this->userSessionTable->session_id, $to, $this->componentParams );

			$unixTimeStamp = time();
			$query =  "INSERT INTO #__jchat" .
					  "\n (#__jchat.from," .
					  "\n #__jchat.to," .
					  "\n #__jchat.message," .
					  "\n #__jchat.sent," .
					  "\n #__jchat.read," .
					  "\n #__jchat.actualfrom," .
					  "\n #__jchat.sentroomid," .
					  "\n #__jchat.ipaddress) VALUES (".
					  $this->dbInstance->quote($this->userSessionTable->session_id) . ", " .
					  "0" . "," .
					  $this->dbInstance->quote($message) . "," .
					  $this->dbInstance->quote($unixTimeStamp) . "," .
					  "0" . "," .
					  $this->dbInstance->quote($actualNames['fromActualName']) . "," .
					  (int)$this->myChatRoom . "," .
					  $this->dbInstance->quote($_SERVER['REMOTE_ADDR']) . ")";
		
			try {
				$this->dbInstance->setQuery($query);
				$this->dbInstance->execute ();
			} catch (JChatException $e) {
				$this->response['storing'] = array('status'=>false, 'details'=>$e->getMessage());
				return $this->response;
			} catch (\Exception $e) {
				$jchatException = new JChatException($e->getMessage(), 'error');
				$this->response['storing'] = array('status'=>false, 'details'=>$jchatException->getMessage());
				return $this->response;
			}
			
			if (empty($this->sessionName['jchat_user_'.$to])) {
				$this->sessionName['jchat_user_'.$to] = array();
			}
			
			// Store local session message
			$lastInsertId = $this->dbInstance->insertid();
			$insertTime = HTMLHelper::_('date', $unixTimeStamp, Text::_('DATE_FORMAT_LC2'));
			if(!$skipSession) {
				$this->sessionName['jchat_user_'.$to][$lastInsertId] = array(
																	"id" => $lastInsertId, 
																    "fromuser" => $this->myUser->username, 
																    "from" => $to, 
																    "message" => $message,
																    "time" => $insertTime,
																    "self" => 1, 
																    "old" => 1);
			}
			$this->response['storing'] = array('status'=>true,  'details'=>array('id'=>$lastInsertId, 'time'=>$insertTime));
			
			return $this->response;
		}
	}
	
	/**
	 * Add a new chatroom to the database
	 *
	 * @access public
	 * @param string $roomName
	 * @param string $roomDescription
	 * @param int $roomAccess
	 * @param string $roomMenuitems
	 * @return array
	 */
	public function storeNewChatroom($roomName, $roomDescription, $roomAccess, $roomMenuItems) {
		$sqlMenuItems = $roomMenuItems ? $this->dbInstance->quote($roomMenuItems) : 'NULL';
		$query = "INSERT INTO #__jchat_rooms (" .
				 "\n " . $this->dbInstance->quoteName('name') . "," .
				 "\n " . $this->dbInstance->quoteName('description') . "," .
				 "\n " . $this->dbInstance->quoteName('access') . "," .
				 "\n " . $this->dbInstance->quoteName('ordering') . "," .
				 "\n " . $this->dbInstance->quoteName('menuitems') . ")" .
				 "\n VALUES (" .
				 $this->dbInstance->quote($roomName) . ", " .
				 $this->dbInstance->quote($roomDescription) . ", " .
				 (int)$roomAccess . ", " .
				 "\n COALESCE((SELECT MAX(" .$this->dbInstance->quoteName('ordering') . ") FROM " .
				 "\n (SELECT * FROM #__jchat_rooms) AS " . $this->dbInstance->quoteName('inrtable') . "), 0) + 1" . ", " .
				 $sqlMenuItems . ')';
	
		try {
			$this->dbInstance->setQuery($query);
			$this->dbInstance->execute ();
			
			// Clean the component cache to refresh immediately
			JChatHelpersMessages::cleanComponentCache('com_jchat', 0);
		} catch (JChatException $e) {
			$this->response['storing'] = array('status'=>false, 'details'=>$e->getMessage());
			return $this->response;
		} catch (\Exception $e) {
			$jchatException = new JChatException($e->getMessage(), 'error');
			$this->response['storing'] = array('status'=>false, 'details'=>$jchatException->getMessage());
			return $this->response;
		}
	
		$this->response['storing'] = array('status'=>true, 'chatroomid'=>$this->dbInstance->insertid());

		return $this->response;
	}
	
	/**
	 * Delete conversation from session
	 * 
	 * @access public
	 * @param int $from
	 * @return array
	 */
	public function deleteConversation($from) {
		if (isset($this->sessionName['jchat_user_'.$from])) {
			// 1) Get file messages
			if(is_array($this->sessionName['jchat_user_'.$from]) && $from != 'wall') {
				$idsToUpdate = array();
				foreach ($this->sessionName['jchat_user_'.$from] as $genericMsg) {
					// Select only file messages ids
					if(isset($genericMsg['type']) && $genericMsg['type'] === 'file') {
						$idsToUpdate[] = $genericMsg['id'];
					}
				}
			}
				
			// 2) Flag as clientdeleted on DB
			if(!empty($idsToUpdate)) {
				$query =  "UPDATE #__jchat SET " . $this->dbInstance->quoteName('clientdeleted') . " = 1" .
						  "\n WHERE " . $this->dbInstance->quoteName('id') . " IN (" . implode(',', $idsToUpdate) . ")";
				try {
					$this->dbInstance->setQuery($query);
					$this->dbInstance->execute ();
				} catch (JChatException $e) {
					$this->response['storing'] = array('status'=>false,  'details'=>$e->getMessage());
					return $this->response;
				} catch (\Exception $e) {
					$jchatException = new JChatException($e->getMessage(), 'error');
					$this->response['storing'] = array('status'=>false,  'details'=>$jchatException->getMessage());
					return $this->response;
				}
			}
				
			// 3) Empty session array
			$this->sessionName['jchat_user_'.$from] = array();
		}
		
		$this->response['storing'] = array('status'=>true);
		
		return $this->response;
	}
	
	/**
	 * Delete a single message in 3 steps:
	 *
	 * 1) Delete it from the local sender session, being it a private message or a wall message
	 * 2) Add a physical deleted message row to the dedicated table, this is required for the clients fetch
	 * 3) Physically delete the sent message from the main table, this will affect all messaging types and logged in/out states by future fetches
	 *
	 * @access public
	 * @param int $deletedMessageId
	 * @param int $to
	 * @param int $deletedMessagePM
	 * @return array
	 */
	public function deleteSingleMessage($deletedMessageId, $to, $deletedMessagePM) {
		if (isset($this->sessionName['jchat_user_'.$to]) || $deletedMessagePM) {
			// 1) Delete the message from the session
			if(isset($this->sessionName['jchat_user_'.$to]) && is_array($this->sessionName['jchat_user_'.$to]) && isset($this->sessionName['jchat_user_'.$to][$deletedMessageId])) {
				// Unset this message from the session array
				unset($this->sessionName['jchat_user_'.$to][$deletedMessageId]);
			}
			
			try {
				// 2) Add the physical deleted message
				$query =  	"INSERT INTO #__jchat_deleted_messages" .
							"\n (" .
							"\n #__jchat_deleted_messages.messageid," .
							"\n #__jchat_deleted_messages.from," .
							"\n #__jchat_deleted_messages.to," .
							"\n #__jchat_deleted_messages.sent) VALUES (".
							$this->dbInstance->quote($deletedMessageId) . ", " .
							$this->dbInstance->quote($this->userSessionTable->session_id) . ", " .
							$this->dbInstance->quote($to) . ", " .
							time() . ")";
				$this->dbInstance->setQuery($query);
				$this->dbInstance->execute ();
				
				// 3) Flag as clientdeleted on DB
				$query = "DELETE FROM #__jchat WHERE " . $this->dbInstance->quoteName('id') . " = " . $deletedMessageId;
				$this->dbInstance->setQuery($query);
				$this->dbInstance->execute ();
			} catch (JChatException $e) {
				$this->response['storing'] = array('status'=>false,  'details'=>$e->getMessage());
				return $this->response;
				
			} catch (Exception $e) {
				$jchatException = new JChatException($e->getMessage(), 'error');
				$this->response['storing'] = array('status'=>false,  'details'=>$jchatException->getMessage());
				return $this->response;
			}
			
		}
		
		$this->response['storing'] = array('status'=>true);
		
		return $this->response;
	}
	
	/**
	 * Delete chatroom from the frontend stream
	 *
	 * @access public
	 * @param int $chatroomID
	 * @return array
	 */
	public function deleteChatroom($chatroomID) {
		if(!empty($chatroomID)) {
			$query = "DELETE FROM #__jchat_rooms" .
					 "\n WHERE " . $this->dbInstance->quoteName('id') . " = " . (int)$chatroomID;
			try {
				$this->dbInstance->setQuery($query);
				$this->dbInstance->execute ();
				
				// Clean the component cache to refresh immediately
				JChatHelpersMessages::cleanComponentCache('com_jchat', 0);
			} catch (JChatException $e) {
				$this->response['storing'] = array('status'=>false,  'details'=>$e->getMessage());
				return $this->response;
			} catch (\Exception $e) {
				$jchatException = new JChatException($e->getMessage(), 'error');
				$this->response['storing'] = array('status'=>false,  'details'=>$jchatException->getMessage());
				return $this->response;
			}
		}

		$this->response['storing'] = array('status'=>true);

		return $this->response;
	}
	
	/**
	 * Retrieve guest info user informations, usually stored by guest activation form
	 *
	 * @access public
	 * @param string $session_id
	 * @return array
	 */
	public function getInfoGuest($session_id) {
		// Empty initialization
		$resultInfo = array();
		
		$query = "SELECT " . $this->dbInstance->quoteName('email') . "," .
				 $this->dbInstance->quoteName('description') .
			     "\n FROM " . $this->dbInstance->quoteName('#__jchat_sessionstatus') .
				 "\n WHERE " . $this->dbInstance->quoteName('sessionid') . " = " . $this->dbInstance->quote($session_id);
	
		try {
			$this->dbInstance->setQuery($query);
			$resultInfo = $this->dbInstance->loadObject ();
		} catch (JChatException $e) {
			$this->response = array('status'=>false, 'details'=>$e->getMessage());
			return $this->response;
		} catch (\Exception $e) {
			$jchatException = new JChatException($e->getMessage(), 'error');
			$this->response = array('status'=>false, 'details'=>$jchatException->getMessage());
			return $this->response;
		}
	
		$this->response = array('status'=>true, 'details'=>$resultInfo);
	
		return $this->response;
	}
	
	/**
	 * Load chatrooms from DB using caching system
	 *
	 * @access private
	 * @param int $menuItemid
	 * @return array
	 */
	public function loadChatRooms($menuItemid) {
		// Get user access levels to filter rooms access
		$viewLevels	= implode(',', $this->myUser->getAuthorisedViewLevels());
		// Get menu Itemid
		$menuFiltering = null;
		if($menuItemid) {
			$menuFiltering = "\n AND (ISNULL(rooms.menuitems) OR rooms.menuitems REGEXP '(^|[^0-9])$menuItemid([^0-9]|$)')";
		}
		
		$sql = "SELECT rooms.id, rooms.name, 0 AS numusers, NULL AS users," .
				"\n rooms.description, rooms.ordering" .
				"\n FROM #__jchat_rooms AS rooms" .
				"\n WHERE rooms.published = 1" .
				"\n AND rooms.access IN (" . $viewLevels . ")" .
				$menuFiltering .
				"\n ORDER BY rooms.ordering ASC";
	
		$this->dbInstance->setQuery($sql);
		$rooms = $this->dbInstance->loadAssocList('ordering');
	
		return $rooms;
	}
	
	/**
	 * Get user profile based on integration type
	 *
	 * @access public
	 * @param int $id
	 * @param string $name
	 * @param Object $cParams
	 * @return string
	 */
	public function formatUserProfileLink($id, $name, $cParams) {
		$profileLink = null;
		$Itemid = $this->componentParams->get('social_menu_item', 0);
		$Itemid = $Itemid ? '&Itemid=' . $Itemid : null;
	
		// The Joomla user profile link takes always the highest priority if enabled
		if ($cParams->get ( 'joomlauser_profilelink', 0 )) {
			$query = $this->dbInstance->getQuery(true);
			
			$query->select('MAX(contact.id) AS contactid, contact.alias, contact.catid');
			$query->from($this->dbInstance->quoteName('#__contact_details', 'contact'));
			$query->where('contact.published = 1');
			$query->where('contact.user_id = ' . (int) $id);
			if (Multilanguage::isEnabled() === true) {
				$query->where('(contact.language in '
						. '(' . $this->dbInstance->quote(Factory::getLanguage()->getTag()) . ',' . $this->dbInstance->quote('*') . ') '
						. ' OR contact.language IS NULL)');
			}
			$this->dbInstance->setQuery($query);
			$contactObject = $this->dbInstance->loadObject();
			
			if(!$contactObject->contactid) {
				return null;
			}
			$contactLink = Route::_(\Joomla\Component\Contact\Site\Helper\RouteHelper::getContactRoute($contactObject->contactid . ':' . $contactObject->alias, $contactObject->catid));
			
			return $contactLink;
		}
		
		$integrationType = $this->componentParams->get('3pdintegration', null);
		// Evaluate if integration type is activated
		if($integrationType === 'jomsocial') {
			// Format fo JomSocial
			$profileLink = Route::_('index.php?option=com_community&view=profile&userid=' . $id . $Itemid);
		} elseif($integrationType === 'easysocial') {
			// Format for EasySocial users
			if($cParams->get('usefullname', 'username') == 'name') {
				$transformUser = Factory::getContainer()->get(\Joomla\CMS\User\UserFactoryInterface::class)->loadUserById($id);
				$name = $transformUser->username;
			}
			$formattedName = OutputFilter::stringURLSafe($name);
			if($this->componentParams->get('easysocial_profilelink_id', 1)) {
				$profileLink = Route::_('index.php?option=com_easysocial&view=profile&id=' . $id . '-' . $formattedName . $Itemid);
			} else {
				$profileLink = Route::_('index.php?option=com_easysocial&view=profile&id=' . $formattedName . $Itemid);
			}
		} elseif($integrationType === 'cbuilder') {
			global $_CB_framework;
			// Format for CB users
			if (! file_exists ( JPATH_ADMINISTRATOR . '/components/com_comprofiler/plugin.foundation.php' )) {
				// Format for CB users
				$profileLink = Route::_('index.php?option=com_comprofiler&task=userprofile&user=' . $id . $Itemid);
				return $profileLink;
			}
			include_once (JPATH_ADMINISTRATOR . '/components/com_comprofiler/plugin.foundation.php');
			cbimport ( 'cb.html' );
			cbimport ( 'language.front' );
			$profileLink = $_CB_framework->userProfileUrl ( $id );
		} elseif($integrationType === 'kunena') {
			// Format for Kunena users
			$profileLink = Route::_('index.php?option=com_kunena&view=user&userid=' . $id . $Itemid);
		} elseif($integrationType === 'k2user') {
			// Format for K2 users
			if($cParams->get('usefullname', 'username') == 'username') {
				$transformUser = Factory::getContainer()->get(\Joomla\CMS\User\UserFactoryInterface::class)->loadUserById($id);
				$name = $transformUser->name;
			}
			$formattedName = OutputFilter::stringURLSafe($name);
			$profileLink = Route::_('index.php?option=com_k2&view=itemlist&task=user&id=' . $id . ':' . $formattedName . $Itemid);
		}
	
		return $profileLink;
	}
	
	/**
	 * Retrieve old messages based on time period and private conversation
	 * between to logged in and registered users
	 *
	 * @access public
	 * @param int $fromLoggedID
	 * @param string $fromUserID
	 * @param string $timePeriod
	 * @param int $minMessageId
	 * @return array
	 */
	public function fetchHistoryMessages($fromLoggedID, $fromUserID, $timePeriod, $minMessageId) {
		// Empty initialization
		$historyMessages = array();
		$limitMessages = null;
		
		// Calculate starting period to retrieve messages
		switch($timePeriod) {
			case '1d':
				$periodTimeSeconds = 60 * 60 * 24;
				$minMessagesTime = time() - $periodTimeSeconds;
				break;
			case '1w':
				$periodTimeSeconds = 60 * 60 * 24 * 7;
				$minMessagesTime = time() - $periodTimeSeconds;
				break;
			case '1m':
				$periodTimeSeconds = 60 * 60 * 24 * 30;
				$minMessagesTime = time() - $periodTimeSeconds;
				break;
			case '3m':
				$periodTimeSeconds = 60 * 60 * 24 * 90;
				$minMessagesTime = time() - $periodTimeSeconds;
				break;
			case '6m':
				$periodTimeSeconds = 60 * 60 * 24 * 180;
				$minMessagesTime = time() - $periodTimeSeconds;
				break;
			case '1y':
				$periodTimeSeconds = 60 * 60 * 24 * 365;
				$minMessagesTime = time() - $periodTimeSeconds;
				break;
		}
		
		try {
			$filter = InputFilter::getInstance();
			$userFieldName = $filter->clean($this->componentParams->get('usefullname', 'username'), 'word');
			
			// Exclude already showed and exchanged messages if any
			if((bool)$minMessageId) {
				$limitMessages = "\n AND cchat.id < " . (int)$minMessageId;
			}
			
			$sql = 	"SELECT cchat.id, " .
					"\n " . $this->dbInstance->quote($fromUserID) . " AS " . $this->dbInstance->quoteName('from') . "," .
					"\n " . $this->dbInstance->quote($this->userSessionTable->session_id) . " AS " . $this->dbInstance->quoteName('to') . "," .
					"\n cchat.message, cchat.sent, cchat.read, cchat.type, cchat.status, u.id AS userid, " .
					"\n u.$userFieldName AS " . $this->dbInstance->quoteName('fromusername') .
					"\n FROM #__jchat AS cchat" .
					"\n INNER JOIN #__users AS u ON cchat.fromuser = u.id" .
					"\n WHERE ((cchat.touser = ". $this->dbInstance->quote($this->myUser->id) . 
					"\n AND cchat.fromuser = " . $this->dbInstance->quote($fromLoggedID) . ")" .
					"\n OR (cchat.fromuser = ". $this->dbInstance->quote($this->myUser->id) . 
					"\n AND cchat.touser = " . $this->dbInstance->quote($fromLoggedID) . "))" .
					"\n AND cchat.sent > " . (int)$minMessagesTime .
					$limitMessages .
					"\n ORDER BY cchat.id ASC";
			
			$this->dbInstance->setQuery($sql);
			$rows = $this->dbInstance->loadAssocList();
			
			if(is_array($rows) && count($rows)) {
				// Add new received messages on stream if any
				foreach ($rows as $chatmessage) {
					$self = $chatmessage['userid'] == $this->myUser->id ? 1 : 0;
					
					// Get profile link
					$chatmessage['profilelink'] = $this->getUserProfileLink($chatmessage['userid'], $chatmessage['fromusername'], $this->componentParams);
			
					$messageUserTime = HTMLHelper::_('date', $chatmessage['sent'], Text::_('DATE_FORMAT_LC2'));
					$historyMessages[] = array( 'id' => $chatmessage['id'],
												'from' => $chatmessage['from'],
												'fromusername' => @$chatmessage['fromusername'],
												'profilelink' => @$chatmessage['profilelink'],
												'message' => stripslashes($chatmessage['message']),
												'type' => @$chatmessage['type'],
												'status' => @$chatmessage['status'],
												'time' => $messageUserTime,
												'self' => $self,
												'old' => 0);
			
					// Store new streamed messages into session if not own messages, old messages and already read
					$this->sessionName['jchat_user_'.$chatmessage['from']][$chatmessage['id']] = array(
																							  'id' => $chatmessage['id'],
																							  'from' => $chatmessage['from'],
																							  'fromusername' => @$chatmessage['fromusername'],
																							  'profilelink' => @$chatmessage['profilelink'],
																							  'message' => stripslashes($chatmessage['message']),
																							  'type' => @$chatmessage['type'],
																							  'status' => @$chatmessage['status'],
																							  'time' => $messageUserTime,
																							  'self' => $self,
																							  'old' => 1);
				}
			}
		} catch (JChatException $e) {
			$this->response = array('status'=>false, 'details'=>$e->getMessage());
			return $this->response;
		
		} catch (\Exception $e) {
			$jchatException = new JChatException($e->getMessage(), 'error');
			$this->response = array('status'=>false, 'details'=>$jchatException->getMessage());
			return $this->response;
		}
		
		if($minMessageId !== null) {
			$this->response = array('status'=>true, 'messages'=>$historyMessages);
		} else {
			if(!isset($this->response['messages'])) {
				$this->response['messages'] = array();
			}
			$this->response['messages'] = array_merge($this->response['messages'], $historyMessages);
		}
		
		return $this->response;
	}

	/** 
	 * Class contructor
	 * 
	 * @access public
	 * @param $config array
	 * @return Object
	 */
	public function __construct($config = array(), MVCFactoryInterface $factory = null) {
		$this->getComponentParams();
		$this->response = array();
		$this->messages = array();
		$this->wallMessages = array();
		$this->myUser = Factory::getApplication()->getIdentity();
		$this->integratedExtensions = $this->componentParams->get('3pdintegration', null);
		$this->typingStatusChanged = false;
		$this->typingTo = null;
		$this->typingEnabled = $this->componentParams->get('typing_enabled', true);
		$this->meetingsFeatureEnabled = $this->componentParams->get('guestenabled', 1) == 2 && $this->componentParams->get('meetings_enabled', 0);
		
		// User session table instance
		$this->userSessionTable = $config['sessiontable'];
		
		parent::__construct ( $config, $factory );
		
		// Evaluate the shared session option for SQL queries
		$sharedSession = (int)$this->app->get('shared_session', null);
		if($sharedSession == 1 && $this->componentParams->get('shared_session_support', 1)) {
			$this->sessClientId = '(sess.client_id = 0 OR ISNULL(sess.client_id))';
			$this->sessionClientId = '(session.client_id = 0 OR ISNULL(session.client_id))';
		} else {
			$this->sessClientId = 'sess.client_id = 0';
			$this->sessionClientId = 'session.client_id = 0';
		}
		
		// Patch for propagating DB session lifetime if the native PHP sessions handler is active
		$config = $this->app->getConfig ();
		if($config->get ( 'session_handler' ) != 'database') {
			try {
				$time = time();
				// The modulus introduces a little entropy, making the flushing less accurate but fires the query less than half the time.
				$query = $this->dbInstance->getQuery(true)
									      ->update($this->dbInstance->quoteName('#__session'))
									      ->set($this->dbInstance->quoteName('time') . ' = ' . $time)
									      ->where($this->dbInstance->quoteName('session_id') . ' = ' . $this->dbInstance->quote($this->userSessionTable->session_id));
		
				$this->dbInstance->setQuery($query);
				$this->dbInstance->execute();
			} catch (\Exception $e) {
				// No errors managed, leave the process go on anyway
			}
		}
		
		// Setup the live streaming feature
		$query = $this->_db->getQuery(true)
						   ->select('id')
						   ->from($this->dbInstance->quoteName('#__menu'))
						   ->where($this->dbInstance->quoteName('link') . ' = ' . $this->_db->quote('index.php?option=com_jchat&view=livestreaming'))
						   ->where($this->dbInstance->quoteName('published') . ' = 1' );
		// Set the query for execution
		$this->dbInstance->setQuery($query);
		$this->livestreamingFeatureEnabled = $this->dbInstance->loadResult();
		
		// Set my chatroom if chatrrom mode is enabled, for buddylist my chatroom users filtering and stream messages
		$this->myChatRoom = $this->getMyChatRoom();
	}
}