<?php

/**
* Data writer for profile posts.
*
* @package XenForo_ProfilePost
*/
class XenForo_DataWriter_DiscussionMessage_ProfilePost extends XenForo_DataWriter_DiscussionMessage
{
	const DATA_PROFILE_USER = 'profileUser';

	const OPTION_MAX_TAGGED_USERS = 'maxTaggedUsers';

	protected $_taggedUsers = array();

	/**
	 * Gets the object that represents the definition of this type of message.
	 *
	 * @return XenForo_DiscussionMessage_Definition_Abstract
	 */
	public function getDiscussionMessageDefinition()
	{
		return new XenForo_DiscussionMessage_Definition_ProfilePost();
	}

	/**
	* Gets the actual existing data out of data that was passed in. See parent for explanation.
	*
	* @param mixed
	*
	* @return array|false
	*/
	protected function _getExistingData($data)
	{
		if (!$profilePostId = $this->_getExistingPrimaryKey($data))
		{
			return false;
		}

		return array($this->getDiscussionMessageTableName() => $this->_getProfilePostModel()->getProfilePostById($profilePostId));
	}

	/**
	* Gets the fields that are defined for the table. See parent for explanation.
	*
	* @return array
	*/
	protected function _getFields()
	{
		$fields = $this->_getCommonFields();

		$structure = $this->_messageDefinition->getMessageStructure();

		$fields[$structure['table']] += array(
			'comment_count'      => array('type' => self::TYPE_UINT_FORCED, 'default' => 0),
			'first_comment_date' => array('type' => self::TYPE_UINT, 'default' => 0),
			'last_comment_date'  => array('type' => self::TYPE_UINT, 'default' => 0),
			'latest_comment_ids' => array('type' => self::TYPE_BINARY, 'default' => '', 'maxLength' => 100)
		);

		return $fields;
	}

	/**
	* Gets the default set of options for this data writer.
	*
	* @return array
	*/
	protected function _getDefaultOptions()
	{
		$options = parent::_getDefaultOptions();
		$options[self::OPTION_MAX_MESSAGE_LENGTH] = 420;
		$options[self::OPTION_MAX_TAGGED_USERS] = 0;

		return $options;
	}

	protected function _publishAndNotify()
	{
		parent::_publishAndNotify();

		// send an alert if the message is visible, is not posted on the user's own profile
		// is an insert, or is an update with message state changed from 'moderated'

		$isAlertable = (
			$this->get('message_state') == 'visible'
			&& ($this->isInsert() || $this->getExisting('message_state') == 'moderated')
		);
		if ($isAlertable)
		{
			$alerted = $this->_alertUser();

			$maxTagged = $this->getOption(self::OPTION_MAX_TAGGED_USERS);
			if ($maxTagged && $this->_taggedUsers)
			{
				if ($maxTagged > 0)
				{
					$alertTagged = array_slice($this->_taggedUsers, 0, $maxTagged, true);
				}
				else
				{
					$alertTagged = $this->_taggedUsers;
				}

				if (!$profileUser = $this->getExtraData(self::DATA_PROFILE_USER))
				{
					$profileUser = $this->_getUserModel()->getUserById($this->get('profile_user_id'), array(
						'join' => XenForo_Model_User::FETCH_USER_FULL
					));
				}

				$profilePost = $this->getMergedData();
				$this->_getProfilePostModel()->alertTaggedMembers(
					$profilePost, $profileUser, $alertTagged, $alerted
				);
			}
		}
	}

	/**
	 * Sends an alert to the profile owner
	 *
	 * Note: ensure that you $dw->setExtraData(self::PROFILE_USER, $profileUser) to save a query
	 *
	 * @return array List of user IDs alerted
	 */
	protected function _alertUser()
	{
		if ($this->get('profile_user_id') == $this->get('user_id'))
		{
			return array();
		}

		$userModel = $this->_getUserModel();
		$alerted = array();

		if (!$profileUser = $this->getExtraData(self::DATA_PROFILE_USER))
		{
			$profileUser = $userModel->getUserById($this->get('profile_user_id'), array(
				'join' => XenForo_Model_User::FETCH_USER_FULL
			));
		}

		if (!$userModel->isUserIgnored($profileUser, $this->get('user_id'))
			&& XenForo_Model_Alert::userReceivesAlert($profileUser, $this->getContentType(), 'insert')
		)
		{
			XenForo_Model_Alert::alert(
				$this->get('profile_user_id'),
				$this->get('user_id'),
				$this->get('username'),
				$this->getContentType(),
				$this->get('profile_post_id'),
				'insert'
			);

			$alerted[] = $this->get('profile_user_id');
		}

		return $alerted;
	}

	protected function _messagePreSave()
	{
		if ($this->get('user_id') == $this->get('profile_user_id') && $this->isChanged('message'))
		{
			// statuses are more limited than other posts
			$message = $this->get('message');
			$maxLength = 140;

			$message = preg_replace('/\r?\n/', ' ', $message);

			if (utf8_strlen($message) > $maxLength)
			{
				$this->error(new XenForo_Phrase('please_enter_message_with_no_more_than_x_characters', array('count' => $maxLength)), 'message');
			}

			$this->set('message', $message);
		}

		// do this auto linking after length counting
		/** @var $taggingModel XenForo_Model_UserTagging */
		$taggingModel = $this->getModelFromCache('XenForo_Model_UserTagging');

		$this->_taggedUsers = $taggingModel->getTaggedUsersInMessage(
			$this->get('message'), $newMessage, 'text'
		);
		$this->set('message', $newMessage);
	}

	protected function _updateUserMessageCount($isDelete = false)
	{
		// disable message counting for profile posts - people are just going to get confused
		// by this, plus the messages are basically one liners
	}

	protected function _messagePostSave()
	{
		if ($this->isChanged('message_state') && $this->get('message_state') == 'visible')
		{
			$this->_updateStatus();
		}
		else if ($this->isUpdate() && $this->isChanged('message_state') && $this->getExisting('message_state') == 'visible')
		{
			$this->_hideStatus();
		}

		if ($this->isUpdate() && $this->get('message_state') == 'deleted' && $this->getExisting('message_state') == 'visible')
		{
			$this->getModelFromCache('XenForo_Model_Alert')->deleteAlerts('profile_post', $this->get('profile_post_id'));
		}

		if ($this->isUpdate() && $this->isStatus() && $this->get('message_state') == 'visible' && $this->isChanged('message'))
		{
			$userDw = XenForo_DataWriter::create('XenForo_DataWriter_User', XenForo_DataWriter::ERROR_SILENT);
			$userDw->setExistingData($this->get('user_id'));
			if ($userDw->get('status_profile_post_id') == $this->get('profile_post_id'))
			{
				$userDw->set('status', $this->get('message'));
				$userDw->save();
			}
		}
	}

	protected function _messagePostDelete()
	{
		$this->_hideStatus();

		$this->getModelFromCache('XenForo_Model_Alert')->deleteAlerts('profile_post', $this->get('profile_post_id'));
		$this->_db->delete('xf_profile_post_comment', 'profile_post_id = ' . $this->_db->quote($this->get('profile_post_id')));
	}

	protected function _updateStatus()
	{
		if (!$this->isStatus())
		{
			return;
		}

		$this->_db->query('
			INSERT INTO xf_user_status
				(profile_post_id, user_id, post_date)
			VALUES
				(?, ?, ?)
			ON DUPLICATE KEY UPDATE
				user_id = VALUES(user_id),
				post_date = VALUES(post_date)
		', array($this->get('profile_post_id'), $this->get('user_id'), $this->get('post_date')));

		$userDw = XenForo_DataWriter::create('XenForo_DataWriter_User', XenForo_DataWriter::ERROR_SILENT);
		$userDw->setExistingData($this->get('user_id'));
		if ($this->get('post_date') >= $userDw->get('status_date'))
		{
			$userDw->set('status', $this->get('message'));
			$userDw->set('status_date', $this->get('post_date'));
			$userDw->set('status_profile_post_id', $this->get('profile_post_id'));
			$userDw->save();
		}
	}

	protected function _hideStatus()
	{
		if (!$this->isStatus())
		{
			return;
		}

		$this->_db->delete('xf_user_status', 'profile_post_id = ' . $this->_db->quote($this->get('profile_post_id')));

		$userDw = XenForo_DataWriter::create('XenForo_DataWriter_User');
		$userDw->setExistingData($this->get('user_id'));
		if ($userDw->get('status_profile_post_id') == $this->get('profile_post_id'))
		{
			$userDw->set('status', '');
			$userDw->set('status_date', 0);
			$userDw->set('status_profile_post_id', 0);
			$userDw->save();
		}
	}

	/**
	 * Returns true if this message is a status update.
	 *
	 * @return boolean
	 */
	public function isStatus()
	{
		return ($this->get('user_id') == $this->get('profile_user_id'));
	}

	public function rebuildProfilePostCommentCounters()
	{
		$db = $this->_db;
		$profilePostId = $this->get('profile_post_id');

		$counts = $db->fetchRow('
			SELECT COUNT(*) AS comment_count,
				MIN(comment_date) AS first_comment_date,
				MAX(comment_date) AS last_comment_date
			FROM xf_profile_post_comment
			WHERE profile_post_id = ?
		', $profilePostId);

		if ($counts['comment_count'])
		{
			$ids = $db->fetchCol($db->limit(
				'
					SELECT profile_post_comment_id
					FROM xf_profile_post_comment
					WHERE profile_post_id = ?
					ORDER BY comment_date DESC
				', 3
			), $profilePostId);
			$ids = array_reverse($ids); // need last 3, but in oldest first order
		}
		else
		{
			$ids = array();
		}

		$this->bulkSet($counts);
		$this->set('latest_comment_ids', implode(',', $ids));
	}

	public function insertNewComment($commentId, $commentDate)
	{
		$this->set('comment_count', $this->get('comment_count') + 1);
		if (!$this->get('first_comment_date') || $commentDate < $this->get('first_comment_date'))
		{
			$this->set('first_comment_date', $commentDate);
		}
		$this->set('last_comment_date', max($this->get('last_comment_date'), $commentDate));

		$latest = $this->get('latest_comment_ids');
		$ids = ($latest ? explode(',', $latest) : array());
		$ids[] = $commentId;

		if (count($ids) > 3)
		{
			$ids = array_slice($ids, -3);
		}

		$this->set('latest_comment_ids', implode(',', $ids));
	}

	/**
	 * @return XenForo_Model_ProfilePost
	 */
	protected function _getProfilePostModel()
	{
		return $this->getModelFromCache('XenForo_Model_ProfilePost');
	}
}