/*
 * Copyright 2006-2007 Queplix Corp.
 *
 * Licensed under the Queplix Public License, Version 1.1.1 (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.queplix.com/solutions/commercial-open-source/queplix-public-license/
 *
 * 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 com.queplix.core.utils.www;

import com.queplix.core.error.IncorrectParameterException;
import com.queplix.core.modules.config.ejb.CaptionManagerLocal;
import com.queplix.core.modules.config.ejb.CaptionManagerLocalHome;
import com.queplix.core.modules.eql.CompoundKey;
import com.queplix.core.utils.JNDINames;
import com.queplix.core.utils.StringHelper;
import com.queplix.core.utils.cache.CacheObjectManager;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

/**
 * Helper class for servlets.
 *
 * @author [DAG] Dmitry Gaponenko
 * @author [ALB] Baranov Andrey
 * @author [ONZ] Oleg N. Zhovtanyuk
 */
public final class ServletHelper {

    // =============================================================== Constants

    /**
     * Charset encoding.
     */
    public static final String CHARSET_ENCODING = "UTF-8";

    /**
     * XML content-type.
     */
    public static final String CONTENT_TYPE_XML = "text/xml; charset=UTF-8";

    /**
     * HTML content-type.
     */
    public static final String CONTENT_TYPE_HTML = "text/html; charset=UTF-8";

    /**
     * Plaint text content-type.
     */
    public static final String CONTENT_TYPE_TEXT = "text/plain; charset=UTF-8";

    /**
     * Javascript text content-type.
     */
    public static final String CONTENT_TYPE_JAVASCRIPT
            = "text/javascript; charset=UTF-8";

    /**
     * Gzip content-type.
     */
    public static final String CONTENT_TYPE_GZIP = "gzip";

    /**
     * Octet-stream (binary) content-type.
     */
    public static final String CONTENT_TYPE_BINARY = "application/octet-stream";

    /**
     * "Cache-contol" http header.
     */
    public static final String HEADER_CACHE_CONTROL = "Cache-Control";

    /**
     * "Pragma" http header.
     */
    public static final String HEADER_PRAGMA = "Pragma";

    /**
     * "no-cache" http header value.
     */
    public static final String HEADER_VALUE_NO_CACHE = "no-cache";

    /**
     * Cache object attribute in servlet context
     */
    private static final String ATTR_CACHE_OBJECT = "_cache_object_";

    private static Properties popupMenuCaptions = new Properties();

    private static Properties serverMessages = new Properties();

    private static Properties focusCaptions = new Properties();

    // ================================================================= Methods

    /**
     * Retrives request URL from HTTP request.
     *
     * @param request HTTP request
     * @return request URL
     */
    public static String getRequestURL(HttpServletRequest request) {
        StringBuffer sb = request.getRequestURL();
        String qs = request.getQueryString();
        if(qs != null) {
            sb.append("?");
            sb.append(qs);
        }
        return sb.toString();
    }

    /**
     * Gets the parameter value from HTTP request as string.
     * Parameter is checked for empty value.
     *
     * @param request   HTTP request
     * @param paramName parameter name
     * @return parameter value
     * @throws IncorrectParameterException
     */
    public static String getParamAsString(HttpServletRequest request,
                                          String paramName)
            throws IncorrectParameterException {
        return getParamAsString(request, paramName, true);
    }

    /**
     * Gets the parameter value from HTTP request as string.
     *
     * @param request   HTTP request
     * @param paramName parameter name
     * @param noEmpty   whether to check for empty string
     * @return parameter value
     * @throws IncorrectParameterException
     */
    public static String getParamAsString(HttpServletRequest request,
                                          String paramName, boolean noEmpty)
            throws IncorrectParameterException {
        String value = request.getParameter(paramName);
        if(noEmpty) {
            if(StringHelper.isEmpty(value)) {
                throw new IncorrectParameterException(paramName);
            }
        }
        return value;
    }

    /**
     * Gets the parameter value from HTTP request as string.
     * Parameter is checked for empty value.
     *
     * @param request      HTTP request
     * @param paramName    parameter name
     * @param defaultValue default value for parameter
     * @return parameter value
     * @throws IncorrectParameterException
     */
    public static String getParamAsString(HttpServletRequest request,
                                          String paramName,
                                          String defaultValue)
            throws IncorrectParameterException {
        String value = request.getParameter(paramName);
        return StringHelper.isEmpty(value) ? defaultValue:value;
    }

    /**
     * Gets all values of the given parameter from HTTP request as string array.
     * Defaults are:
     * <ul>
     * <li>to check values for emptiness;</li>
     * <li>to exclude equals values.</li>
     * </ul>
     *
     * @param request   HTTP request
     * @param paramName parameter name
     * @return parameter values
     * @throws IncorrectParameterException
     */
    public static String[] getAllParamValuesAsString(HttpServletRequest request,
                                                     String paramName)
            throws IncorrectParameterException {
        return getAllParamValuesAsString(request, paramName, true, true);
    }

    /**
     * Gets all values of the given parameter from HTTP request as string array.
     *
     * @param request   HTTP request
     * @param paramName parameter name
     * @param noEmpty   whether to check for empty string
     * @param noEquals  whether to exclude equal strings
     * @return parameter values
     * @throws IncorrectParameterException
     */
    public static String[] getAllParamValuesAsString(HttpServletRequest request,
                                                     String paramName,
                                                     boolean noEmpty,
                                                     boolean noEquals)
            throws IncorrectParameterException {

        String[] values = request.getParameterValues(paramName);

        // Null (and maybe, emptiness) checks.
        if(values == null) {
            throw new IncorrectParameterException(paramName);
        }
        if(noEmpty) {
            for(int i = 0; i < values.length; i++) {
                if(StringHelper.isEmpty(values[i])) {
                    throw new IncorrectParameterException(paramName);
                }
            }
        } else {
            for(int i = 0; i < values.length; i++) {
                if(values[i] == null) {
                    throw new IncorrectParameterException(paramName);
                }
            }
        }

        // Get values.
        if(noEquals) {
            Set uniqueValues = new HashSet();
            for(int i = 0; i < values.length; i++) {
                uniqueValues.add(values[i]);
            }
            return (String[]) uniqueValues.toArray(new String[0]);
        } else {
            return values;
        }

    }

    /**
     * Gets the parameter value from HTTP request as long.
     *
     * @param request   HTTP request
     * @param paramName parameter name
     * @return parameter value
     * @throws IncorrectParameterException
     */
    public static long getParamAsLong(HttpServletRequest request,
                                      String paramName)
            throws IncorrectParameterException {
        String value = getParamAsString(request, paramName, false);
        try {
            return Long.parseLong(value);
        } catch (NumberFormatException nfe) {
            throw new IncorrectParameterException(paramName, value);
        }
    }

    /**
     * Gets all values of the given parameter from HTTP request as long array.
     *
     * @param request   HTTP request
     * @param paramName parameter name
     * @return parameter values
     * @throws IncorrectParameterException
     */
    public static long[] getAllParamValuesAsLong(HttpServletRequest request,
                                                 String paramName)
            throws IncorrectParameterException {
        String[] paramValues = getAllParamValuesAsString(request, paramName);
        long[] values = new long[paramValues.length];
        for(int i = 0; i < paramValues.length; i++) {
            try {
                values[i] = Long.parseLong(paramValues[i]);
            } catch (NumberFormatException nfe) {
                throw new IncorrectParameterException(paramName,
                        paramValues[i]);
            }
        }
        return values;
    }

    /**
     * Gets the parameter value from HTTP request as integer.
     *
     * @param request   HTTP request
     * @param paramName parameter name
     * @return parameter value
     * @throws IncorrectParameterException
     */
    public static int getParamAsInt(HttpServletRequest request,
                                    String paramName)
            throws IncorrectParameterException {
        String value = getParamAsString(request, paramName, false);
        try {
            return Integer.parseInt(value);
        } catch (NumberFormatException nfe) {
            throw new IncorrectParameterException(paramName, value);
        }
    }

    /**
     * Gets the parameter value from HTTP request as boolean.
     *
     * @param request   HTTP request
     * @param paramName parameter name
     * @return parameter value
     * @throws IncorrectParameterException
     */
    public static boolean getParamAsBoolean(HttpServletRequest request,
                                            String paramName)
            throws IncorrectParameterException {

        String value = getParamAsString(request, paramName, false);
        return (value == null) ? false:(value.equalsIgnoreCase("1") || value
                .equalsIgnoreCase("true"));
    }

    public static String getInitParamAsString(ServletConfig config,
                                              String paramName)
            throws IncorrectParameterException {

        String paramValue = config.getInitParameter(paramName);
        if(paramValue == null) {
            throw new IncorrectParameterException(paramName);
        }

        return paramValue;
    }

    public static long getInitParamAsLong(ServletConfig config,
                                          String paramName)
            throws IncorrectParameterException {

        String paramValue = config.getInitParameter(paramName);
        if(paramValue == null) {
            throw new IncorrectParameterException(paramName);
        }

        return Long.parseLong(paramValue);
    }

    public static int getInitParamAsInt(ServletConfig config, String paramName)
            throws IncorrectParameterException {

        return Integer.parseInt(getInitParamAsString(config, paramName));
    }

    /**
     * Gets compound record ID.
     *
     * @param req HTTP request
     * @return CompoundKey object
     * @throws ServletException
     */
    public static CompoundKey getCompoundRecordId(HttpServletRequest req)
            throws ServletException {

        CompoundKey compoundPkey = new CompoundKey();
        compoundPkey.addKey(getParamAsString(req, "recordId", true));

        String recordId2 = getParamAsString(req, "recordId2", false);
        if(!StringHelper.isEmpty(recordId2)) {
            compoundPkey.addKey(recordId2);
        }
        String recordId3 = getParamAsString(req, "recordId3", false);
        if(!StringHelper.isEmpty(recordId3)) {
            compoundPkey.addKey(recordId3);
        }
        String recordId4 = getParamAsString(req, "recordId4", false);
        if(!StringHelper.isEmpty(recordId4)) {
            compoundPkey.addKey(recordId4);
        }

        return compoundPkey;

    }

    /**
     * Gets EJB reference.
     *
     * @param ctx       ServletContext
     * @param jndiName  JNDI name
     * @param homeClass home class
     * @return remote object
     */
    public static Object getRemoteObject(ServletContext ctx, String jndiName,
                                         Class homeClass) {
        return getCacheObjectManager(ctx).getRemoteObject(jndiName, homeClass);
    }

    /**
     * Gets EJB local reference.
     *
     * @param ctx       ServletContext
     * @param jndiName  JNDI name
     * @param homeClass home class
     * @return local object
     */
    public static Object getLocalObject(ServletContext ctx, String jndiName,
                                        Class homeClass) {
        return getCacheObjectManager(ctx).getLocalObject(jndiName, homeClass);
    }

    /**
     * Gets EJB remote home object.
     *
     * @param ctx       ServletContext
     * @param jndiName  JNDI name
     * @param homeClass home class
     * @return remote home object
     */
    public static Object getRemoteHome(ServletContext ctx, String jndiName,
                                       Class homeClass) {
        return getCacheObjectManager(ctx).getRemoteHome(jndiName, homeClass);
    }

    /**
     * Gets EJB local home interface.
     *
     * @param ctx       ServletContext
     * @param jndiName  JNDI name
     * @param homeClass home class
     * @return local home object
     */
    public static Object getLocalHome(ServletContext ctx, String jndiName,
                                      Class homeClass) {
        return getCacheObjectManager(ctx).getLocalHome(jndiName, homeClass);
    }

    /**
     * Gets Cache manager EJB reference.
     *
     * @param ctx ServletContext
     * @return CacheObjectManager object
     */
    private static CacheObjectManager getCacheObjectManager(
            ServletContext ctx) {
        CacheObjectManager com = null;
        if((com = (CacheObjectManager) ctx.getAttribute(ATTR_CACHE_OBJECT))
                == null) {
            synchronized(ctx) {
                if((com = (CacheObjectManager) ctx.getAttribute(
                        ATTR_CACHE_OBJECT)) == null) {
                    com = new CacheObjectManager();
                    ctx.setAttribute(ATTR_CACHE_OBJECT, com);
                }
            }
        }
        return com;
    }

    public static String getPopupMenuCaption(ServletContext ctx, String langID,
                                             String captionID) {
        String key = langID + captionID;
        String strCaption = popupMenuCaptions.getProperty(key);
        if(strCaption == null) {
            CaptionManagerLocal cm = (CaptionManagerLocal)
                    getLocalObject(ctx, JNDINames.CaptionManager,
                            CaptionManagerLocalHome.class);

            strCaption = cm.getPopupMenuCaption(langID, captionID);
            popupMenuCaptions.setProperty(key, strCaption);
        }
        return strCaption;
    }

    public static String getServerMessage(ServletContext ctx, String langID,
                                          String captionID) {
        String key = langID + captionID;
        String strCaption = serverMessages.getProperty(key);
        if(strCaption == null) {
            CaptionManagerLocal cm = (CaptionManagerLocal)
                    getLocalObject(ctx, JNDINames.CaptionManager,
                            CaptionManagerLocalHome.class);

            strCaption = cm.getServerMessage(langID, captionID);
            serverMessages.setProperty(key, strCaption);
        }
        return strCaption;
    }

    public static String getFocusCaption(ServletContext ctx, String langID,
                                         String captionID) {
        String key = langID + captionID;
        String strCaption = focusCaptions.getProperty(key);
        if(strCaption == null) {
            CaptionManagerLocal cm = (CaptionManagerLocal)
                    getLocalObject(ctx, JNDINames.CaptionManager,
                            CaptionManagerLocalHome.class);

            strCaption = cm.getServerMessage(langID, captionID);
            focusCaptions.setProperty(key, strCaption);
        }
        return strCaption;
    }
}
