<?php
// Constants used for the 'filter_seen' preference.
/** @const INGO_SCRIPT_FILTER_UNSEEN  Only filter unseen messages. */ define('INGO_SCRIPT_FILTER_UNSEEN', 1);
/** @const INGO_SCRIPT_FILTER_SEEN  Only filter seen messages.     */ define('INGO_SCRIPT_FILTER_SEEN', 2);

/**
 * The Ingo_Script:: class provides a common abstracted interface to the
 * script-generation subclasses.
 *
 * $Horde: ingo/lib/Script.php,v 1.30 2004/05/20 15:48:47 jan Exp $
 *
 * See the enclosed file LICENSE for license information. If you
 * did not receive this file, see http://www.horde.org/licenses.
 *
 * @author  Brent J. Nordquist <bjn@horde.org>
 * @version $Revision: 1.30 $
 * @since   Ingo 0.1
 * @package Ingo
 */
class Ingo_Script {

    /**
     * The script class' additional parameters.
     *
     * @var array $_params
     */
    var $_params = array();

    /**
     * The list of actions allowed (implemented) for this driver.
     * This SHOULD be defined in each subclass.
     *
     * @var array $_actions
     */
    var $_actions = array();

    /**
     * The categories of filtering allowed.
     * This SHOULD be defined in each subclass.
     * 
     * @var array $_categories
     */
    var $_categories = array();

    /**
     * The list of tests allowed (implemented) for this driver.
     * This SHOULD be defined in each subclass.
     *
     * @var array $_tests
     */
    var $_tests = array();

    /**
     * The types of tests allowed (implemented) for this driver.
     * This SHOULD be defined in each subclass.
     *
     * @var array $_types
     */
    var $_types = array();

    /**
     * Can tests be case sensitive?
     *
     * @var boolean $_casesensitive
     */
    var $_casesensitive = false;

    /**
     * Does the driver support setting IMAP flags?
     *
     * @var boolean $_supportIMAPFlags
     */
    var $_supportIMAPFlags = false;

    /**
     * Does the driver support the stop-script option?
     *
     * @var boolean $_supportStopScript
     */
    var $_supportStopScript = false;

    /**
     * Can this driver perform on demand filtering?
     *
     * @var boolean $_ondemand
     */
    var $_ondemand = false;

    /**
     * Does the driver require a script file to be generated?
     *
     * @var boolean $_scriptfile
     */
    var $_scriptfile = false;

    /**
     * Attempts to return a concrete Ingo_Script instance based on $script.
     *
     * @param string $script          The type of Ingo_Script subclass to
     *                                return. The code is dynamically
     *                                included.
     * @param optional array $params  Hash containing additional paramters to
     *                                be passed to the subclass' constructor.
     *
     * @return object Ingo_Script  The newly created concrete Ingo_Script
     *                             instance, or false on error.
     */
    function &factory($script, $params = array())
    {
        $script = basename($script);
        include_once dirname(__FILE__) . '/Script/' . $script . '.php';
        $class = 'Ingo_Script_' . $script;
        if (class_exists($class)) {
            return $ret = &new $class($params);
        } else {
            Horde::fatal(PEAR::raiseError(sprintf(_("Unable to load the definition of %s."), $class)), __FILE__, __LINE__);
        }
    }

    /**
     * Attempts to return a reference to a concrete Ingo_Script instance
     * based on $script. It will only create a new instance if no
     * Ingo_Script instance with the same parameters currently exists.
     *
     * This method must be invoked as: $var = &Ingo_Script::singleton()
     *
     * @param string $script          The type of concrete Ingo_Script
     *                                subclass to return. This is based on the
     *                                script type ($script). The code is
     *                                dynamically included.
     * @param optional array $params  A hash containing additional parameters
     *                                for the subclass.
     *
     * @return object Ingo_Script  The concrete Ingo_Script reference, or
     *                             false on error.
     */
    function &singleton($script, $params = array())
    {
        static $instances;

        if (!isset($instances)) {
            $instances = array();
        }

        $signature = serialize(array($script, $params));
        if (!isset($instances[$signature])) {
            $instances[$signature] = &Ingo_Script::factory($script, $params);
        }

        return $instances[$signature];
    }

    /**
     * Constructor.
     *
     * @access public
     *
     * @param optional array $params  A hash containing parameters needed.
     */
    function Ingo_Script($params = array())
    {
        global $registry;

        $this->_params = $params;

        /* Determine if ingo should handle the blacklist. */
        $key = array_search(INGO_STORAGE_ACTION_BLACKLIST, $this->_categories);
        if ($key !== false && ($registry->hasMethod('mail/blacklistFrom') != 'ingo')) {
            unset($this->_categories[$key]);
        }
            
        /* Determine if ingo should handle the whitelist. */
        $key = array_search(INGO_STORAGE_ACTION_WHITELIST, $this->_categories);
        if ($key !== false && ($registry->hasMethod('mail/whitelistFrom') != 'ingo')) {
            unset($this->_categories[$key]);
        }
    }

    /**
     * Returns the available actions for this driver.
     *
     * @access public
     *
     * @return array  The list of available actions.
     */
    function availableActions()
    {
        return $this->_actions;
    }

    /**
     * Returns the available categories for this driver.
     *
     * @access public
     *
     * @return array  The list of categories.
     */
    function availableCategories()
    {
        return $this->_categories;
    }

    /**
     * Returns the available tests for this driver.
     *
     * @access public
     *
     * @return array  The list of tests actions.
     */
    function availableTests()
    {
        return $this->_tests;
    }

    /**
     * Returns the available test types for this driver.
     *
     * @access public
     *
     * @return array  The list of test types.
     */
    function availableTypes()
    {
        return $this->_types;
    }

    /**
     * Returns if this driver allows case sensitive searches.
     *
     * @access public
     *
     * @return boolean  Does this driver allow case sensitive searches?
     */
    function caseSensitive()
    {
        return $this->_casesensitive;
    }

    /**
     * Returns if this driver allows IMAP flags to be set.
     *
     * @access public
     *
     * @return boolean  Does this driver allow IMAP flags to be set?
     */
    function imapFlags()
    {
        return $this->_supportIMAPFlags;
    }

    /**
     * Returns if this driver supports the stop-script option.
     *
     * @access public
     *
     * @return boolean  Does this driver support the stop-script option?
     */
    function stopScript()
    {
        return $this->_supportStopScript;
    }

    /**
     * Returns a script previously generated with generate().
     *
     * @abstract
     *
     * @access public
     *
     * @return string  The script.
     */
    function toCode()
    {
        return '';
    }

    /**
     * Can this driver generate a script file?
     *
     * @access public
     *
     * @return boolean  True if generate() is available, false if not.
     */
    function generateAvailable()
    {
        return $this->_scriptfile;
    }

    /**
     * Generates the script to do the filtering specified in
     * the rules.
     *
     * @abstract
     *
     * @access public
     *
     * @return string  The script.
     */
    function generate()
    {
        return '';
    }

    /**
     * Can this driver perform on demand filtering?
     *
     * @access public
     *
     * @return boolean  True if perform() is available, false if not.
     */
    function performAvailable()
    {
        return $this->_ondemand;
    }

    /**
     * Perform the filtering specified in the rules.
     *
     * @abstract
     *
     * @access public
     *
     * @param optional array $params  The parameter array.
     *
     * @return boolean  True if filtering performed, false if not.
     */
    function perform($params = array())
    {
        return false;
    }

    /**
     * Is the apply() function available?
     *
     * @access public
     *
     * @return boolean  True if apply() is available, false if not.
     */
    function canApply()
    {
        return $this->performAvailable();
    }

    /**
     * Apply the filters now.
     * This is essentially a wrapper around perform() that allows that
     * function to be called from within Ingo ensuring that all necessary
     * parameters are set.
     *
     * @abstract
     *
     * @access public
     *
     * @return boolean  See perform().
     */
    function apply()
    {
        return $this->perform();
    }

    /**
     * Is this a valid rule?
     *
     * @access private
     *
     * @param integer $type  The rule type.
     *
     * @return boolean  Whether the rule is valid or not for this driver.
     */
    function _validRule($type)
    {
        return (!empty($type) && in_array($type, array_merge($this->_categories, $this->_actions)));
    }
}
