/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package freemarker.test.servlet;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import freemarker.ext.servlet.FreemarkerServlet;
import freemarker.template.utility.StringUtil;

/**
 * MVC controller servlet used for {@link FreemarkerServlet} JUnit tests. It understands these request parameters:
 * <ul>
 *   <li>{@value #VIEW_PARAM_NAME}: The servlet-relative path of the URL to which we forward after the action.
 *       Required unless the action will return with non-{@code null}.
 *   <li>{@value #ACTION_PARAM_NAME}: The full qualified name of the {@link Model2Action} implementation used as the
 *       action. Optional, by default it will be {@link DefaultModel2TesterAction#INSTANCE}. 
 * </ul>
 */
public class Model2TesterServlet extends HttpServlet {
    
    private static final Logger LOG = LoggerFactory.getLogger(Model2TesterServlet.class);
    
    public static final String VIEW_PARAM_NAME = "view";
    public static final String ACTION_PARAM_NAME = "action";
    public static final String VIEW_SERVLET_PARAM_NAME = "viewServlet";

    private static final long serialVersionUID = 1L;

    private static final String CHARSET = "UTF-8";

    @Override
    protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException,
            IOException {
        req.setCharacterEncoding(CHARSET);
        resp.setCharacterEncoding(CHARSET);

        final Model2Action action;
        {
            final String actionClassName = req.getParameter(ACTION_PARAM_NAME);
            if (actionClassName != null) {
                try {
                    action = (Model2Action) Class.forName(actionClassName).newInstance();
                } catch (final Throwable e) {
                    final String msg = "Failed to instantiate action: " + actionClassName;
                    resp.sendError(
                            HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                            msg + ".\nException (see log for trace): " + e);
                    LOG.error(msg, e);
                    return;
                }
            } else {
                action = DefaultModel2TesterAction.INSTANCE;
            }
        }
        
        final String actionViewPath;
        try {
            actionViewPath = action.execute(req, resp);
        } catch (final Throwable e) {
            final String msg = action.getClass().getName() + ".execute has thrown exception";
            resp.sendError(
                    HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                    msg + ".\nException (see log for trace): " + e);
            LOG.error(msg, e);
            return;
        }

        final String paramViewPath = req.getParameter(VIEW_PARAM_NAME);
        final String viewPath = removeStartingSlash(actionViewPath != null ? actionViewPath : paramViewPath);
        
        if (viewPath == null) {
            resp.sendError(
                    HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                    "No view was specified as parameter or as action return value.");
            return;
        }
        
        final String paramViewServlet = req.getParameter(VIEW_SERVLET_PARAM_NAME);
        if (paramViewServlet == null) {
            req.getRequestDispatcher(viewPath).forward(req, resp);
        } else {
            final RequestDispatcher requestDispatcher = getServletContext().getNamedDispatcher(paramViewServlet);
            if (requestDispatcher == null) {
                throw new ServletException("Can't find request dispatched for servlet name "
                        + StringUtil.jQuote(paramViewServlet) + ".");
            }
            
            final HttpServletRequestWrapper viewReq = new HttpServletRequestWrapper(req) {

                @Override
                public String getPathInfo() {
                    return viewPath;
                }
                
            };
            
            requestDispatcher.forward(viewReq, resp);
        }
    }

    private String removeStartingSlash(final String s) {
        if (s == null) return null;
        return s.startsWith("/") ? s.substring(1) : s;
    }

    @Override
    protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException,
            IOException {
        super.doGet(req, resp);
    }

}
