<?php
/**
* Web Application Component Toolkit
*
* @link http://www.phpwact.org/
*
* @author Wact Development Team
* @link http://www.phpwact.org/team
*
* @copyright Copyright 2006, Jeff Moore
* @license http://opensource.org/licenses/mit-license.php MIT
*/

/**
 * Base class for coverage recording.
 */
abstract class Wact_Test_Coverage_Recorder {

    /*
     */
    const EXECUTABLE_LINES = 1;

    /*
     */
    const COVERED_LINES = 2;
    
    /*
     */
    const UNCOVERED_LINES = 3;
    
    /*
     */
    const DEAD_LINES = 4;

    /**
     * @var array paths to record
     */
    protected $includePaths;
    
    /**
     * @var array
     */
    //protected $excludePaths;
    
    /**
     * @var array extensions to exclude from the recording
     */
    protected $excludeExtensions;
    
    /**
     * @var Wact_Test_Coverage_Reporter
     */
    protected $coverageReporter;
    
    /**
     * Class constructor.
     * 
     * @param $includePaths array paths to record
     * @param $excludePaths array
     * @param $coverageReporter Wact_Test_Coverage_Reporter
     */
    public function __construct($includePaths, $excludePaths, $coverageReporter) {
        $this->checkRequirements();
        
        $this->includePaths = $includePaths;
        //$this->excludePaths = $excludePaths;
        $this->coverageReporter = $coverageReporter;
    }
    
    /**
     * Ensures that the requirements for this coverage recorder are currently available.
     */
    abstract protected function checkRequirements();
    
    /**
     * Starts instrumentation.
     */
    abstract public function startInstrumentation();

    /**
     * Stops instrumentation and stores collected information.
     */
    abstract public function stopInstrumentation();
    
    /**
     * Processes collected coverage data and returns generated report information.
     */
    abstract public function processCoveredFiles();
    
    /**
     * Stores the list of extensions to exclude from the recording
     * 
     * @param $excludeExtensions array extensions to exclude
     */
    public function setExcludeExtensions($excludeExtensions) {
        $this->excludeExtensions = $excludeExtensions;
    }
    
    /**
     * Scans for any unrecorded files in the include paths
     * and adds them to the statistics so that they show up
     * as uncovered in the report.
     * 
     * @param $report array covered report data
     * @return array covered + uncovered report data
     */
    protected function mergeUncoveredFiles($report) {
        foreach ($this->includePaths as $includePath) {
            $items = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($includePath));
            foreach ($items as $item) {
                $filename = $item->getPathName();
                
                // skip dot, svn, and not .php files
                if ( ($filename[0] == '.') || (strpos($filename, '.svn') !== FALSE) || (substr($filename, -4, 4) != '.php') ) {
                    continue;
                }
                
                if ($this->isExcludedExtension($filename)) {
                    continue;
                }
                
                if (!isset($report[$filename])) {
                    $report[$filename] = array(
                        self::EXECUTABLE_LINES => 0,
                        self::COVERED_LINES => 0,
                        self::UNCOVERED_LINES => 0,
                        self::DEAD_LINES => 0
                    );
                }
            }
        }
        
        return $report;
    }
    
    /**
     * Test if a given path is to be recorded.
     *
     * @param string $target the path to test
     */
    public function isIncluded($path) {
        foreach ($this->includePaths as $includePath) {
            if (strpos($path, $includePath) === 0) {
                return TRUE;
            }
        }
        
        return FALSE;
    }
    
    /**
     * Test if a given extension is to be excluded.
     *
     * @param string $extension the extension to test
     */
    public function isExcludedExtension($filename) {
        foreach ($this->excludeExtensions as $extension) {
            if (substr($filename, -strlen($extension), strlen($extension)) == $extension) {
                return TRUE;
            }
        }
        
        return FALSE;
    }
    
    /**
     * Generates report data and renders report
     * through coverage reporter.
     */
    public function generateReport() {
        $report = $this->processCoveredFiles();
        
        $report = $this->mergeUncoveredFiles($report);
        
        ksort($report);
        
        $this->coverageReporter->render($report);
    }
    
}

?>