<?php
/**
 * A class for logging information.
 *
 * This class provides a way to log messages by level of priority. It also
 * features a filtering mechanism and is adaptable to multiple storage handlers
 * (file, database, email, etc.).
 */
class log extends log_filter
{
    /**
     * @var integer The priority options.
     */
    const ALL = 0;
    const EMERGENCY = 1;
    const ALERT = 2;
    const CRITICAL = 3;
    const ERROR = 4;
    const WARNING = 5;
    const NOTICE = 6;
    const INFO = 7;
    const DEBUG = 8;

    /**
     * @var array The writers.
     */
    protected $writer;

    ////////////////////////////////////////////////////////////////////////////
    // Construct and destruct functions.

    /**
     * @param integer (optional) $filter The priority filter.
     * @param array/log_writer (optional) $writer The writers.
     */
    public function __construct($filter = log::DEBUG, $writer = null)
    {
        parent::__construct($filter);
        $this->clear();
        
        if ($writer !== null)
        {
            if (!is_array($writer))
            {
                $writer = array($writer);
            }
            
            foreach ($writer as $k => $v)
            {
                $this->set($v, $k);
            }
        }
    }
    
    ////////////////////////////////////////////////////////////////////////////
    // Overload functions.

    /**
     * A shortcut for write().
     * 
     * @param string $function The priority.
     * @param array $parameter The parameters to provide.
     * @throws badmethodcallexception If the log magic method does not exist.
     * @return log A fluent interface.
     */
    public function __call($function, array $parameter)
    {
        $priority = null;
        
        switch ($function)
        {
            case 'emergency' : $priority = log::EMERGENCY; break;
            case 'alert'     : $priority = log::ALERT;     break;
            case 'critical'  : $priority = log::CRITICAL;  break;
            case 'error'     : $priority = log::ERROR;     break;
            case 'warning'   : $priority = log::WARNING;   break;
            case 'notice'    : $priority = log::NOTICE;    break;
            case 'info'      : $priority = log::INFO;      break;
            case 'debug'     : $priority = log::DEBUG;     break;
            default          : throw new badmethodcallexception('Magic method ' . __CLASS__ . '::' . $function . '() is not available.');
        }
        
        return $this->write(array_shift($parameter), $priority, array_shift($parameter));
    }

    ////////////////////////////////////////////////////////////////////////////
    // Interface functions.

    /**
     * Sets a writer.
     * 
     * @param log_writer $writer The writer.
     * @param string (optional) $name The writer name.
     * @return log A fluent interface.
     */
    public function set(log_writer $writer, $name = null)
    {
        if ($name === null)
        {
            $this->writer[] = $writer;
        }
        else
        {
            $this->writer[$name] = $writer;
        }
        
        return $this;
    }

    /**
     * Clears one or all writers.
     * 
     * @param string (optional) $name The writer name.
     * @return log A fluent interface.
     */
    public function clear($name = null)
    {
        if ($name === null)
        {
            $this->writer = array();
        }
        else
        {
            unset($this->writer[$name]);
        }
        
        return $this;
    }
    
    ////////////////////////////////////////////////////////////////////////////
    // Internal functions.
    
    /**
     * Writes a log message.
     * 
     * @param string $message The message.
     * @param integer $priority The priority.
     * @param string $timestamp The date.
     * @return log A fluent interface.
     */
    protected function _write($message, $priority, $timestamp)
    {
        if (!$this->writer)
        {
            $this->set(new log_writer_stream);
        }
        
        foreach ($this->writer as $w)
        {
            $w->write($message, $priority, $timestamp);
        }
        
        return $this;
    }
}
