<?php
/**
 * ActionInvocation.
 * @package magic.core
 * @subpackage internal
 */
/**
 * Actionクラスを実行するクラスです.
 * @package magic.core
 * @subpackage internal
 * @author T.Okumura
 * @version 1.0.0
 * @final
 */
final class ActionInvocation {
    /**
     * HttpRequestクラスを保持します.
     * @var HttpRequest
     */
    private $_request = NULL;
    /**
     * HttpSessionクラスを保持します.
     * @var HttpSession
     */
    private $_session = NULL;
    /**
     * NameConfigureクラスを保持します.
     * @var NameConfigure
     */
    private $_name = NULL;
    /**
     * PathConfigureクラスを保持します.
     * @var PathConfigure
     */
    private $_path = NULL;
    /**
     * DataSourceConfigureクラスを保持します.
     * @var DataSourceConfigure
     */
    private $_dataSource = NULL;
    /**
     * コンストラクタ.
     * @param HttpRequest $request HttpRequestクラス
     * @param HttpSession $session HttpSessionクラス
     * @param NameConfigure $name NameConfigureクラス
     * @param PathConfigure $path PathConfigureクラス
     * @param DataSourceConfigure $dataSource DataSourceConfigureクラス
     */
    public function __construct(HttpRequest $request, HttpSession $session, NameConfigure $name, PathConfigure $path,
            DataSourceConfigure $dataSource) {
        $this->_request = $request;
        $this->_session = $session;
        $this->_name = $name;
        $this->_path = $path;
        $this->_dataSource = $dataSource;
    }
    /**
     * Actionクラスをロードして実行します.
     * @param string $actionName ロードするActionクラス名
     * @param string $methodName 実行するファンクション名
     * @return ActionResult Actionクラスの実行結果
     * @throws MagicException Actionクラスで発生した実行時エラー
     */
    public function invoke($actionName, $methodName) {
        $action = $this->_inject(ActionClassLoader::load($actionName, $this->_name, $this->_path));
        $action->intialize($this->_request, $this->_session);
        $result = $action->invoke(ActionClassLoader::getMethod($action, $methodName, $this->_name));
        $result->setResponse($this->_extract($action, $result->getResponse()));
        return $result;
    }
    /**
     * Actionクラスのプロパティへのinject処理を実行します.
     * <p>
     * DAOクラス、DTOクラス及びリクエストパラメータを
     * Actionクラスのプロパティにセットします。<br/>
     * DAOクラスの{@link Dao::initialize()}はここでコールされるため、
     * Actionクラスでは、すでにデータベース接続が完了した状態です。<br/>
     * DTOクラスがロードされた場合、DTOクラスのセッターメソッドに、
     * リクエストパラメータをセットします。
     * </p>
     * @param Action $action Actionクラス
     * @return Action injectされたActionクラス
     * @see Dao::initialize()
     */
    private function _inject(Action $action) {
        $props = ActionClassLoader::getProperties($action, $this->_path);
        foreach ($props['dao'] as $key => $value) {
            $class = DaoClassLoader::load($value, $this->_name, $this->_path);
            $class->initialize($this->_dataSource);
            $action->inject($key, $class);
        }
        foreach ($props['dto'] as $key => $value) {
            $class = DtoClassLoader::load($value, $this->_name, $this->_path);
            $methods = DtoClassLoader::getSetterMethods($class);
            foreach ($methods as $method) {
                $name = preg_replace('/set_?/i', '', $method, 1);
                if ($val = $this->_request->get($this->_name->getRequestNamingType()->apply($name))) {
                    $class->$method($val);
                }
            }
            $action->inject($key, $class);
        }
        foreach ($props['prop'] as $key => $value) {
            if ($val = $this->_request->get($this->_name->getRequestNamingType()->apply($key))) {
                $action->inject($key, $val);
            }
        }
        return $action;
    }
    /**
     * Actionクラスのプロパティからレスポンスにセットします.
     * <p>
     * ActionクラスのプロパティがDTOクラスの場合、
     * DTOクラスのゲッターメソッドから値を取得して、
     * レスポンスにセットします。<br/>
     * そうでない場合はスカラー値としてセットします。
     * </p>
     * @param Action $action Actionクラス
     * @param HttpResponse $response HttpResponseクラス
     * @return HttpResponse 設定後のHttpResponseクラス
     */
    private function _extract(Action $action, HttpResponse $response) {
        $methods = ActionClassLoader::getGetterMethods($action);
        foreach ($methods as $method) {
            $obj = $action->$method();
            if ($obj instanceof Dto) {
                $dtoMethods = DtoClassLoader::getGetterMethods($obj);
                foreach ($dtoMethods as $dtoMethod) {
                    $dtoObj = $obj->$dtoMethod();
                    $name = preg_replace(array('/get_?/i', '/is_?/i'), '', $dtoMethod, 1);
                    $response->set($this->_name->getRequestNamingType()->apply($name), $dtoObj);
                }
            } else {
                $name = preg_replace(array('/get_?/i', '/is_?/i'), '', $method, 1);
                $response->set($this->_name->getRequestNamingType()->apply($name), $obj);
            }
        }
        return $response;
    }
}
// EOF.