<?php

namespace SilverStripe\Forms\HTMLEditor;

use SilverStripe\Assets\File;
use SilverStripe\Control\Controller;
use SilverStripe\Core\Convert;
use SilverStripe\Forms\CompositeField;
use SilverStripe\Forms\DateField_Disabled;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\FieldGroup;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\HiddenField;
use SilverStripe\Forms\HTMLReadonlyField;
use SilverStripe\Forms\LiteralField;
use SilverStripe\Forms\ReadonlyField;
use SilverStripe\Forms\TextField;
use SilverStripe\View\ViewableData;

/**
 * Encapsulation of a file which can either be a remote URL
 * or a {@link File} on the local filesystem, exhibiting common properties
 * such as file name or the URL.
 *
 * @todo Remove once core has support for remote files
 */
abstract class HTMLEditorField_File extends ViewableData
{

	/**
	 * Default insertion width for Images and Media
	 *
	 * @config
	 * @var int
	 */
	private static $insert_width = 600;

	/**
	 * Default insert height for images and media
	 *
	 * @config
	 * @var int
	 */
	private static $insert_height = 360;

	/**
	 * Max width for insert-media preview.
	 *
	 * Matches CSS rule for .cms-file-info-preview
	 *
	 * @var int
	 */
	private static $media_preview_width = 176;

	/**
	 * Max height for insert-media preview.
	 *
	 * Matches CSS rule for .cms-file-info-preview
	 *
	 * @var int
	 */
	private static $media_preview_height = 128;

	private static $casting = array(
		'URL' => 'Varchar',
		'Name' => 'Varchar'
	);

	/**
	 * Absolute URL to asset
	 *
	 * @var string
	 */
	protected $url;

	/**
	 * File dataobject (if available)
	 *
	 * @var File
	 */
	protected $file;

	/**
	 * @param string $url
	 * @param File $file
	 */
	public function __construct($url, File $file = null)
	{
		$this->url = $url;
		$this->file = $file;
		$this->failover = $file;
		parent::__construct();
	}

	/**
	 * @return FieldList
	 */
	public function getFields()
	{
		$fields = new FieldList(
			CompositeField::create(
				CompositeField::create(LiteralField::create("ImageFull", $this->getPreview()))
					->setName("FilePreviewImage")
					->addExtraClass('cms-file-info-preview'),
				CompositeField::create($this->getDetailFields())
					->setName("FilePreviewData")
					->addExtraClass('cms-file-info-data')
			)
				->setName("FilePreview")
				->addExtraClass('cms-file-info'),
			TextField::create('CaptionText', _t('HTMLEditorField.CAPTIONTEXT', 'Caption text')),
			DropdownField::create(
				'CSSClass',
				_t('HTMLEditorField.CSSCLASS', 'Alignment / style'),
				array(
					'leftAlone' => _t('HTMLEditorField.CSSCLASSLEFTALONE', 'On the left, on its own.'),
					'center' => _t('HTMLEditorField.CSSCLASSCENTER', 'Centered, on its own.'),
					'left' => _t('HTMLEditorField.CSSCLASSLEFT', 'On the left, with text wrapping around.'),
					'right' => _t('HTMLEditorField.CSSCLASSRIGHT', 'On the right, with text wrapping around.')
				)
			),
			FieldGroup::create(_t('HTMLEditorField.IMAGEDIMENSIONS', 'Dimensions'),
				TextField::create(
					'Width',
					_t('HTMLEditorField.IMAGEWIDTHPX', 'Width'),
					$this->getInsertWidth()
				)->setMaxLength(5),
				TextField::create(
					'Height',
					" x " . _t('HTMLEditorField.IMAGEHEIGHTPX', 'Height'),
					$this->getInsertHeight()
				)->setMaxLength(5)
			)->addExtraClass('dimensions last'),
			HiddenField::create('URL', false, $this->getURL()),
			HiddenField::create('FileID', false, $this->getFileID())
		);
		return $fields;
	}

	/**
	 * Get list of fields for previewing this records details
	 *
	 * @return FieldList
	 */
	protected function getDetailFields()
	{
		$fields = new FieldList(
			ReadonlyField::create("FileType", _t('AssetTableField.TYPE', 'File type'), $this->getFileType()),
			HTMLReadonlyField::create(
				'ClickableURL', _t('AssetTableField.URL', 'URL'), $this->getExternalLink()
			)
		);
		// Get file size
		if ($this->getSize()) {
			$fields->insertAfter(
				'FileType',
				ReadonlyField::create("Size", _t('AssetTableField.SIZE', 'File size'), $this->getSize())
			);
		}
		// Get modified details of local record
		if ($this->getFile()) {
			$fields->push(new DateField_Disabled(
				"Created",
				_t('AssetTableField.CREATED', 'First uploaded'),
				$this->getFile()->Created
			));
			$fields->push(new DateField_Disabled(
				"LastEdited",
				_t('AssetTableField.LASTEDIT', 'Last changed'),
				$this->getFile()->LastEdited
			));
		}
		return $fields;

	}

	/**
	 * Get file DataObject
	 *
	 * Might not be set (for remote files)
	 *
	 * @return File
	 */
	public function getFile()
	{
		return $this->file;
	}

	/**
	 * Get file ID
	 *
	 * @return int
	 */
	public function getFileID()
	{
		if ($file = $this->getFile()) {
			return $file->ID;
		}
		return null;
	}

	/**
	 * Get absolute URL
	 *
	 * @return string
	 */
	public function getURL()
	{
		return $this->url;
	}

	/**
	 * Get basename
	 *
	 * @return string
	 */
	public function getName()
	{
		return $this->file
			? $this->file->Name
			: preg_replace('/\?.*/', '', basename($this->url));
	}

	/**
	 * Get descriptive file type
	 *
	 * @return string
	 */
	public function getFileType()
	{
		return File::get_file_type($this->getName());
	}

	/**
	 * Get file size (if known) as string
	 *
	 * @return string|false String value, or false if doesn't exist
	 */
	public function getSize()
	{
		if ($this->file) {
			return $this->file->getSize();
		}
		return false;
	}

	/**
	 * HTML content for preview
	 *
	 * @return string HTML
	 */
	public function getPreview()
	{
		$preview = $this->extend('getPreview');
		if ($preview) {
			return $preview;
		}

		// Generate tag from preview
		$thumbnailURL = Convert::raw2att(
			Controller::join_links($this->getPreviewURL(), "?r=" . rand(1, 100000))
		);
		$fileName = Convert::raw2att($this->Name);
		return sprintf(
			"<img id='thumbnailImage' class='thumbnail-preview'  src='%s' alt='%s' />\n",
			$thumbnailURL,
			$fileName
		);
	}

	/**
	 * HTML Content for external link
	 *
	 * @return string
	 */
	public function getExternalLink()
	{
		$title = $this->file
			? $this->file->getTitle()
			: $this->getName();
		return sprintf(
			'<a href="%1$s" title="%2$s" target="_blank" rel="external" class="file-url">%1$s</a>',
			Convert::raw2att($this->url),
			Convert::raw2att($title)
		);
	}

	/**
	 * Generate thumbnail url
	 *
	 * @return string
	 */
	public function getPreviewURL()
	{
		// Get preview from file
		if ($this->file) {
			return $this->getFilePreviewURL();
		}

		// Generate default icon html
		return File::get_icon_for_extension($this->getExtension());
	}

	/**
	 * Generate thumbnail URL from file dataobject (if available)
	 *
	 * @return string
	 */
	protected function getFilePreviewURL()
	{
		// Get preview from file
		if ($this->file) {
			$width = $this->config()->media_preview_width;
			$height = $this->config()->media_preview_height;
			return $this->file->ThumbnailURL($width, $height);
		}
		return null;
	}

	/**
	 * Get file extension
	 *
	 * @return string
	 */
	public function getExtension()
	{
		$extension = File::get_file_extension($this->getName());
		return strtolower($extension);
	}

	/**
	 * Category name
	 *
	 * @return string
	 */
	public function appCategory()
	{
		if ($this->file) {
			return $this->file->appCategory();
		} else {
			return File::get_app_category($this->getExtension());
		}
	}

	/**
	 * Get height of this item
	 */
	public function getHeight()
	{
		if ($this->file) {
			$height = $this->file->getHeight();
			if ($height) {
				return $height;
			}
		}
		return $this->config()->insert_height;
	}

	/**
	 * Get width of this item
	 *
	 * @return int
	 */
	public function getWidth()
	{
		if ($this->file) {
			$width = $this->file->getWidth();
			if ($width) {
				return $width;
			}
		}
		return $this->config()->insert_width;
	}

	/**
	 * Provide an initial width for inserted media, restricted based on $embed_width
	 *
	 * @return int
	 */
	public function getInsertWidth()
	{
		$width = $this->getWidth();
		$maxWidth = $this->config()->insert_width;
		return ($width <= $maxWidth) ? $width : $maxWidth;
	}

	/**
	 * Provide an initial height for inserted media, scaled proportionally to the initial width
	 *
	 * @return int
	 */
	public function getInsertHeight()
	{
		$width = $this->getWidth();
		$height = $this->getHeight();
		$maxWidth = $this->config()->insert_width;
		return ($width <= $maxWidth) ? $height : round($height * ($maxWidth / $width));
	}

}