<?php

/**
 * Controller for attachment-related actions.
 *
 * @package XenForo_Attachment
 */
class XenForo_ControllerPublic_Attachment extends XenForo_ControllerPublic_Abstract
{
	/**
	 * Viewing an attachment.
	 *
	 * @return XenForo_ControllerResponse_Abstract
	 */
	public function actionIndex()
	{
		$attachmentId = $this->_input->filterSingle('attachment_id', XenForo_Input::UINT);
		$attachment = $this->_getAttachmentOrError($attachmentId);

		$tempHash = $this->_input->filterSingle('temp_hash', XenForo_Input::STRING);

		$attachmentModel = $this->_getAttachmentModel();

		if (!$attachmentModel->canViewAttachment($attachment, $tempHash))
		{
			return $this->responseNoPermission();
		}

		$filePath = $attachmentModel->getAttachmentDataFilePath($attachment);
		if (!file_exists($filePath) || !is_readable($filePath))
		{
			return $this->responseError(new XenForo_Phrase('attachment_cannot_be_shown_at_this_time'));
		}

		if (!$this->_input->filterSingle('no_canonical', XenForo_Input::UINT))
		{
			$this->canonicalizeRequestUrl(
				XenForo_Link::buildPublicLink('attachments', $attachment)
			);
		}

		$eTag = $this->_request->getServer('HTTP_IF_NONE_MATCH');
		$eTagExpected =  '"' . $attachment['attach_date'] . '"';
		if ($eTag && ($eTag == $eTagExpected || $eTag == "W/$eTagExpected"))
		{
			$this->_routeMatch->setResponseType('raw');
			return $this->responseView('XenForo_ViewPublic_Attachment_View304');
		}

		$attachmentModel->logAttachmentView($attachmentId);

		$this->_routeMatch->setResponseType('raw');

		$viewParams = array(
			'attachment' => $attachment,
			'attachmentFile' => $filePath
		);

		return $this->responseView('XenForo_ViewPublic_Attachment_View', '', $viewParams);
	}

	/**
	 * Shows the form for uploading and managing attachments in various contexts.
	 *
	 * @return XenForo_ControllerResponse_Abstract
	 */
	public function actionUpload()
	{
		$input = $this->_input->filter(array(
			'hash' => XenForo_Input::STRING,
			'content_type' => XenForo_Input::STRING,
			'content_data' => array(XenForo_Input::UINT, 'array' => true),
			'key' => XenForo_Input::STRING
		));
		if (!$input['hash'])
		{
			$input['hash'] = $this->_input->filterSingle('temp_hash', XenForo_Input::STRING);
		}

		$this->_assertCanUploadAndManageAttachments($input['hash'], $input['content_type'], $input['content_data']);

		$attachmentModel = $this->_getAttachmentModel();
		$attachmentHandler = $attachmentModel->getAttachmentHandler($input['content_type']); // known to be valid
		$contentId = $attachmentHandler->getContentIdFromContentData($input['content_data']);

		$existingAttachments = ($contentId
			? $attachmentModel->getAttachmentsByContentId($input['content_type'], $contentId)
			: array()
		);
		$newAttachments = $attachmentModel->getAttachmentsByTempHash($input['hash']);

		$constraints = $attachmentHandler->getAttachmentConstraints();
		if ($constraints['count'] <= 0)
		{
			$canUpload = true;
			$remainingUploads = true;
		}
		else
		{
			$remainingUploads = $constraints['count'] - (count($existingAttachments) + count($newAttachments));
			$canUpload = ($remainingUploads > 0);
		}

		$viewParams = array(
			'attachmentConstraints' => $constraints,
			'existingAttachments' => $existingAttachments,
			'newAttachments' => $newAttachments,

			'canUpload' => $canUpload,
			'remainingUploads' => $remainingUploads,

			'hash' => $input['hash'],
			'contentType' => $input['content_type'],
			'contentData' => $input['content_data'],
			'attachmentParams' => array(
				'hash' => $input['hash'],
				'content_type' => $input['content_type'],
				'content_data' => $input['content_data']
			),
			'key' => $input['key']
		);

		return $this->responseView('XenForo_ViewPublic_Attachment_Upload', 'attachment_upload', $viewParams);
	}

	/**
	 * Handles uploading new attachments (and redirecting delete requests).
	 *
	 * @return XenForo_ControllerResponse_Abstract
	 */
	public function actionDoUpload()
	{
		$this->_assertPostOnly();

		$deleteArray = array_keys($this->_input->filterSingle('delete', XenForo_Input::ARRAY_SIMPLE));
		$delete = reset($deleteArray);
		if ($delete)
		{
			$this->_request->setParam('attachment_id', $delete);
			return $this->responseReroute(__CLASS__, 'delete');
		}

		$input = $this->_input->filter(array(
			'hash' => XenForo_Input::STRING,
			'content_type' => XenForo_Input::STRING,
			'content_data' => array(XenForo_Input::UINT, 'array' => true),
			'key' => XenForo_Input::STRING
		));
		if (!$input['hash'])
		{
			$input['hash'] = $this->_input->filterSingle('temp_hash', XenForo_Input::STRING);
		}

		$this->_assertCanUploadAndManageAttachments($input['hash'], $input['content_type'], $input['content_data']);

		$attachmentModel = $this->_getAttachmentModel();
		$attachmentHandler = $attachmentModel->getAttachmentHandler($input['content_type']); // known to be valid
		$contentId = $attachmentHandler->getContentIdFromContentData($input['content_data']);

		$existingAttachments = ($contentId
			? $attachmentModel->getAttachmentsByContentId($input['content_type'], $contentId)
			: array()
		);
		$newAttachments = $attachmentModel->getAttachmentsByTempHash($input['hash']);

		$attachmentConstraints = $attachmentHandler->getAttachmentConstraints();

		if ($attachmentConstraints['count'] > 0)
		{
			$remainingUploads = $attachmentConstraints['count'] - (count($existingAttachments) + count($newAttachments));
			if ($remainingUploads <= 0)
			{
				return $this->responseError(new XenForo_Phrase(
					'you_may_not_upload_more_files_with_message_allowed_x',
					array('total' => $attachmentConstraints['count'])
				));
			}
		}

		$file = XenForo_Upload::getUploadedFile('upload');
		if (!$file)
		{
			return $this->responseRedirect(
				XenForo_ControllerResponse_Redirect::SUCCESS,
				XenForo_Link::buildPublicLink('attachments/upload', false, array(
					'hash' => $input['hash'],
					'content_type' => $input['content_type'],
					'content_data' => $input['content_data'],
					'key' => $input['key']
				))
			);
		}

		$filename = $this->_input->filterSingle('filename', XenForo_Input::STRING);
		if ($filename)
		{
			$file->setFileName($filename);
		}

		$file->setConstraints($attachmentConstraints);
		if (!$file->isValid())
		{
			return $this->responseError($file->getErrors());
		}
		$dataId = $attachmentModel->insertUploadedAttachmentData($file, XenForo_Visitor::getUserId());
		$attachmentId = $attachmentModel->insertTemporaryAttachment($dataId, $input['hash']);

		$message = new XenForo_Phrase('upload_completed_successfully');

		// return a view if noredirect has been requested and we are not deleting
		if ($this->_noRedirect())
		{
			$contentId = $attachmentHandler->getContentIdFromContentData($input['content_data']);

			//$newAttachments = $attachmentModel->getAttachmentsByTempHash($input['hash']);

			$attachment = $attachmentModel->getAttachmentById($attachmentId);

			$viewParams = array(
				'attachment' => $attachmentModel->prepareAttachment($attachment),
				'message' => $message,
				'hash' => $input['hash'],
				'content_type' => $input['content_type'],
				'content_data' => $input['content_data'],
				'key' => $input['key']
			);

			return $this->responseView('XenForo_ViewPublic_Attachment_DoUpload', '', $viewParams);
		}
		else
		{
			return $this->responseRedirect(
				XenForo_ControllerResponse_Redirect::SUCCESS,
				XenForo_Link::buildPublicLink('attachments/upload', false, array(
					'hash' => $input['hash'],
					'content_type' => $input['content_type'],
					'content_data' => $input['content_data'],
					'key' => $input['key']
				)),
				$message
			);
		}
	}

	/**
	 * Deletes the specified attachment.
	 *
	 * @return XenForo_ControllerResponse_Abstract
	 */
	public function actionDelete()
	{
		$this->_assertPostOnly();

		$input = $this->_input->filter(array(
			'attachment_id' => XenForo_Input::UINT,
			'hash' => XenForo_Input::STRING,
			'content_type' => XenForo_Input::STRING,
			'content_data' => array(XenForo_Input::UINT, 'array' => true)
		));
		if (!$input['hash'])
		{
			$input['hash'] = $this->_input->filterSingle('temp_hash', XenForo_Input::STRING);
		}

		$attachment = $this->_getAttachmentOrError($input['attachment_id']);
		if (!$this->_getAttachmentModel()->canDeleteAttachment($attachment, $input['hash']))
		{
			return $this->responseNoPermission();
		}

		$dw = XenForo_DataWriter::create('XenForo_DataWriter_Attachment');
		$dw->setExistingData($attachment, true);
		$dw->delete();

		return $this->responseRedirect(
			XenForo_ControllerResponse_Redirect::SUCCESS,
			XenForo_Link::buildPublicLink('attachments/upload', false, array(
				'hash' => $input['hash'],
				'content_type' => $input['content_type'],
				'content_data' => $input['content_data']
			))
		);
	}

	public function updateSessionActivity($controllerResponse, $controllerName, $action) {}

	/**
	 * Asserts that the viewing user can upload and manage attachments.
	 *
	 * @param string $hash Unique hash
	 * @param string $contentType
	 * @param array $contentData
	 */
	protected function _assertCanUploadAndManageAttachments($hash, $contentType, array $contentData)
	{
		if (!$hash)
		{
			throw $this->getNoPermissionResponseException();
		}

		$attachmentHandler = $this->_getAttachmentModel()->getAttachmentHandler($contentType);
		if (!$attachmentHandler || !$attachmentHandler->canUploadAndManageAttachments($contentData))
		{
			 throw $this->getNoPermissionResponseException();
		}
	}

	/**
	 * Gets the specified attachment or throws an error.
	 *
	 * @param integer $attachment
	 *
	 * @return array
	 */
	protected function _getAttachmentOrError($attachmentId)
	{
		$attachment = $this->_getAttachmentModel()->getAttachmentById($attachmentId);
		if (!$attachment)
		{
			throw $this->responseException($this->responseError(new XenForo_Phrase('requested_attachment_not_found'), 404));
		}

		return $attachment;
	}

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