/*******************************************************************************
 * Copyright (c) 2014, 2015 Jens Reimann.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Jens Reimann - initial API and implementation
 *******************************************************************************/

package de.dentrassi.osgi.web.controller;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import de.dentrassi.osgi.web.ModelAndView;
import de.dentrassi.osgi.web.RequestHandler;
import de.dentrassi.osgi.web.controller.binding.BindingManager;
import de.dentrassi.osgi.web.controller.binding.PathVariableBinder;
import de.dentrassi.osgi.web.controller.binding.RequestParameterBinder;
import de.dentrassi.osgi.web.controller.form.FormDataBinder;
import de.dentrassi.osgi.web.controller.routing.RequestMappingInformation;
import de.dentrassi.osgi.web.controller.routing.RequestMappingInformation.Match;

public class ControllerCall
{
    private final RequestMappingInformation rmi;

    private final Method m;

    private final ControllerInterceptorProcessor[] interceptors;

    private final Object controller;

    public ControllerCall ( final Object controller, final RequestMappingInformation rmi, final Method m, final Set<ControllerInterceptorProcessor> interceptors )
    {
        this.controller = controller;
        this.rmi = rmi;
        this.m = m;
        this.interceptors = interceptors.toArray ( new ControllerInterceptorProcessor[interceptors.size ()] );
    }

    public Match matches ( final HttpServletRequest request )
    {
        return this.rmi.matches ( request );
    }

    protected RequestHandler runForward ( final int index, final HttpServletRequest request, final HttpServletResponse response, final Callable<RequestHandler> last ) throws Exception
    {
        if ( index < this.interceptors.length )
        {
            return this.interceptors[index].before ( this.controller, this.m, request, response, ( chainReq, chainRes ) -> {
                try
                {
                    return runForward ( index + 1, chainReq, chainRes, last );
                }
                catch ( final Exception e )
                {
                    throw new RuntimeException ( e );
                }
            } );
        }
        else
        {
            return last.call ();
        }
    }

    protected RequestHandler runBackward ( final int index, final HttpServletRequest request, final HttpServletResponse response, final Callable<RequestHandler> last ) throws Exception
    {
        if ( index > 0 )
        {
            return this.interceptors[index - 1].after ( this.controller, this.m, request, response, ( chainReq, chainRes ) -> {
                try
                {
                    return runBackward ( index - 1, chainReq, chainRes, last );
                }
                catch ( final Exception e )
                {
                    throw new RuntimeException ( e );
                }
            } );
        }
        else
        {
            return last.call ();
        }
    }

    public RequestHandler call ( final Match match, final HttpServletRequest request, final HttpServletResponse response )
    {
        // process call

        try
        {
            final RequestHandler result = runForward ( 0, request, response, ( ) -> processCall ( match, request, response ) );

            return runBackward ( this.interceptors.length, request, response, new Callable<RequestHandler> () {

                @Override
                public RequestHandler call () throws Exception
                {
                    return result;
                }
            } );
        }
        catch ( final Exception e )
        {
            throw new RuntimeException ( e );
        }
    }

    protected RequestHandler processCall ( final Match match, final HttpServletRequest request, final HttpServletResponse response ) throws Exception
    {
        final Map<String, Object> data = new HashMap<String, Object> ();

        data.put ( "request", request );
        data.put ( "response", response );
        data.put ( "session", request.getSession () );

        final BindingManager manager = BindingManager.create ( data );

        manager.addBinder ( new RequestParameterBinder ( request ) );
        manager.addBinder ( new PathVariableBinder ( match ) );
        manager.addBinder ( new FormDataBinder ( request, this.controller ) );

        final de.dentrassi.osgi.web.controller.binding.BindingManager.Call call = manager.bind ( this.m, this.controller );
        final Object result = call.invoke ();

        if ( result instanceof ModelAndView )
        {
            return new ModelAndViewRequestHandler ( (ModelAndView)result, this.controller.getClass (), this.m );
        }
        else if ( result instanceof String )
        {
            return new ModelAndViewRequestHandler ( new ModelAndView ( (String)result ), this.controller.getClass (), this.m );
        }
        else if ( result == null )
        {
            return new NoOpRequestHandler ();
        }
        else
        {
            throw new IllegalStateException ( String.format ( "Response type %s is unsupported", result.getClass () ) );
        }

    }
}

@@ -4553,24 +4553,87 @@
 ession () );
+%0A        data.put ( %22principal%22, request.getUserPrincipal () );
 %0A%0A        fi

allow injecting the Principal