<?php if (!defined ('ABSPATH')) die('No direct access allowed');

/**
 * WP BackItUp  - Zip Class
 *
 * @package WP BackItUp
 * @author  Chris Simmons <chris.simmons@wpbackitup.com>
 * @link    http://www.wpbackitup.com
 *
 */

class WPBackItUp_Zip {
	private $log_name;

    private $zip_file_count;
	private $zip_actual_size;
	private $max_zip_size;
	private $zip;
	private $zip_file_path;

	function __construct($log_name,$zip_file_path) {
		global $WPBackitup;

		try {
			$this->log_name = 'debug_zip';//default log name
			if (is_object($log_name)){
				//This is for the old logger
				$this->log_name = $log_name->getLogFileName();
			} else{
				if (is_string($log_name) && isset($log_name)){
					$this->log_name = $log_name;
				}
			}

			$this->zip_file_count=0;
			$this->zip_file_path=$zip_file_path;

			$this->zip_actual_size = 0;
			if ( file_exists($this->zip_file_path) ){
				clearstatcache();
				$this->zip_actual_size = filesize($this->zip_file_path);
			}

			$this->max_zip_size = $WPBackitup->max_zip_size();;

			if (!self::zip_utility_exists()){
				throw new Exception('Zip Archive Class is not available.');
			}

			//Open or create the zip file
			$this->zip = new ZipArchive;
			if($this->zip->open($this->zip_file_path, ZipArchive::CREATE) !== TRUE){
				throw new Exception('Unable to open/create zip file.');
			}else{
				WPBackItUp_Logger::log_info($this->log_name,__METHOD__,'Zip file opened/created:'.$this->zip_file_path);
			}

		} catch(Exception $e) {
			error_log($e);
			WPBackItUp_Logger::log_error($this->log_name,__METHOD__,'Constructor Exception: ' .$e);
			throw $e;
		}
   }


	/**
	 *  Close the zip file
	 *  -  This method will compress the zip file and may be slow if there are a
	 *  large number of files OR the hosts doesnt allocate much memory
	 * @return bool
	 */
	public function close() {
		WPBackItUp_Logger::log_info($this->log_name,__METHOD__,'Close Zip File.');
		try {
			$zip_closed = $this->zip->close();
			if (false===$zip_closed){
				WPBackItUp_Logger::log_info($this->log_name,__METHOD__,'Zip Close failed:' .$this->get_zip_status());
			}

			return $zip_closed;

		}
		catch(Exception $e) {
			//swallow this error
			WPBackItUp_Logger::log_error($this->log_name,__METHOD__,'Exception Occurred:'.$e);
		}
	}

	/**
	 * Check if zip archive util exists
	 * @return bool
	 */
	static public function zip_utility_exists (){
		return class_exists('ZipArchive');
	}

	//this method does NOT work on folders in the root for some reason.
	public function folder_exists($target_root_folder){
		WPBackItUp_Logger::log_info($this->log_name,__METHOD__,'Folder Exists:' . $target_root_folder );
		if ('/' != substr($target_root_folder, -1)){
			$target_root_folder.= '/';
		}

		//Does the folder already exist in the zip folder?
		WPBackItUp_Logger::log_info($this->log_name,__METHOD__,'Does folder exist in zip file:'.$target_root_folder);
		$folder_info = $this->zip->statName($target_root_folder);
		if ($folder_info===false){
			WPBackItUp_Logger::log_info($this->log_name,__METHOD__,'Folder does NOT exist!!' );
			return false;
		} else{
			//Folder exists
			WPBackItUp_Logger::log_info($this->log_name,__METHOD__,'Folder Info ' );
			WPBackItUp_Logger::log($this->log_name,$folder_info);
			return true;
		}
	}

	public function zip_file($source_path,$target_root_folder) {
		WPBackItUp_Logger::log_info($this->log_name,__METHOD__,'Add file to zip:' .$source_path);

		$target_path = $target_root_folder . '/' .basename($source_path);
		WPBackItUp_Logger::log_info($this->log_name,__METHOD__,'Target FILE Path:' .$target_path);

		if (!is_file($source_path)){
			WPBackItUp_Logger::log_error($this->log_name,__METHOD__,'File doesnt exist:' .$source_path);
			return false;
		}

		if (!$this->zip->addFile($source_path,$target_path)){
			WPBackItUp_Logger::log_error($this->log_name,__METHOD__,'Cannot add file to zip' .$source_path);
			return false;
		}

		WPBackItUp_Logger::log_info($this->log_name,__METHOD__,'File added successfully to zip' .$source_path);
		return true;
	}


	public function validate_file($target_file) {
		return $this->zip->statName($target_file);
	}

	//use this to validate each of the root folders
	public function validate_folder($source_path,$target_root_folder) {
		//WPBackItUp_Logger::log_info($this->log_name,__METHOD__,'Validate backup folder.' .$source_path);
		$rtn_value=true;

		//open the current folder
		$iterator = new DirectoryIterator ( $source_path );
		foreach ( $iterator as $file ) {
			if (!$file->isDot ()) {
				//WPBackItUp_Logger::log_info($this->log_name,__METHOD__,'File:' .$file->getFilename());

				if ($file->isDir()) {
					$target_path = $target_root_folder . '/' .$file->getFilename();
					//WPBackItUp_Logger::log_info($this->log_name,__METHOD__,'Target DIR Path:' .$target_path);

					//Validate Folder
					if (false===$this->zip->statName($target_path.'/')){
						WPBackItUp_Logger::log_error($this->log_name,__METHOD__,'DIFF Folder:' .$target_path);
						$rtn_value= false;
					};

					//Validate the files in the folder
					if (!$this->validate_folder($file->getRealPath(),$target_path)){
						//Dont do anything here but dont stop
						$rtn_value= false;
					}
				}
				else {
					$target_path = $target_root_folder . '/' .$file->getFilename();
					//WPBackItUp_Logger::log_info($this->log_name,__METHOD__,'Target FILE Path:' .$target_path);

					if (false===$this->zip->statName($target_path)){
						WPBackItUp_Logger::log_error($this->log_name,__METHOD__,'DIFF File:' .$target_path);
						$rtn_value= false;
					};
				}

			}
		}

		//WPBackItUp_Logger::log_info($this->log_name,__METHOD__,'Backup folder compressed.' .$source_path);
		return $rtn_value;
	}

	public function zip_log_files_in_folder($source_path,$target_path,$filter=['log','info','debug','error']) {
		WPBackItUp_Logger::log_info($this->log_name,__METHOD__,'Zip files in folder:' .$source_path);
		WPBackItUp_Logger::log_info($this->log_name,__METHOD__,'Filter:' . implode(',', $filter));

		//Create the root folder
		if (!$this->zip->addEmptyDir($target_path)){
			WPBackItUp_Logger::log_error($this->log_name,__METHOD__,'Cannot add empty directory' .$target_path);
			return false;
		}

		$dir = $source_path;
		//error_log('PATH:'.$dir);
		$allFiles  = scandir($dir);
		$filteredFiles = array_filter($allFiles, function ($file) use ($filter) {
			return in_array(pathinfo($file, PATHINFO_EXTENSION), $filter);
		});

		foreach ($filteredFiles as $file){
			WPBackItUp_Logger::log_info($this->log_name,__METHOD__,'Add file:' . $file);
			$source_file =  $source_path. DIRECTORY_SEPARATOR . $file;
		//	error_log('Source File'.$source_file);
	//		error_log('Target File'.$target_path);

			if (!$this->zip_file($source_file,$target_path)){
				WPBackItUp_Logger::log_error($this->log_name,__METHOD__,'File NOT added successfully.');
				return false;
			}
		}
		WPBackItUp_Logger::log_info($this->log_name,__METHOD__,'All files in folder added.' .$source_path);
		return true;
	}

	public function add_empty_folder($path){
		WPBackItUp_Logger::log_info($this->log_name,__METHOD__,'Add Empty Folder.' .$path);
		return $this->zip->addEmptyDir($path);
	}

	public function add_file($source_path,$target_path){
		WPBackItUp_Logger::log_info($this->log_name,__METHOD__,'Source:' .$source_path);
		WPBackItUp_Logger::log_info($this->log_name,__METHOD__,'Target:' .$target_path);

		//use isreadable to check file exists AND readable.
		if ( is_readable($source_path) && $this->zip->addFile($source_path,$target_path) ){
			WPBackItUp_Logger::log_info($this->log_name,__METHOD__,'File Size:' . WPBackItUp_FileSystem::format_file_size(filesize ($source_path)));
			$this->zip_file_count++;
			return true;
		}

		return false;
	}


	//Getters

	//dont call this too often and prob not accurate until zip is closed
	//use estimated for most comparisons
	public function get_zip_actual_size(){
		clearstatcache();
		$this->zip_actual_size=filesize($this->zip_file_path);
		return $this->zip_actual_size;
	}

	public function get_max_zip_size($threshold=1){
		return $this->max_zip_size*$threshold;
	}


	// Once issue with missing zips is resolved we can probably merge these two properties
	// to use the PHP archive count.

	/**
	 * Number of files that we have added to the zip
	 * @return int
	 */
	public function get_zip_file_count(){
		return $this->zip_file_count;
	}

	/**
	 * Number of files contained in the zip files.
	 * This property is what is being reported by PHP Archive
	 * @return mixed
	 */
	public function get_files_in_zip(){
		return $this->zip->numFiles;
	}

	public function get_zip_status(){
		return $this->zip->getStatusString();
	}
}