<?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
*/

/**
*/
abstract class Wact_Controller_Transition {

    /**
    */
    protected $channels;
    
    /**
    */
    protected $model;

    /**
    */
    protected $parameters;

    /**
    */
    protected $controller;

    /**
    */
    protected $command;

    // If the target of a viewMapping is NULL, our isset checks don't detect it.
    // What should we do in this case?

    /**
    */
    function __construct($controller, $command, $model, $parameters = array()) {
        $this->model = $model;
        $this->parameters = $parameters;
        $this->controller = $controller;
        $this->command = $command;
    }

    /**
    */
    function writeValueToChannel($channel, $name, $value) {
        $this->channels[$channel][$name] = $value;
    }

    /**
    */
    function readValueFromChannel($channel, $name) {
        if (isset($this->channels[$channel][$name])) {
            return $this->channels[$channel][$name];
        }
    }

    /**
    */
    protected function parsePath($path) {
        $list = explode('/', $path);
        array_shift($list);
        if (empty($list[count($list)-1])) {
            array_pop($list);
        }
        return $list;
    }

    /**
    */
    protected function buildControllerChainFromCommandPath($controller, $commandPath) {
        $names = $this->parsePath($commandPath);
        $chain = array($controller);
        $focus = $controller;
        foreach( $names as $name) {
            $child = $focus->getChild($name);
            if (is_null($child)) {
                throw new Wact_Controller_Exception('Invalid path "{Path}"', $commandPath);
            }
            $chain[] = $child;
            $focus = $child;
        }
        return $chain;
    }

    /**
    */
    protected function buildCommandPathArrayFromChain($chain) {
        $path = array();
        for ($i = 0; $i < count($chain); $i++) {
            $path[$i] = $chain[$i]->name;
        }
        return $path;
    }

    /**
    */
    protected function getLastSharedPosition($target, $context) {
        $i = 1;
        foreach($context as $name) {
            if (!isset($target[$i]) || $target[$i] != $name) {
                return $i-1;
            }
            $i++;
        }
        return $i - 1;
    }

    /**
    */
    function build() {
    
        $context = array();
        $root = $this->controller;
        while (is_object($root->parent)) {
            $context[] = $root->getName();
            $root = $root->parent;
        }
        $context = array_reverse($context);

        $chain = $this->buildControllerChainFromCommandPath($root, $this->command);
        $target = $this->buildCommandPathArrayFromChain($chain);

        $lastSharedPosition = $this->getLastSharedPosition($target, $context);

        $childName = NULL;
        for ($i = count($chain) - 1; $i >= 0; $i--) {
            $node = $chain[$i];
            $parameters = $node->getParameters();
            foreach(array_reverse(array_keys($parameters)) as $key) {
                $parameter = $parameters[$key];
                $name = $parameter->getName();
                
                if ($node->isDispatcher($name)) {
                    // Write a dispatch parameter
                    if ($childName === NULL) {
                        // This occurs when our command targets a node that has a dispatch
                        // parameter, but the comamnd does not specify which children should 
                        // be chosen.  We must not generate a value for the dispatch parameter,
                        // instead leaving it to default to the proper value or to fail dispatching.
                        continue;
                    }
                    $value = $childName;
                } else if ($i <= $lastSharedPosition &&
                        !is_null($parameter->getModelLocation()) && 
                        !isset($this->parameters[$name])) {

                    // Use a shared value
                    $value = Wact_Property_Binder::getPath($this->model, $parameter->getModelLocation());
                } else {
                
                    // use a standard value
                    if (isset($this->parameters[$name])) {
                        $value = $this->parameters[$name];
                    } else {
                        // throw new Wact_Exception_Base('Parameter {Parameter} not satisfied', $name);
                        $value = NULL; 
                        // This is so wrong.  Didn't see this bug before :(
                        // Can't fix it yet, cuz it breaks too much stuff
                    }
                }
                $parameter->writeValueToTransition($this, $value);
            }
            $childName = $node->getName();
        }

    }

}

?>