<?php  

class FileModel
{
	private $bID;
	private $nodeId;
	private $fileHelper;
	private $filePath;
	private $validationHelper;
	
	public static $folderRegexp   = '#^[^/?*:;{}<>\\\]+$#';
	public static $filenameRegexp = '#^[^/?*:;{}<>\\\]+$#'; // '#^[^/?*:;{}\\\]+\\.[^/?*:;{}\\\]+$#'
	
	public function __construct($bID, $nodeId, $filePath, FileHelper $fileHelper, ValidationFileHelper $validationHelper)
	{
		if(!is_int($bID) || $bID <= 0)
			throw new Exception('bID must be an integer > 0.');

		if(!is_int($nodeId) || $nodeId <= 0)
			throw new Exception('nodeId must be an integer > 0.');

		$this->bID = $bID;
		$this->nodeId = $nodeId;
		$this->fileHelper = $fileHelper;
		$this->validationHelper = $validationHelper;
		$this->filePath = $filePath;
	}

	public function FileDir()
	{
		return $this->filePath . '/' . $this->bID . '/' . $this->nodeId;
	}
	
	public function FileName($fileName)
	{
		if(!preg_match(self::$filenameRegexp, $fileName))
			throw new Exception(t('Invalid filename.'));
		
		return $this->FileDir() . '/' . $fileName;
	}

	public function DeleteLink($view, $file)
	{
		return $this->actionLink($view, 'delete', $file);
	}
		
	public function DownloadLink($view, $file)
	{
		return $this->actionLink($view, 'download', $file);
	}
	
	public function Delete($file)
	{
		$fullFile = $this->FileName($file);

		if(file_exists($fullFile))
		{
			unlink($fullFile);
		}
		else
		{
			throw new Exception(t('File does not exist.'));
		}
	}

	public function Upload($file)
	{
		$name = $file['name'];
		
		if ($this->validationHelper->extension($name)) 
		{
			$fullFile = $this->FileName($name);

			$this->mkdirRecursive(dirname($fullFile));
			
			if(move_uploaded_file($file['tmp_name'], $fullFile))
			{
				return $this->fileArray($name, time(), $file['size']);
			}			
			
			throw new Exception(t('Failed to save file.'));
		}
		else
		{
			throw new Exception(t('Invalid file extension.'));
		}
	}
	
	public function Download($file)
	{
		$fullFile = $this->FileName($file);

		if(file_exists($fullFile))
		{
			$this->forceDownload($fullFile);
		}
		else
		{
			throw new Exception(t('File does not exist.'));
		}
	}
	
	private function forceDownload($file)
	{
		session_write_close();
		ob_clean();
		$filename = basename($file);
		header("Content-Disposition: attachment; filename=\"$filename\"");
		header('Content-Length: ' . filesize($file));
		header("Pragma: public");
		header("Expires: 0");
		header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
		header("Cache-Control: private",false);
		header("Content-Transfer-Encoding: binary");
		header("Content-Encoding: plainbinary");  

		$fh = Loader::helper('file');
		$h = Loader::helper('mime');
		$mimeType = $h->mimeFromExtension($fh->getExtension($filename));
		
		header('Content-type: ' . ($mimeType ? $mimeType : 'application/octet-stream'));
	
		$buffer = '';
		$chunk = 1024*1024;
		$handle = fopen($file, 'rb');
		if ($handle === false) {
			return false;
		}
		while (!feof($handle)) {
			$buffer = fread($handle, $chunk);
			print $buffer;
		}
		
		fclose($handle);
		exit;		
	}
	
	public function Files()
	{
		$dir = $this->FileDir();
		$output = array();
		
		if ($handle = @opendir($dir))
		{
			while (false !== ($file = readdir($handle)))
			{
				$fullName = $dir . '/' . $file;
				
				if (is_file($fullName))
				{
					$output[] = $this->fileArray($file, filemtime($fullName), filesize($fullName));
				}
			}
			closedir($handle);
		}
		
		sort($output);
		return $output;
	}
	
	public function DeleteFolder()
	{
		$dir = $this->FileDir();
		
		// There is a chance this dir doesn't exist, so test for that before
		// throwing exception.
		if(is_dir($dir) && !@rmdir($dir))
			throw new Exception(t('Folder is not empty, cannot be deleted.'));
	}

	public function FileSize($filesize)
	{
		if($filesize < 1048576)
			return round($filesize / 1024) . ' Kb';
		else
			return round($filesize / 1048576, 1) . ' Mb';
	}
		
	///// Private methods ///////////////////////////////////////////
	
	private function fileArray($name, $modified, $bytes)
	{
		return array('name' => $name, 'modified' => $modified, 'nodeId' => $this->nodeId, 'size' => $bytes);
	}
	
	private function mkdirRecursive($path)
	{
		if(@mkdir($path) || file_exists($path)) return true;
		return ($this->mkdirRecursive(dirname($path)) && mkdir($path));
	}
	
	private function actionLink($view, $action, $file)
	{
		return $view->action($action) . '&nodeId=' . $file['nodeId'] . '&file=' . urlencode($file['name']);
	}
}