<?php
/**
 * Attachment
 *
 * @author   Ryuji AMANO <ryuji@ryus.co.jp>
 * @link http://www.netcommons.org NetCommons Project
 * @license http://www.netcommons.org/license.txt NetCommons License
 */

App::uses('FilesAppModel', 'Files.Model');
App::uses('Folder', 'Utility');

/**
 * Summary for File Model
 */

class UploadFile extends FilesAppModel {

/**
 * @var string UploadFileでアップロードする基準パス
 */
	public $uploadBasePath = WWW_ROOT;

/**
 * @var int recursiveはデフォルトアソシエーションなしに
 */
	public $recursive = -1;

/**
 * ビヘイビア
 *
 * @var array
 */
	public $actsAs = [
		'Upload.Upload' => [
			'real_file_name' => array(
					'thumbnailSizes' => array(
						// NC2 800 , 640, 480だった
							'big' => '800ml',
							'medium' => '400ml',
							'small' => '200ml',
							'thumb' => '80x80',
					),
					'nameCallback' => 'nameCallback',
					'fields' => [
							//'dir' => 'path',
							'type' => 'mimetype',
							'size' => 'size'
					],
					// https://github.com/josegonzalez/cakephp-upload/issues/263
					// 上記修正がUploadビヘイビアにとりこまれるまで false
					'deleteFolderOnDelete' => false,
			),
		],
		'Files.UploadFileValidate',
	];

/**
 * hasMany
 *
 * @var array
 */
	public $hasMany = [
			'UploadFilesContent' => array(
					'className' => 'Files.UploadFilesContent',
			),
	];

/**
 * beforeValidate
 *
 * @param array $options options
 * @return bool
 */
	public function beforeValidate($options = array()) {
		// 拡張子チェック
		$uploadAllowExtension = $this->getAllowExtension();
		$this->validate['real_file_name']['extension'] = [
			'rule' => ['isValidExtension', $uploadAllowExtension, false],
			'message' => __d('files', 'It is upload disabled file format')
		];
		$this->validate['real_file_name']['size'] = 'validateRoomFileSizeLimit';

		return parent::beforeValidate($options);
	}

/**
 * ビヘイビア設定
 *
 * @param array $options オプション
 * @return void
 */
	public function setOptions($options) {
		$this->uploadSettings('real_file_name', $options);
	}

/**
 * アップロードファイルとコンテンツとの関連づけ削除
 *
 * @param int $contentId コンテンツID
 * @param int $fileId アップロードファイルID
 * @throws InternalErrorException
 * @return void
 */
	public function removeFile($contentId, $fileId) {
		if (! $contentId || ! $fileId) {
			return;
		}
		$UploadFilesContent = ClassRegistry::init('Files.UploadFilesContent');
		$link = $UploadFilesContent->findByContentIdAndUploadFileId($contentId, $fileId);
		if ($link) {
			// 関連レコードみつかったら削除する
			if ($UploadFilesContent->delete($link['UploadFilesContent']['id'], false) === false) {
				throw new InternalErrorException('Failed UploadFile::removeFile()');
			}
			// ファイルIDの関連テーブルが他に見つからなかったらファイルも削除する
			$count = $UploadFilesContent->find('count', ['conditions' => ['upload_file_id' => $fileId]]);
			if ($count == 0) {
				// 他に関連レコード無ければファイル削除
				if ($this->deleteUploadFile($fileId) === false) {
					throw new InternalErrorException('Failed UploadFile::removeFile()');
				}
			}
		}
	}

/**
 * Delete uploadFile
 *
 * @param int $fileId UploadFile.id
 * @return bool
 */
	public function deleteUploadFile($fileId) {
		// Uploadビヘイビアにpathを渡す
		$uploadFile = $this->findById($fileId);
		$path = $this->uploadBasePath . $uploadFile['UploadFile']['path'];
		$this->uploadSettings('real_file_name', 'path', $path);
		$this->uploadSettings('real_file_name', 'thumbnailPath', $path);

		return $this->delete($fileId, false);
	}

/**
 * nameCallback method
 *
 * @param string $field Name of field being modified
 * @param string $currentName current filename
 * @param array $data Array of data being manipulated in the current request
 * @param array $options Array of options for the current rename
 * @return string file name
 */
	public function nameCallback($field, $currentName, $data, $options) {
		//return Security::hash(mt_rand() . microtime(), 'md5')
		return Security::hash($currentName, 'md5') . '.' . pathinfo($currentName, PATHINFO_EXTENSION);
	}

/**
 * beforeSave
 *
 * ファイルの保存先を設定
 * トータルダウンロード数を設定
 *
 * @param array $options オプション
 * @return void
 */
	public function beforeSave($options = array()) {
		// imagickクラスがなかったらサムネイル生成はGDを利用
		if (class_exists('imagick') === false) {
			// @codeCoverageIgnoreStart
			$this->uploadSettings('real_file_name', 'thumbnailMethod', '_resizePhp');
			// @codeCoverageIgnoreEnd
		}

		$roomId = Current::read('Room.id');
		$path = $this->uploadBasePath . 'files' . DS .
			'upload_file' . DS . 'real_file_name' . DS . $roomId . DS;

		// ID以外のpathを保存 WWW_ROOTも除外する
		$path = substr($path, strlen($this->uploadBasePath));
		$this->data['UploadFile']['path'] = $path;

		$this->uploadSettings('real_file_name', 'path', $path);
		$this->uploadSettings('real_file_name', 'thumbnailPath', $path);

		// トータルダウンロード数設定
		if ($this->data['UploadFile']['content_key']) {
			$this->virtualFields['total'] = 'sum(download_count)';
			$options = [
					'fields' => ['total'],
					'conditions' => [
							'plugin_key' => $this->data['UploadFile']['plugin_key'],
							'content_key' => $this->data['UploadFile']['content_key'],
							'field_name' => $this->data['UploadFile']['field_name'],
					]
			];
			if (Hash::get($this->data, 'UploadFile.id', false) === false) {
				// 新規の時だけトータルをセット
				$result = $this->find('first', $options);
				$total = ($result['UploadFile']['total'] !== null) ? $result['UploadFile']['total'] : 0;
				$this->data['UploadFile']['total_download_count'] = $total;
			}
			unset($this->virtualFields['total']);
		}

		return true;
	}

/**
 * ファイル情報取得
 *
 * @param string $pluginKey プラグインキー
 * @param int $contentId コンテンツID
 * @param string $fieldName フィールド名
 * @return array|false
 */
	public function getFile($pluginKey, $contentId, $fieldName) {
		$options = [
			'conditions' => [
				'UploadFilesContent.plugin_key' => $pluginKey,
				'UploadFilesContent.content_id' => $contentId,
				'UploadFile.field_name' => $fieldName
			],
			'order' => ['UploadFile.id' => 'desc']
		];

		$UploadFilesContent = ClassRegistry::init('Files.UploadFilesContent');
		$file = $UploadFilesContent->find('first', $options);
		return $file;
	}

/**
 * ダウンロードカウントアップ
 *
 * @param array $data UploadFileデータ
 * @throws InternalErrorException
 * @return void
 */
	public function countUp($data) {
		$data[$this->alias]['download_count'] = $data[$this->alias]['download_count'] + 1;
		$data[$this->alias]['total_download_count'] = $data[$this->alias]['total_download_count'] + 1;
		// plugin_key, content_key, field_nameが同じだったら
		$this->create();
		$this->begin();
		$result = $this->save($data, ['callbacks' => false, 'validate' => false]);
		if ($result === false) {
			throw new InternalErrorException('Failed UploadFile::countUp()');
		}
		$this->commit();
		return $result;
	}

/**
 * FileインスタンスのファイルをUplodFileに登録する
 *
 * @param File|string $file 登録するファイルのFileインスタンス OR ファイルパス
 * @param string $pluginKey プラグインキー
 * @param string $contentKey コンテンツキー
 * @param string $fieldName フィールド名
 * @param array $data データ登録時に上書きしたいデータを渡す
 * @return array
 * @throws InternalErrorException
 */
	public function registByFile(File $file, $pluginKey, $contentKey, $fieldName, $data = array()) {
		if (is_string($file)) {
			$file = new File($file);
		}

		// データの登録
		$_tmpData = $this->create();
		// $dataにアサイン
		$_tmpData['UploadFile']['plugin_key'] = $pluginKey;
		$_tmpData['UploadFile']['content_key'] = $contentKey;
		$_tmpData['UploadFile']['field_name'] = $fieldName;
		$originalName = property_exists($file, 'originalName') ? $file->originalName : $file->name;
		$_tmpData['UploadFile']['original_name'] = $originalName;
		$_tmpData['UploadFile']['extension'] = pathinfo($file->name, PATHINFO_EXTENSION);
		$_tmpData['UploadFile']['real_file_name'] = [
			'name' => $file->name,
			'type' => $file->mime(),
			'tmp_name' => $file->path,
			'error' => 0,
			'size' => $file->size(),
		];
		$data = Hash::merge($_tmpData, $data);
		$data = $this->save($data);
		if ($data === false) {
			throw new InternalErrorException('Failed UploadFile::registByFile()');
		}

		return $data;
	}

/**
 * 関連テーブルにコンテンツとUploadFileとの関連データを作成
 *
 * @param string $pluginKey プラグインキー
 * @param int $contentId コンテンツID
 * @param int $uploadFileId アップロードファイルID
 * @throws InternalErrorException
 * @return void
 */
	public function makeLink($pluginKey, $contentId, $uploadFileId) {
		$data = [
				'content_id' => $contentId,
				'upload_file_id' => $uploadFileId,
				'plugin_key' => $pluginKey,
		];
		$UploadFilesContent = ClassRegistry::init('Files.UploadFilesContent');
		$data = $UploadFilesContent->create($data);
		if ($UploadFilesContent->save($data) === false) {
			throw new InternalErrorException('Failed UploadFile::makeLink()');
		}
	}

/**
 * 関連テーブルデータの削除
 *
 * @param string $pluginKey プラグインキー
 * @param int $contentId コンテンツID
 * @param string $fieldName フィールド名
 * @return void
 */
	public function deleteLink($pluginKey, $contentId, $fieldName = null) {
		if (! $pluginKey || ! $contentId) {
			return;
		}

		$conditions = [
			'UploadFilesContent.plugin_key' => $pluginKey,
			'UploadFilesContent.content_id' => $contentId,
		];
		if ($fieldName !== null) {
			$conditions['UploadFile.field_name'] = $fieldName;
		}
		$result = $this->UploadFilesContent->find('all', ['conditions' => $conditions]);
		foreach ($result as $link) {
			$this->_deleteNoRelationUploadFile($link);
		}
	}

/**
 * 関連テーブルデータがひとつもないUploadFileレコードを削除する
 *
 * @param array $link UploadFilesContent関連レコードのデータ
 * @throws InternalErrorException
 * @return void
 */
	protected function _deleteNoRelationUploadFile($link) {
		$this->UploadFilesContent->delete($link['UploadFilesContent']['id']);
		// このリンク以外にリンクがなければUploadFileレコードを削除する
		$conditions = [
				'UploadFilesContent.upload_file_id' => $link['UploadFile']['id'],

		];
		$count = $this->UploadFilesContent->find('count', ['conditions' => $conditions]);
		if ($count == 0) {
			if ($this->deleteUploadFile($link['UploadFile']['id']) === false) {
				throw new InternalErrorException('Failed UploadFile::_deleteNoRelationUploadFile');
			};
		}
	}

/**
 * 添付ファイルをDBに登録する
 *
 * @param string $pluginKey 登録するプラグイン名
 * @param string $contentKey コンテンツキー
 * @param int $contentId コンテンツID
 * @param string $fieldName フィールド名
 * @param File $file 登録するファイルのFileインスタンス
 *
 * @return void
 */
	public function attach($pluginKey, $contentKey, $contentId, $fieldName, $file) {
		// UploadFileへ登録
		$uploadFile = $this->registByFile(
				$file,
				$pluginKey,
				$contentKey,
				$fieldName
		);
		// 以前の添付ファイルとの関連を切る。
		$this->deleteLink($pluginKey, $contentId, $fieldName);
		// 関連テーブル登録
		$this->makeLink($pluginKey, $contentId, $uploadFile['UploadFile']['id']);
	}

/**
 * UploadFileに登録済みの実ファイルのパスを返す
 *
 * @param array $uploadFileData UploadFile
 * @return string 実ファイルパス
 */
	public function getRealFilePath($uploadFileData) {
		$filePath = $this->uploadBasePath .
			$uploadFileData['UploadFile']['path'] .
			$uploadFileData['UploadFile']['id'] .
			DS . $uploadFileData['UploadFile']['real_file_name'];
		return $filePath;
	}
}
