<?php
    /* vim: set expandtab tabstop=4 shiftwidth=4: */
    // +----------------------------------------------------------------------+
    // | PHP version 4ΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚ|
    // +----------------------------------------------------------------------+
    // | Copyright (c) 1997-2003 The PHP GroupΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚ|
    // +----------------------------------------------------------------------+
    // | This source file is subject to version 2.0 of the PHP license,ΚΚΚΚΚΚΚ|
    // | that is bundled with this package in the file LICENSE, and isΚΚΚΚΚΚΚΚ|
    // | available through the world-wide-web atΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚ|
    // | http://www.php.net/license/2_02.txt.ΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚ|
    // | If you did not receive a copy of the PHP license and are unable toΚΚΚ|
    // | obtain it through the world-wide-web, please send a note toΚΚΚΚΚΚΚΚΚΚ|
    // | license@php.net so we can mail you a copy immediately.ΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚ|
    // +----------------------------------------------------------------------+
    // | Authors: Herr Witten <LingWitt@yahoo.com>ΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚΚ       |
    // |ΚΚΚΚΚΚΚΚΚΚ                                                            |
    // +----------------------------------------------------------------------+
    //
    // $Id$
    
    require_once "PEAR.php";
    require_once "PEAR/DelegateInterface.php";
    
    class PEAR_DelegateOwner extends PEAR
    {
        var $_delegates     = array();
        var $_method_map    = array();
    
        public function addDelegate($variableRequired)
        {
            $args = array_values(func_get_args());
            
            foreach ($args as $delegate)
            {
                if (!is_object($arg))
                    $delegate = new $delegate;
                            
                if (!($delegate instanceof PEAR_DelegateInterface))
                    continue;
                            
                $delegate->setOwner($this);
                $this->_delegates[get_class($delegate)] = $delegate;
            }
        }
        
        public function &getDelegates()
        {
            return $this->_delegates;
        }
        
        //Classnames
        public function getDelegate($variableRequired)
        {
            $args = func_get_args();
            
            foreach ($args as $arg)
            {
                foreach ($this->_delegates as $delegate)
                {
                    if ($delegate instanceof $arg)
                    {
                        $results[$arg] = $delegate;
                    }
                }
            }
            
            return $results;
        }
        
        //Classnames
        public function getDelegateExact($classname)
        {
            $classname = strtolower($classname);
            
            if (array_key_exists($classname, $this->_delegates))
            {
                return $this->_delegates[$classname];
            }
            else
            {
                return null;
            }
            
        }
        
        public function hasDelegate($delegate)
        {        
            if (is_string($delegate) && $this->getDelegate($delegate))
            {
                return true;
            }
            else if (is_object($delegate))
            {
                foreach ($this->_delegates as $delegateObject)
                {
                    if ($delegateObject === $delegate)
                        return true;
                }
            }
            
            return false;
        }
        
        //Classnames
        public function hasDelegateExact($classname)
        {        
            if ($this->getDelegateExact($classname))
            {
                return true;
            }
            
            return false;
        }
        
        public function removeDelegates()
        {
            unset($this->_method_map);
            unset($this->_delegates);
        }
        
        private function filterMethodMapWithDelegate(PEAR_DelegateInterface $filterDelegate)
        {
            $result = array();
            
            $method_map_keys    = array_keys($this->_method_map);
            $method_map_values  = array_values($this->_method_map);
            
            for ($i = 0, $count = count($method_map_values); $i < $count; $i++)
            {
                $delegate = $this->_method_map[$i];
                
                if ($delegate === $filterDelegate)
                {
                    continue;
                }
                
                $result[$method_map_keys[$i]] = $delegate;
            }
            
            return $result;
        }
        
        //Classnames
        public function removeDelegate($variableRequired)
        {
            $args = func_get_args();
            
            foreach ($args as $arg)
            {
                if ($delegates = $this->getDelegate($arg))
                {
                    foreach ($delegates as $delegate)
                    {
                        unset($this->_delegates[get_class($delegate)]);
                        $this->_delegates = $this->filterMethodMapWithDelegate($delegate);
                    }                
                }
            }
        }
        
        //Classnames
        public function removeDelegateExact($variableRequired)
        {
            $args = func_get_args();
            
            foreach ($args as $arg)
            {
                if ($delegate = $this->getDelegateExact($arg))
                {
                    unset($this->_delegates[get_class($delegate)]);
                    $this->_delegates = $this->filterMethodMapWithDelegate($delegate);       
                }
            }
        }
        
        protected function cacheMethod($method)
        {
            foreach ($this->_delegates as $delegate)
            {
                if (method_exists($delegate, $method))
                {
                    $this->_method_map[$method] = $delegate;
                    return;
                }
            }
            
            throw new Exception("No Such Method: $method()");
        }
        
        public function __call($method, $args)
        {
            $method = strtolower($method);
            
            if (!array_key_exists($method, $this->_method_map))
            {
                try {$this->cacheMethod($method);}
                    catch(Exception $exception)
                    {
                        die ("Fatal: " . $exception->getMessage());
                    }
            }
            
            return call_user_func_array(array($this->_method_map[$method], $method), $args);
        }
    }
?>