/***********************************************************************
 *
 * JISC.ControllerServlet.java
 *
 * ---------------------------------------------------------------------
 *
 * @author Dennis Laws
 * @version 0.0
 *
 ***********************************************************************/

//*********************//
//      Package        //
//*********************//
package JISC;

//*********************//
//      Imports        //
//*********************//
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

/***********************************************************************
 * This class is a <STRONG>Front Control</STRONG> for my web site.
 * It is the gateway for ALL requests to my web site.
 ***********************************************************************/
public class ControllerServlet extends HttpServlet
{
    public static final String HTTP = "http";
    public static final String HTTPS = "https";
    public static final int STD_HTTP_PORT = 80;
    public static final int STD_HTTPS_PORT = 443;

    private ServletConfig m_config;
    
    private boolean m_firstAccess = true;
    private String m_defaultScheme;
    private boolean m_enable_security;
    private int m_defaultPort;
    
    public void doInit( ServletConfig config )
    {
        m_config = config;
    }    
    
    /*******************************************************************
     * This method services an HTTP GET request.
     *
     * @param request   Provides incoming information from browser.
     * @param response  Provices resources for sending information
     *                  to the browser.
     *******************************************************************/
    public void doGet( HttpServletRequest request,
                       HttpServletResponse response )
        throws ServletException, IOException
    {
        _processRequest(request, response);
    }


    /*******************************************************************
     * This method services an HTTP POST request.
     *
     * @param request   Provides incoming information from browser.
     * @param response  Provices resources for sending information
     *                  to the browser.
     *******************************************************************/
    public void doPost( HttpServletRequest request,
                        HttpServletResponse response )
        throws ServletException, IOException
    {
        _processRequest(request, response);
    }


    /*******************************************************************
     * Provides primary control processing of request.
     *
     * @param request   Provides incoming information from browser.
     * @param response  Provices resources for sending information
     *                  to the browser.
     *******************************************************************/
    private void _processRequest( HttpServletRequest request,
                                  HttpServletResponse response )
        throws ServletException, IOException
    {
        int filterDepth = BasicFilter.getDepth(request);
        String pageRequestID = "";

        if (m_firstAccess)
        {
            m_defaultScheme = request.getScheme();
            m_defaultPort = request.getServerPort();
            
            m_enable_security = (request.getContextPath().equals("") == false);
            
            m_firstAccess = false;
        }
        
        String desiredScheme = m_defaultScheme;
        int desiredPort = m_defaultPort;

        String redirectString = null;

        while (pageRequestID != null)
        {
            pageRequestID = null;
            
            //==================================
            // Retrieve request handler
            //==================================
            BasicHandler handler;
            handler = BasicHandlerManager.getRequestHandler(m_config, request);
            
            if (m_enable_security && (handler instanceof SecureHandler))
            {
                SecureHandler secureHandler = (SecureHandler) handler;
                
                desiredScheme = secureHandler.getDesiredScheme();
                desiredPort = secureHandler.getDesiredPort();
            }

            if (!request.getScheme().equals(desiredScheme) || 
                (request.getServerPort() != desiredPort))
            {
                redirectString = buildNewUrlString(request,desiredScheme,desiredPort);

                // Temporarily store the request attributes in session.
                storeRequestAttributes(request);
            }
            else
            {
                // Retrieve request attributes attached to the session object.
                retrieveRequestAttributes(request);
            }
            
            if (redirectString == null)
            {
                //==================================
                // Process request
                //==================================
                handler.doProcessing(request,response);

                pageRequestID = (String) request.getAttribute(RequestID.FORWARD_TOKEN);

                if (pageRequestID != null)
                {
                    if (BasicFilter.getDepth(request) == 0)
                    {
                        request.removeAttribute(RequestID.FORWARD_TOKEN);
                        request.setAttribute(RequestID.REQUEST_TOKEN,pageRequestID);
                    }
                    else
                    {
                        Stack filterCallStack = new Stack();
                        request.setAttribute(RequestID.FILTER_CALL_STACK,filterCallStack);
                        filterCallStack.push(this);
                        pageRequestID = null;
                    }
                }
            }
            else
            {
                // Redirect the page to the desired URL
                String encodedURL = response.encodeRedirectURL(redirectString);
                response.sendRedirect(encodedURL);
            }            
        }
    }


    /**
     * Builds the URL to which we will redirect the user request.
     *
     * @param request DOCUMENT ME!
     * @param desiredScheme DOCUMENT ME!
     * @param desiredPort DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */
    private String buildNewUrlString( HttpServletRequest request,
                                      String desiredScheme,
                                      int desiredPort )
    {
        String requestURL = request.getRequestURL().toString();
        StringBuffer newURL = new StringBuffer(128);

        newURL.append(desiredScheme);
        
        int beginHost = requestURL.indexOf(':');
        int beginPath = requestURL.indexOf('/',beginHost + 3);
        int endHost = requestURL.lastIndexOf(':',beginPath);
        
        if (beginHost == endHost)
        {
            // Port specification is NOT included in request URL.
            newURL.append(requestURL.substring(beginHost,beginPath));
        }
        else
        {
            // Port specification IS included in request URL.
            newURL.append(requestURL.substring(beginHost,endHost));
        }
        
        if ((desiredScheme.equals(HTTP) && (desiredPort != STD_HTTP_PORT)) ||
            (desiredScheme.equals(HTTPS) && (desiredPort != STD_HTTPS_PORT)))
        {
            newURL.append(":" + String.valueOf(desiredPort));
        }
        
        newURL.append(requestURL.substring(beginPath));

        // add query string, if any
        String queryString = request.getQueryString();

        if ((queryString != null) && (queryString.length() != 0))
        {
            newURL.append("?" + queryString);
        }
        else
        {
            Map paramMap = request.getParameterMap();
            
            if (paramMap.size() > 0)
            {
                queryString = createQueryStringFromParamMap(paramMap);
                newURL.append("?" + queryString);
            }
        }

        return newURL.toString();
    }

    /*******************************************************************
     * Builds a query string from a given map of request parameters.
     *
     * @param paramMap A map of request parameters.
     *
     * @return A string suitable for use as a query string.
     *******************************************************************/
    private String createQueryStringFromParamMap(Map paramMap)
    {
        StringBuffer buffer = new StringBuffer();
        Set keySet = paramMap.keySet();
        Iterator keysIterator = keySet.iterator();

        while (keysIterator.hasNext())
        {
            Object objParamKey = keysIterator.next();
            Object objParamValue = paramMap.get(objParamKey);
            
            String strParamValue = null;;
            
            if (objParamValue == null)
            {
                strParamValue = "";
            }
            else if (objParamValue instanceof String)
            {
                strParamValue = (String) objParamValue;
            }
            else if (objParamValue instanceof String[])
            {
                String strValues[] = (String[]) objParamValue;
                
                for (int i = 0; i < strValues.length; i++)
                {
                    if (buffer.length() > 0)
                    {
                        buffer.append("&");
                    }
                    buffer.append(objParamKey.toString());
                    buffer.append("=");
                    buffer.append(strValues[i]);
                }
            }
            else
            {
                strParamValue = objParamValue.toString();
            }
            
            if (strParamValue != null)
            {
                if (buffer.length() > 0)
                {
                    buffer.append("&");
                }
                buffer.append(objParamKey.toString());
                buffer.append("=");
                buffer.append(strParamValue);
            }
        }

        return buffer.toString();
    }

    private boolean storeRequestAttributes( HttpServletRequest request )
    {
        HttpSession session = request.getSession(false);

        String attrName = RequestID.REQ_OBJ_ATTRS_MAP;
        
        if (session.getAttribute(attrName) == null)
        {
            Enumeration enumRequestAttrs = request.getAttributeNames();
            HashMap mapRequestAttrs = new HashMap();

            while (enumRequestAttrs.hasMoreElements())
            {
                String name = (String) enumRequestAttrs.nextElement();
                mapRequestAttrs.put(name, request.getAttribute(name));
            }

            session.setAttribute(attrName,mapRequestAttrs);
            
            return true;  // Action prformed
        }
        
        return false;  // No action performed
    }

    private boolean retrieveRequestAttributes( HttpServletRequest request )
    {
        HttpSession session = request.getSession(false);

        String attrName = RequestID.REQ_OBJ_ATTRS_MAP;

        HashMap mapRequestAttrs = (HashMap) session.getAttribute(attrName);

        if (mapRequestAttrs != null)
        {
            Iterator keysIterator = mapRequestAttrs.keySet().iterator();

            while (keysIterator.hasNext())
            {
                String name = (String) keysIterator.next();
                request.setAttribute(name, mapRequestAttrs.get(name));
            }

            session.removeAttribute(attrName);
            
            return true;  // Action prformed
        }
        
        return false;  // No action performed
    }
}
