<?php
/**
 * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
 * This file is licensed under the Affero General Public License version 3 or
 * later.
 * See the COPYING-README file.
 */


/**
 * handles updating the filecache according to outside changes
 */
class OC_FileCache_Update{
	/**
	 * check if a file or folder is updated outside owncloud
	 * @param string path
	 * @param string root (optional)
	 * @param boolean folder
	 * @return bool
	 */
	public static function hasUpdated($path,$root=false,$folder=false) {
		if($root===false) {
			$view=OC_Filesystem::getView();
		}else{
			$view=new OC_FilesystemView($root);
		}
		if(!$view->file_exists($path)) {
			return false;
		}
		$cachedData=OC_FileCache_Cached::get($path, $root);
		if(isset($cachedData['mtime'])) {
			$cachedMTime=$cachedData['mtime'];
			if($folder) {
				return $view->hasUpdated($path.'/', $cachedMTime);
			}else{
				return $view->hasUpdated($path, $cachedMTime);
			}
		}else{//file not in cache, so it has to be updated
			if(($path=='/' or $path=='') and $root===false) {//dont auto update the home folder, it will be scanned
				return false;
			}
			return true;
		}
	}

	/**
	 * delete non existing files from the cache
	 */
	public static function cleanFolder($path,$root=false) {
		if($root===false) {
			$view=OC_Filesystem::getView();
		}else{
			$view=new OC_FilesystemView($root);
		}

		$cachedContent=OC_FileCache_Cached::getFolderContent($path,$root);
		foreach($cachedContent as $fileData) {
			$path=$fileData['path'];
			$file=$view->getRelativePath($path);
			if(!$view->file_exists($file)) {
				if($root===false) {//filesystem hooks are only valid for the default root
					OC_Hook::emit('OC_Filesystem', 'post_delete', array('path'=>$file));
				}else{
					self::delete($file, $root);
				}
			}
		}
	}

	/**
	 * update the cache according to changes in the folder
	 * @param string path
	 * @param string root (optional)
	 */
	public static function updateFolder($path,$root=false) {
		if($root===false) {
			$view=OC_Filesystem::getView();
		}else{
			$view=new OC_FilesystemView($root);
		}
		$dh=$view->opendir($path.'/');
		if($dh) {//check for changed/new files
			while (($filename = readdir($dh)) !== false) {
				if($filename != '.' and $filename != '..' and $filename != '') {
					$file=$path.'/'.$filename;
					$isDir=$view->is_dir($file);
					if(self::hasUpdated($file, $root, $isDir)) {
						if($isDir){
							self::updateFolder($file, $root);
						}elseif($root===false) {//filesystem hooks are only valid for the default root
							OC_Hook::emit('OC_Filesystem', 'post_write', array('path'=>$file));
						}else{
							self::update($file, $root);
						}
					}
				}
			}
		}

		self::cleanFolder($path, $root);

		//update the folder last, so we can calculate the size correctly
		if($root===false) {//filesystem hooks are only valid for the default root
			OC_Hook::emit('OC_Filesystem', 'post_write', array('path'=>$path));
		}else{
			self::update($path, $root);
		}
	}

	/**
	 * called when changes are made to files
	 * @param array $params
	 * @param string root (optional)
	 */
	public static function fileSystemWatcherWrite($params) {
		$path=$params['path'];
		self::update($path);
	}

	/**
	 * called when files are deleted
	 * @param array $params
	 * @param string root (optional)
	 */
	public static function fileSystemWatcherDelete($params) {
		$path=$params['path'];
		self::delete($path);
	}

	/**
	 * called when files are deleted
	 * @param array $params
	 * @param string root (optional)
	 */
	public static function fileSystemWatcherRename($params) {
		$oldPath=$params['oldpath'];
		$newPath=$params['newpath'];
		self::rename($oldPath, $newPath);
	}

	/**
	 * update the filecache according to changes to the filesystem
	 * @param string path
	 * @param string root (optional)
	 */
	public static function update($path,$root=false) {
		if($root===false) {
			$view=OC_Filesystem::getView();
		}else{
			$view=new OC_FilesystemView($root);
		}

		$mimetype=$view->getMimeType($path);

		$size=0;
		$cached=OC_FileCache_Cached::get($path,$root);
		$cachedSize=isset($cached['size'])?$cached['size']:0;

		if($view->is_dir($path.'/')) {
			if(OC_FileCache::inCache($path, $root)) {
				$cachedContent=OC_FileCache_Cached::getFolderContent($path, $root);
				foreach($cachedContent as $file) {
					$size+=$file['size'];
				}
				$mtime=$view->filemtime($path.'/');
				$ctime=$view->filectime($path.'/');
				$writable=$view->is_writable($path.'/');
				OC_FileCache::put($path, array('size'=>$size,'mtime'=>$mtime,'ctime'=>$ctime,'mimetype'=>$mimetype,'writable'=>$writable));
			}else{
				$count=0;
				OC_FileCache::scan($path, null, $count, $root);
				return; //increaseSize is already called inside scan
			}
		}else{
			$size=OC_FileCache::scanFile($path, $root);
		}
		OC_FileCache::increaseSize(dirname($path), $size-$cachedSize, $root);
	}

	/**
	 * update the filesystem after a delete has been detected
	 * @param string path
	 * @param string root (optional)
	 */
	public static function delete($path,$root=false) {
		$cached=OC_FileCache_Cached::get($path, $root);
		if(!isset($cached['size'])) {
			return;
		}
		$size=$cached['size'];
		OC_FileCache::increaseSize(dirname($path), -$size, $root);
		OC_FileCache::delete($path, $root);
	}

	/**
	 * update the filesystem after a rename has been detected
	 * @param string oldPath
	 * @param string newPath
	 * @param string root (optional)
	 */
	public static function rename($oldPath,$newPath,$root=false) {
		if(!OC_FileCache::inCache($oldPath, $root)) {
			return;
		}
		if($root===false) {
			$view=OC_Filesystem::getView();
		}else{
			$view=new OC_FilesystemView($root);
		}

		$cached=OC_FileCache_Cached::get($oldPath, $root);
		$oldSize=$cached['size'];
		OC_FileCache::increaseSize(dirname($oldPath), -$oldSize, $root);
		OC_FileCache::increaseSize(dirname($newPath), $oldSize, $root);
		OC_FileCache::move($oldPath, $newPath);
	}
}
