<?php

/**
 * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * @package fusionLib
 * @copyright Copyright (c) 2011 - 2013 fusionLib. All rights reserved.
 * @link http://fusionlib.com
 */

/**
 * flIntegrity Class
 *
 * Traverse the file tree from FLROOTPATH building a list of files
 * and their md5 hash.
 *
 * @package fusionLib
 */
class flIntegrity {

	/**#@+
	 * The changed type.
	 */
	const CHANGED = 1;
	const MISSING = 2;
	const ADDED = 3;
	/**#@-*/

	/**
	 * Array of files and hashes.
	 *
	 * @var array
	 */
	protected $files = array();

	/**
	 * The length of the base part of the scan.
	 *
	 * @var int
	 */
	private $l;

	/**
	 * The list of files to ignore.
	 *
	 * @array
	 */
	protected $ignore;

	/**
	 * Build the list of files and hashes.
	 *
	 * @param string $path The path to start the scan at.
	 * @param array $ignore Paths starting with any of these are ignored.
	 */
	function build($path, $ignore = array()) {
		$this->l = strlen($path);
		$this->ignore = $ignore;
		$this->_build($path);
	}

	/**
	 * Recursive function to find and hash all files.
	 *
	 * @param string $dir The folder to scan.
	 */
	private function _build($dir) {
		$l = strlen(FLROOTPATH);
		foreach(glob(rtrim($dir, '/\\') . '/*', GLOB_NOSORT) as $f) {
			if(is_dir($f))
				$this->_build($f);
			else {
				$fk = substr($f, $l);
				$m = false;
				foreach($this->ignore as $i) {
					if(strpos($fk, $i) === 0) {
						$m = true;
						break;
					}
				}
				if(!$m)
					$this->files[$fk] = md5_file($f);
			}
		}
	}

	/**
	 * Find the file changes and return them.
	 *
	 * @param array $baseSet Is an array of the expected files and their hashes.
	 * @return array An array of changed files and reason flIntegrity::CHANGED, flIntegrity::MISSING, flIntegrity::ADDED
	 */
	function getChanges($baseSet) {
		$changes = array();
		$serverFiles = $this->files;

		// Added files
		$diff = array_diff_key($serverFiles, $baseSet);
		foreach($diff as $k => $hash) {
			$changes[$k] = flIntegrity::ADDED;
			unset($serverFiles[$k]);
		}

		// Check for changes to files
		$diff = array_diff($serverFiles, $baseSet);
		foreach($diff as $k => $hash)
			$changes[$k] = flIntegrity::CHANGED;

		// Check for missing files
		$diff = array_diff_key($baseSet, $serverFiles);
		foreach($diff as $k => $hash)
			$changes[$k] = flIntegrity::MISSING;

		return $changes;
	}

	/**
	 * Get the array of file names and their hashes.
	 *
	 * @return array The files and their hashes.
	 */
	function getFileHash() {
		return $this->files;
	}

	/**
	 * Print the change list to the browser.
	 *
	 * @param array $changes The list of changes and reasons.
	 * @return string The list of changes.
	 */
	function printChanges($changes) {
		ob_start();
		echo "<ul>\n";
		if(count($changes)) {
			foreach($changes as $f => $r) {
				echo "<li>$f ... ";
				switch($r) {
					case flIntegrity::CHANGED: echo '<span class="changed">' . __('Changed', 'fusionLib') . '</span>'; break;
					case flIntegrity::MISSING: echo '<span class="missing">' . __('Missing', 'fusionLib') . '</span>'; break;
					case flIntegrity::ADDED: echo '<span class="added">' . __('Added', 'fusionLib') . '</span>'; break;
				}
				echo "</li>\n";
			}
		}
		else
			echo '<li>' . __('No changes detected.', 'fusionLib') . '</li>';
		echo "</ul>\n";
		return ob_get_clean();
	}
}
