/*
 * Copyright (c) 2016 NIBIO <http://www.nibio.no/>. 
 * 
 * This file is part of VIPSCommon.
 * VIPSCommon is free software: you can redistribute it and/or modify
 * it under the terms of the NIBIO Open Source License as published by 
 * NIBIO, either version 1 of the License, or (at your option) any
 * later version.
 * 
 * VIPSCommon is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * NIBIO Open Source License for more details.
 * 
 * You should have received a copy of the NIBIO Open Source License
 * along with VIPSCommon.  If not, see <http://www.nibio.no/licenses/>.
 * 
 */

package no.nibio.vips.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.stream.Collectors;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;

/**
 * @copyright 2016 <a href="http://www.nibio.no/">NIBIO</a>
 * @author Tor-Einar Skog <tor-einar.skog@nibio.no>
 */
public class ServletUtil {
    /**
     * Because we may be behind a reverse proxy, we might have to read Apache headers
     * to resolve the actual called server name
     * @param request
     * @return 
     */
    public static String getServerName(HttpServletRequest request)
    {
        // request.getServerName() is fallback if we're not behind a reverse proxy
        String serverName = request.getHeader("X-Forwarded-Host") != null ? request.getHeader("X-Forwarded-Host") : request.getServerName();
        // If several reverse proxies are involved, parse and get the first one.
        if(serverName.contains(", "))
        {
            serverName = serverName.split(", ")[0];
        }
        return serverName;
    }
    
    /**
     * For debugging purposes. Dumps all headers in request to stdout
     * @param request 
     */
    public static void debugDumpHeaders(HttpServletRequest request)
    {
        for(Enumeration<String> e = request.getHeaderNames(); e.hasMoreElements();e.nextElement())
        {
            String headerName = e.nextElement();
            System.out.println(headerName + ": " + request.getHeader(headerName));
        }
    }
    
    /**
     * 
     * @param request
     * @return the context path, regardless of reverse proxy mixing stuff up
     */
    public static String getContextPath(HttpServletRequest request)
    {
        return request.getHeader("X-Forwarded-Host") != null ? "" : request.getContextPath();
    }
    
    /**
     * 
     * @param request
     * @return 
     */
    public static String getNormalizedServerNameAndPort(HttpServletRequest request)
    {
        return ServletUtil.getServerName(request) + (request.getServerPort() != 80 && request.getHeader("X-Forwarded-Host") == null ? ":" + request.getServerPort() : "");
    }
    
    /**
     * Attempts to get the most correct client IP, even when this app is 
     * placed behind a reverse proxy
     * @param request
     * @return 
     */
    public static String getClientIP(HttpServletRequest request)
    {
        String XFF = request.getHeader("X-Forwarded-For");
        if(XFF != null && ! XFF.isEmpty())
        {
            String[] IPs = XFF.split(",");
            return IPs[0];
        }
        else
        {
            return request.getRemoteAddr();
        }
    }
    
    /**
     * Reconstructs the entire URL (including protocol) that was used for this request
     * TODO: Check for https (how to??)
     * @param request
     * @return 
     */
    public static String getCompleteRequestURL(HttpServletRequest request)
    {
        return "http://" + getNormalizedServerNameAndPort(request) + getFullRequestURI(request);
    }
    
    /**
     * This method builds a full RequestURI, including query string, ignoring ServletContext,
     * thus enabling correct restore of request when behind reverse proxy
     * @param request
     * @return 
     */
    public static String getFullRequestURI(HttpServletRequest request)
    {
        StringBuilder retVal = new StringBuilder(request.getServletPath());
        if(request.getPathInfo() != null)
        {
            retVal.append(request.getPathInfo());
        }
        if(request.getQueryString() != null)
        {
            retVal.append("?").append(request.getQueryString());
        }
        return retVal.toString();
    }
    
    public static Integer getIntegerParameter(HttpServletRequest request, String parameterName)
    {
        try
        {
            return Integer.valueOf(request.getParameter(parameterName));
        }
        catch(NullPointerException ex) {return null; }
        catch(NumberFormatException nfe) {return null;}
    }
    
    /**
     * Quickest way for caller to get a cookie
     * @param request
     * @param cookieName
     * @return 
     */
    public static Cookie getCookie(HttpServletRequest request, String cookieName)
    {
        if(request.getCookies() == null)
        {
            return null;
        }
        
        for(Cookie cookie:request.getCookies())
        {
            if(cookie.getName().equals(cookieName))
            {
                return cookie;
            }
        }
        return null;
    }
    
    /**
     * Removes the given parameters from a query string
     * @param parametersToRemove
     * @return 
     */
    public static String getCleanedQueryString(String servletPathWithQueryString, String parameterToRemove)
    {
        List<String> whatsLeft;
        String servletPath = servletPathWithQueryString.split("\\?").length > 1 ? 
                servletPathWithQueryString.split("\\?")[0] 
                : ""; 
        whatsLeft = (servletPathWithQueryString.split("\\?").length > 1 ?
                Arrays.asList(servletPathWithQueryString.split("\\?")[1].split("&")) :
                new ArrayList<String>())
            .stream().filter(
                paramStr ->  ! paramStr.contains(parameterToRemove)
            ).collect(Collectors.toList());
        return servletPath + (!whatsLeft.isEmpty() ? "?" + String.join("&", whatsLeft) : "");
    }
}