/*
 * (C) Copyright 2006-2008 Nuxeo SA (http://nuxeo.com/) and others.
 *
 * Licensed 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.
 *
 * Contributors:
 *     Nuxeo - initial API and implementation
 *
 * $Id$
 */

package org.nuxeo.ecm.platform.web.common.vh;

import static org.nuxeo.ecm.platform.ui.web.auth.NXAuthConstants.REQUESTED_URL;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.platform.ui.web.auth.NuxeoAuthenticationFilter;
import org.nuxeo.runtime.api.Framework;

public class VirtualHostHelper {

    private static final int HTTP_PORT_NUMBER = 80;

    private static final int HTTPS_PORT_NUMBER = 443;

    private static final Log log = LogFactory.getLog(VirtualHostHelper.class);

    private static final String X_FORWARDED_HOST = "x-forwarded-host";

    private static final String X_FORWARDED_PROTO = "x-forwarded-proto";

    private static final String X_FORWARDED_PORT = "x-forwarded-port";

    private static final String VH_HEADER = "nuxeo-virtual-host";

    private static final String VH_PARAM = "nuxeo.virtual.host";

    // Utility class.
    private VirtualHostHelper() {
    }

    private static HttpServletRequest getHttpServletRequest(ServletRequest request) {
        if (request instanceof HttpServletRequest) {
            return (HttpServletRequest) request;
        }
        return null;
    }

    /**
     * @return WebApp name : ie : nuxeo
     */
    public static String getWebAppName(ServletRequest request) {
        return getContextPath(request).replace("/", "");
    }

    /**
     * @return Server URL as : protocol://serverName:port/
     */
    public static String getServerURL(ServletRequest request) {
        return getServerURL(request, false);
    }

    private static String getServerUrl(String scheme, String serverName, int serverPort) {
        StringBuilder sbaseURL = new StringBuilder();
        sbaseURL.append(scheme);
        sbaseURL.append("://");
        sbaseURL.append(serverName);
        if (serverPort != 0) {
            if ("http".equals(scheme) && serverPort != HTTP_PORT_NUMBER || "https".equals(scheme)
                    && serverPort != HTTPS_PORT_NUMBER) {
                sbaseURL.append(':');
                sbaseURL.append(serverPort);
            }
        }
        sbaseURL.append('/');
        return sbaseURL.toString();
    }

    /**
     * @return Server URL as : protocol://serverName:port/
     */
    public static String getServerURL(ServletRequest request, boolean local) {
        String baseURL = null;
        HttpServletRequest httpRequest = getHttpServletRequest(request);
        if (httpRequest != null) {
            // Detect Nuxeo specific header for VH
            String nuxeoVH = httpRequest.getHeader(VH_HEADER);
            if (nuxeoVH == null) {
                nuxeoVH = Framework.getProperty(VH_PARAM);
            }
            if (!local && nuxeoVH != null && nuxeoVH.contains("http")) {
                baseURL = nuxeoVH;
            } else {
                // default values
                String serverName = httpRequest.getServerName();
                int serverPort = httpRequest.getServerPort();
                String scheme = httpRequest.getScheme();

                if (!local) {
                    String forwardedPort = httpRequest.getHeader(X_FORWARDED_PORT);

                    if (forwardedPort != null) {
                        try {
                            serverPort = Integer.parseInt(forwardedPort);
                        } catch (NumberFormatException e) {
                            log.error("Unable to get forwarded port from header", e);
                        }
                    }

                    String forwardedProto = httpRequest.getHeader(X_FORWARDED_PROTO);
                    if (forwardedProto != null) {
                        scheme = forwardedProto;
                    }

                    // Detect virtual hosting based in standard header
                    String forwardedHost = httpRequest.getHeader(X_FORWARDED_HOST);
                    if (forwardedHost != null) {
                        if (forwardedHost.contains(":")) {
                            serverName = forwardedHost.split(":")[0];
                            serverPort = Integer.valueOf(forwardedHost.split(":")[1]);
                        } else {
                            serverName = forwardedHost;
                            serverPort = "https".equals(scheme) ? HTTPS_PORT_NUMBER : HTTP_PORT_NUMBER;
                        }
                    }
                }

                baseURL = getServerUrl(scheme, serverName, serverPort);
            }
        }
        if (baseURL == null) {
            log.error("Could not retrieve base url correctly");
            log.debug("Could not retrieve base url correctly", new Throwable());
        }
        return baseURL;
    }

    /**
     * @return base URL as protocol://serverName:port/webappName/
     */
    public static String getBaseURL(ServletRequest request) {
        String baseURL = null;
        String serverUrl = getServerURL(request, false);
        if (serverUrl != null) {
            String webAppName = getWebAppName(request);

            baseURL = StringUtils.isNotBlank(webAppName) ? serverUrl + webAppName + '/' : serverUrl;

        }
        return baseURL;
    }

    /**
     * Returns the context path of the application. Try to get it from the {@code ServletRequest} and then from the
     * {@code org.nuxeo.ecm.contextPath} system property. Fallback on default context path {@code /nuxeo}.
     */
    public static String getContextPath(ServletRequest request) {
        HttpServletRequest httpRequest = getHttpServletRequest(request);
        String contextPath = null;
        if (httpRequest != null) {
            contextPath = httpRequest.getContextPath();
        }
        return contextPath != null ? contextPath : getContextPathProperty();
    }

    public static String getContextPathProperty() {
        return Framework.getProperty("org.nuxeo.ecm.contextPath", "/nuxeo");
    }

    /**
     * Computes the url to be redirected when logging out
     * 
     * @return redirect URL as protocol://serverName:port/webappName/...
     * @since 9.1
     */
    public static String getRedirectUrl(HttpServletRequest request) {
        String redirectURL = getBaseURL(request);
        if (request.getAttribute(REQUESTED_URL) != null) {
            redirectURL += request.getAttribute(REQUESTED_URL);
        } else if (request.getParameter(REQUESTED_URL) != null) {
            redirectURL += request.getParameter(REQUESTED_URL);
        } else if (NuxeoAuthenticationFilter.getRequestedUrl(request) != null) {
            redirectURL += NuxeoAuthenticationFilter.getRequestedUrl(request);
        } else {
            redirectURL = request.getRequestURL().toString();
            String queryString = request.getQueryString();
            if (queryString != null) {
                redirectURL += '?' + queryString;
            }
        }
        return redirectURL;
    }

}
