<?php
/**
 * CONTENT MANIPULATION CLASS
 * 
 * @package 
 * @author Rihards Silins
 * @copyright MTC Media 2015
 * @version 2.0
 * @access public
 * 
 * MTC Media's Full Complete CMS Content Manipulation class.
 * 
 */
Class CmsPatching
{
	
	public $info_path = "";
	public $config_path = "";
	public $backups_path = "";
	public $info = array(
		'version_number' 				=> null, 
		'important_files' 				=> array(), 
		'notes' 						=> null, 
		'original_version' 				=> null, 
		'system_noted_modified_files' 	=> array(),
		'update_log' 					=> array(), 
		'updateable' 					=> null, 
		'user_noted_modified_files' 	=> array(), 
	);
	public $config = null;
	public $backups = array();

	function __construct() {}

	static function newInstance() {
		return new self();
	}	

	function loadBackups($backups_path) {
		$this->backups_path = $backups_path;
		$this->backups = array();

		foreach (new DirectoryIterator($this->backups_path) as $backup_dir) {
		    if($backup_dir->isDot()) continue;
		    if($backup_dir->isDir()) {
		    	$this->backups[$backup_dir->getFilename()] = array(
		    		'folder_name' 	=> $backup_dir->getFilename(),
		    		'datetime' 		=> date('H:m d/m/Y', $backup_dir->getFilename()),
		    	);
		    }
		}

		ksort($this->backups);
		$this->backups = array_reverse($this->backups);

		return $this;
	}

	function loadConfig($config_path) {
		$this->config_path = $config_path;

		$handle = @fopen($this->config_path, "r");
		if ( $handle !== false ) {
			$this->config = json_decode(fread($handle, 1024),true);
		}

		return $this;
	}

	/**
	 * Updates the config.json file.
	 * @return bool success
	 */
	function saveConfig() {
		if ( empty($this->config) || empty($this->config_path) ) {
			return false;
		}
		$json = json_encode($this->config);
		if ( $json === false ) {
			return false;
		}
		$handle = @fopen($this->config_path, "w");
		if ( $handle === false ) {
			return false;
		}
		fwrite($handle, $json);
		fclose($handle);
		return true;
	}

	static function parsePatchingInstructionJson($instructions_json, $downloaded_repo_location, $site_path) {

		$instruction = array();	
		$raw_list = array();
		$ignore_list = array();
		$strict_ignore_list = array();
		$result = array();

		$instructions = json_decode($instructions_json, true);
		if ( empty($instructions) ) {
			return $result;
		}
		$list = $instructions['files'];

		if ( !empty($instructions['name']) ) {
			$instruction['name'] = $instructions['name'];
		}	

		if ( !empty($instructions['description']) ) {
			$instruction['description'] = $instructions['description'];
		}	

		for ($i=0; $i < count($list); $i++) { 	

			if ( substr($list[$i]['source'], -1) === "*" && substr($list[$i]['source'], 0, 1) === "!" ) {

				$ignore_list[] = substr($list[$i]['source'], 2, -1);

				continue;

			} else if ( substr($list[$i]['source'], 0, 1) === "!" ) {

				$strict_ignore_list[] = substr($list[$i]['source'], 2);

				continue;

			}


			// removing the front slash
			$list[$i]['source'] = substr($list[$i]['source'], 1); 

			// generate destination if such is not set
			if ( empty($list[$i]['destination']) ) {
				$list[$i]['destination'] = $site_path.$list[$i]['source'];
			} else {
				$list[$i]['destination'] = substr($list[$i]['destination'], 1); // removing the front slash
				$list[$i]['destination'] = $site_path.$list[$i]['destination'];
			}

			$list[$i]['source'] = $downloaded_repo_location.$list[$i]['source'];


			// set mode if such is not set
			if ( empty($list[$i]['mode']) ) {
				$list[$i]['mode'] = "create";
			}

			if ( substr($list[$i]['source'], -1) === "*" ) {

				$rglob_file_list = self::rglob(basename($list[$i]['source']), 0, dirname($list[$i]['source']));

			} else {

				$rglob_file_list  = glob($list[$i]['source']);
			}

			$base_path_source = dirname($list[$i]['source']);
			$base_path_destination = dirname($list[$i]['destination']);
			
			for ($j=0; $j < count($rglob_file_list); $j++) { 
				
				$append = substr($rglob_file_list[$j], strlen($base_path_source));

				$source = $base_path_source.$append;
				$destination = $base_path_destination.$append;

				$raw_list[] = array(
					'source'		=> $source,
					'destination'	=> $destination,
					'mode'			=> $list[$i]['mode']
				);

			}

		}

		// remove the to be ignored files and check for duplicates
		for ($i=0; $i < count($raw_list); $i++) { 
			$found = false;
			// remove the to be ignored file
			for ($j=0; $j < count($ignore_list); $j++) {
				$check = strpos($raw_list[$i]['source'], $ignore_list[$j]);
				if ( $check !== false && $check === 0 ) {
					$found = true;
				}
			}
			// remove the to be strict ignore file
			for ($j=0; $j < count($strict_ignore_list); $j++) {
				if ( $strict_ignore_list[$j] === $raw_list[$i]['source'] ) {
					$found = true;
				}
			}
			// check for duplicates
			for ($j=0; $j < count($result); $j++) {
				if ( $result[$j]['source'] === $raw_list[$i]['source'] ) {
					$found = true;
				}
			}

			if ( $found === false ) {
				$result[] = $raw_list[$i];
			}
		}

		$instruction['file_instructions'] = $result;

		return $instruction;
	}

	/**
	 * Parse important file list
	 * @param stirng[] $list 
	 * @return stirng[] $list 
	 */
	static function parseImportantFileList($list) {
		$raw_list = array();
		$ignore_list = array();
		$strict_ignore_list = array();
		for ($i=0; $i < count($list); $i++) { 			
			if ( substr($list[$i], -1) === "*" && substr($list[$i], 0, 1) === "!" ) {
				$ignore_list[] = substr($list[$i], 1, -1);
			} else if ( substr($list[$i], 0, 1) === "!" ) {
				$strict_ignore_list[] = substr($list[$i], 1);
			} else if ( substr($list[$i], -1) === "*" ) {
				$raw_list  = array_merge($raw_list, self::rglob(basename($list[$i]), 0, dirname($list[$i])));
			} else {
				$raw_list  = array_merge($raw_list, glob($list[$i]));
			}

		}

		// remove the to be ignored files and check for duplicates
		$result = array();
		for ($i=0; $i < count($raw_list); $i++) { 
			$found = false;
			// remove the to be ignored file
			for ($j=0; $j < count($ignore_list); $j++) {
				$check = strpos($raw_list[$i], $ignore_list[$j]);
				if ( $check !== false && $check === 0 ) {
					$found = true;
				}
			}
			// remove the to be strict ignore file
			for ($j=0; $j < count($strict_ignore_list); $j++) {
				if ( $strict_ignore_list[$j] === $raw_list[$i] ) {
					$found = true;
				}
			}
			// check for duplicates
			for ($j=0; $j < count($result); $j++) {
				if ( $result[$j] === $raw_list[$i] ) {
					$found = true;
				}
			}

			if ( $found === false ) {
				$result[] = $raw_list[$i];
			}
		}

		return $result;
	}

	/**
	 * recursive glob()
	 * @param string $pattern 
	 * @param mixed $flags 
	 * @param string $path 
	 * @return stirng[] $files
	 */
	static function rglob($pattern='*', $flags = 0, $path='') {
	    $paths=glob($path.'*', GLOB_MARK|GLOB_ONLYDIR|GLOB_MARK);
	    $files=glob($path.$pattern, $flags);
	    foreach ($paths as $path) { $files=array_merge($files,self::rglob($pattern, $flags, $path)); }
	    return $files;
	}

	/**
	 * Updates the txt file with new info
	 * @param stirng $filename_of_the_info_file_that_needs_to_be_updated
	 * @return bool success
	 */
	function saveInfoTxt($filename) {
		if ( empty($filename) || empty($this->info_path)) {
			return false;
		}

		$handle = @fopen($this->info_path.$filename.".txt", "w");
		if ( $handle === false ) {
			return false;
		}
		if ( empty($this->info[$filename]) ) {
			$this->info[$filename] = "";
		}
		fwrite($handle, $this->info[$filename]);
		fclose($handle);
		return true;
	}

	/**
	 * Read the important file list (important_files.txt) and return it as a list
	 * @param string $important_file_list_path 
	 * @return string[] list
	 */
	function readImportantFileList($important_file_list_path) {
		$handle = @fopen($important_file_list_path, "rb");
	    if ( $handle === false ) {
	    	return array();
	    }
	    $list = array();
        while (($line = fgets($handle)) !== false) {
            $list[] = trim($line);
        }
	    return $list;
	}

	function loadInfo($info_path) {
		$this->info_path = $info_path;

		$handle = @fopen($this->info_path."important_files.txt", "rb");
		if ( $handle !== false ) {
			$this->info['important_files'] = "";
			while (!feof($handle)) {
				$line = fread($handle, 1024);
				$this->info['important_files'][] = $line;
			}
		}

		$handle = @fopen($this->info_path."version_number.txt", "r");
		if ( $handle !== false ) {
			$this->info['version_number'] = fread($handle, 1024);
		}

		$handle = @fopen($this->info_path."notes.txt", "r");
		if ( $handle !== false ) {
			$this->info['notes'] = fread($handle, 1024);
		}

		$handle = @fopen($this->info_path."original_version.txt", "r");
		if ( $handle !== false ) {
			$this->info['original_version'] = fread($handle, 1024);
		}

		$handle = @fopen($this->info_path."system_noted_modified_files.txt", "r");
		if ( $handle !== false ) {
			while (!feof($handle)) {
				$line = fread($handle, 1024);
				$this->info['system_noted_modified_files'][] = $line;
			}
		}

		$handle = @fopen($this->info_path."update_log.txt", "r");
		if ( $handle !== false ) {
			while (!feof($handle)) {
				$line = fread($handle, 1024);
				$this->info['update_log'][] = $line;
			}
		}

		$handle = @fopen($this->info_path."updateable.txt", "r");
		if ( $handle !== false ) {
			$this->info['updateable'] = fread($handle, 1024);
		}

		$handle = @fopen($this->info_path."user_noted_modified_files.txt", "r");
		if ( $handle !== false ) {
			$this->info['user_noted_modified_files'] = "";
			while (!feof($handle)) {
				$line = fread($handle, 1024);
				$this->info['user_noted_modified_files'][] = $line;
			}
		}

		return $this;
	}
    
    /**
     * cmspatching::fileContentAsString()
     * Returns the file content as a string 
     *  
     * @param string $file_path 
     * @return string $file_as_string 
     */
    
    public function fileContentAsString($file_path) {
        $file_path = trim($file_path);
        $file_as_string = file_get_contents($file_path);
        return $file_as_string;
    }
    
}