<?php

require_once $config->get('app.root').'alpha/util/AlphaErrorHandlers.inc';

/**
 *
 * A class for auto-loading classes in the Alpha Framework, as well as
 * custom classses and files in 3rd party libraries.
 *
 * @package alpha::util
 * @since 1.2
 * @author John Collins <dev@alphaframework.org>
 * @version $Id: AlphaAutoLoader.inc 1548 2012-07-29 17:07:07Z alphadevx $
 * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
 * @copyright Copyright (c) 2012, John Collins (founder of Alpha Framework).  
 * All rights reserved.
 * 
 * <pre>
 * Redistribution and use in source and binary forms, with or 
 * without modification, are permitted provided that the 
 * following conditions are met:
 * 
 * * Redistributions of source code must retain the above 
 *   copyright notice, this list of conditions and the 
 *   following disclaimer.
 * * Redistributions in binary form must reproduce the above 
 *   copyright notice, this list of conditions and the 
 *   following disclaimer in the documentation and/or other 
 *   materials provided with the distribution.
 * * Neither the name of the Alpha Framework nor the names 
 *   of its contributors may be used to endorse or promote 
 *   products derived from this software without specific 
 *   prior written permission.
 *   
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * </pre>
 *  
 */
class AlphaAutoLoader {
	/**
	 * Hash array of the directories to scan
	 * 
	 * @var array
	 */
	private static $scannedDirs;
	
	/**
	 * Logger
	 * 
	 * @var Logger
	 */
	private static $logger;
	
	/**
	 * Sets up the list of directories to scan.
	 * 
	 * @since 1.2
	 */
	private static function initFolderList() {
		global $config;
		
		if(!isset(self::$logger) && class_exists('Logger'))
			self::$logger = new Logger('AlphaAutoLoader');
		
		self::$scannedDirs = array(
					$config->get('app.root').'alpha/controller/',
					$config->get('app.root').'alpha/controller/front/',
					$config->get('app.root').'alpha/exceptions/',
					$config->get('app.root').'alpha/model/',
					$config->get('app.root').'alpha/model/types/',
					$config->get('app.root').'alpha/view/',
					$config->get('app.root').'alpha/view/widgets/',
					$config->get('app.root').'alpha/view/renderers/',
					$config->get('app.root').'alpha/util/',
					$config->get('app.root').'alpha/util/cache/',
					$config->get('app.root').'alpha/util/codehighlight/',
					$config->get('app.root').'alpha/util/convertors/',
					$config->get('app.root').'alpha/util/feeds/',
					$config->get('app.root').'alpha/util/graphs/',
					$config->get('app.root').'alpha/util/helpers/',
					$config->get('app.root').'alpha/util/metrics/',
					$config->get('app.root').'alpha/util/filters/',
					$config->get('app.root').'alpha/tests/',
					$config->get('app.root').'controller/',
					$config->get('app.root').'model/',
					$config->get('app.root').'view/'
				);
	}
	
	/**
	 * Load the file based on the class name provided.  The file is assumed to be called "className.[inc|php]".
	 * 
	 * @param string $className
	 * @since 1.2
	 */
	public static function loadClass($className) {
		global $config;
		
		if(!isset(self::$scannedDirs))
			self::initFolderList();
		
		// lets try to guess the correct dir to save time, based on the name of the class
		$skipDirs = array();

		if(strpos($className, 'Controller') !== false) {
			if(self::loadFile($config->get('app.root').'alpha/controller/', $className)) {
				// it was found and inclued by loadFile() so we can return
				return;
			}else{
				// it was NOT found by loadFile() in this dir, so we can skip a scan of this dir on the full scan
				array_push($skipDirs, $config->get('app.root').'alpha/controller/');
			}

			if(self::loadFile($config->get('app.root').'alpha/controller/front/', $className)) {
				return;
			}else{
				array_push($skipDirs, $config->get('app.root').'alpha/controller/front/');
			}
		}

		if(strpos($className, 'Exception') !== false) {
			if(self::loadFile($config->get('app.root').'alpha/exceptions/', $className)) {
				return;
			}else{
				array_push($skipDirs, $config->get('app.root').'alpha/exceptions/');
			}
		}

		if(strpos($className, 'DAO') !== false) {
			if(self::loadFile($config->get('app.root').'alpha/model/', $className)) {
				return;
			}else{
				array_push($skipDirs, $config->get('app.root').'alpha/model/');
			}
		}

		if(strpos($className, 'Object') !== false) {
			if(self::loadFile($config->get('app.root').'alpha/model/', $className)) {
				return;
			}else{
				array_push($skipDirs, $config->get('app.root').'alpha/model/');
			}
		}

		if(strpos($className, 'View') !== false) {
			if(self::loadFile($config->get('app.root').'alpha/view/', $className)) {
				return;
			}else{
				array_push($skipDirs, $config->get('app.root').'alpha/view/');
			}
		}
		
		if(strpos($className, 'Utils') !== false) {
			if(self::loadFile($config->get('app.root').'alpha/util/', $className)) {
				return;
			}else{
				array_push($skipDirs, $config->get('app.root').'alpha/util/');
			}
		}
		
		// do a full folder scan
		foreach (self::$scannedDirs as $dir) {
			if(!in_array($dir, $skipDirs)) {
				$loaded = self::loadFile($dir, $className);

				if($loaded) {
					if(isset(self::$logger))
						self::$logger->debug('Had to do a full scan to find the class ['.$className.'] in the dir ['.$dir.']');

					return;
				}
			}
		}
		
		if(isset(self::$logger))
			self::$logger->fatal('Unable to load the required class ['.$className.']');
	}
	
	/**
	 * Loads the lib file from the lib dir based on the filename provided.
	 * 
	 * @param string $fileName
	 * @throws LibraryNotInstalledException
	 * @since 1.2
	 */
	public static function loadLib($fileName) {
		global $config;
		
		// Libraries shipped with Alpha
		$iterator = new RecursiveDirectoryIterator($config->get('app.root').'alpha/lib/');
		
		foreach (new RecursiveIteratorIterator($iterator) as $name => $file) {
			if($file->getBaseName() == $fileName) {
				if(isset(self::$logger))
					self::$logger->debug('Found the library file ['.$fileName.'] in the location ['.$name.']');
				
				require_once $name;
				return;
			}
		}
		
		// custom libs
		$iterator = new RecursiveDirectoryIterator($config->get('app.root').'lib/');
		
		foreach (new RecursiveIteratorIterator($iterator) as $name => $file) {
			if($file->getBaseName() == $fileName) {
				if(isset(self::$logger))
					self::$logger->debug('Found the library file ['.$fileName.'] in the location ['.$name.']');
				
				require_once $name;
				return;
			}
		}
		
		throw new LibraryNotInstalledException('Cannot find the library file ['.$fileName.'] in the lib dir!');
	}

	/**
	 * Attempts to load the file for the class in the directory indicated.
	 * 
	 * @param string $dir
	 * @param string $className
	 * @return bool
	 * @since 1.2
	 */
	private static function loadFile($dir, $className) {
		global $config;
		
		// we're doing .inc first, as most classes in Alpha use this extension
		$path = $dir.$className.'.inc';
		if (file_exists($path) && is_readable($path)) {
			if(isset(self::$logger))
				self::$logger->debug('Loaded the class ['.$className.'] in the location ['.$path.']');
			include_once $path;
			return true;
		}

		$path = $dir.$className.'.php';
		if (file_exists($path) && is_readable($path)) {
			if(isset(self::$logger))
				self::$logger->debug('Loaded the class ['.$className.'] in the location ['.$path.']');
			include_once $path;
			return true;
		}

		return false;
	}
}

spl_autoload_register('AlphaAutoLoader::loadClass');

?>
