/*
 *  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 org.apache.myfaces.trinidadinternal.renderkit.core.pages;

import java.io.IOException;

import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;

import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;

import org.apache.myfaces.trinidad.component.core.CoreImportScript;
import org.apache.myfaces.trinidad.component.core.output.CoreOutputText;
import org.apache.myfaces.trinidad.component.html.HtmlHtml;
import org.apache.myfaces.trinidad.component.html.HtmlFrame;
import org.apache.myfaces.trinidad.component.html.HtmlFrameBorderLayout;
import org.apache.myfaces.trinidad.component.html.HtmlScript;

import org.apache.myfaces.trinidadinternal.context.PageFlowScopeProviderImpl;
import org.apache.myfaces.trinidadinternal.renderkit.core.CoreRenderKit;
import org.apache.myfaces.trinidadinternal.share.url.EncoderUtils;

import org.apache.myfaces.trinidad.context.RenderingContext;

/**
 * Entry point for the "fred" JSP.
 * <p>
 * Parameters:
 * <ul>
 * <li>redirect: the full path to an external JSP
 * <li>_red: the name of an internal JSP to redirect to
 * <li>enc: the HTML character set encoding to use
 * <li>configName: the name of a configuration object
 * <li>contextURI: the context URI
 * </ul>
 * <p>
 * @version $Name:  $ ($Revision: adfrt/faces/adf-faces-impl/src/main/java/oracle/adfinternal/view/faces/renderkit/core/pages/FredJSP.java#0 $) $Date: 10-nov-2005.19:03:34 $
 */
public class FredJSP
{
  /**
   * Create an URL that will point to the frame redirect page.
   * @param context the FacesContext
   * @param viewRoot the UIViewRoot that will subsequently displayed.
   *    The viewRoot itself is not saved, but values like the locale
   *    and viewId are saved.
   * @param savedPageFlowScope the process scope to use for the dialog
   * @param minWidth the minimum width for the target page.
   * @param minHeight the minimum height for the target page.
   */
  static public String getRedirectURL(FacesContext context,
                                      UIViewRoot   viewRoot,
                                      String       minWidth,
                                      String       minHeight)
  {
    String baseURL = GenericEntry.getGenericEntryURL(
                         context,
                         GenericEntry.NEW_FRAME_REDIRECT_ENTRY);

    String[] args = new String[]
    {
      _VIEW_ID_REDIRECT_PARAM,
      viewRoot.getViewId(),
      "loc",
      viewRoot.getLocale().toString().replace('_', '-'),
      _MIN_WIDTH_PARAM,
      minWidth,
      _MIN_HEIGHT_PARAM,
      minHeight
    };


    return EncoderUtils.appendURLArguments(baseURL, args);
  }


  @SuppressWarnings("unchecked")
  static void service(FacesContext context) throws IOException
  {
    RenderingContext arc = RenderingContext.getCurrentInstance();

    // Don't use HtmlHead or CoreDocument;  these add a stylesheet link,
    // which we don't need or want
    HtmlHtml root = new HtmlHtml();
    context.getViewRoot().getChildren().add(root);

    Map<String, String> requestParameters = 
      context.getExternalContext().getRequestParameterMap();
    
    // Save the return ID - and do so before generating the
    // link to the frames!
    String returnId = requestParameters.get(_RETURN_ID_PARAM);
    if (returnId != null)
      CoreRenderKit.saveDialogPostbackValues(returnId);

    CoreOutputText headStart = new CoreOutputText();
    root.getChildren().add(headStart);
    headStart.setEscape(false);
    headStart.setValue("<head>");

    CoreImportScript cis = new CoreImportScript();
    cis.setNames(new String[]{"Core"});
    root.getChildren().add(cis);

    // The block-reload script only happens to work on IE, but may someday
    // work on Mozilla as well.
    HtmlScript script = new HtmlScript();
    script.setText(_BLOCK_RELOAD_TEXT + _FIX_DIALOG_TITLE);
    root.getChildren().add(script);

    CoreOutputText headEnd = new CoreOutputText();
    root.getChildren().add(headEnd);
    headEnd.setEscape(false);
    headEnd.setValue("</head>");

    HtmlFrame contentFrame = new HtmlFrame();
    String contentStr = arc.getTranslatedString(_FRAME_CONTENT);
    if (contentStr == null)
      contentStr = _DEFAULT_CONTENT_STRING;

    contentFrame.setShortDesc(contentStr); // for accessibility
    contentFrame.setLongDescURL("#");     // for accessibility
    contentFrame.setHeight("100%");
    contentFrame.setWidth("100%");

    // Get the query string.
    // trim out any "_t" parameter, which was only used to get here.
    // trim out any sizing parameters
    // trim out any redirect parameters
    String queryString = _getQueryString(
       context.getExternalContext().getRequestParameterValuesMap());

    // grab any sizing parameters
    String widthParam = requestParameters.get(_MIN_WIDTH_PARAM);
    boolean gotWidth = (widthParam != null);

    String heightParam = requestParameters.get(_MIN_HEIGHT_PARAM);
    boolean gotHeight = (heightParam != null);

    String viewIdRedirect = requestParameters.get(_VIEW_ID_REDIRECT_PARAM);
    if (viewIdRedirect != null)
    {
      ViewHandler vh =
        context.getApplication().getViewHandler();
      // Prepend an extra slash to avoid re-prepending the context path
      String redirectString = "/" + vh.getActionURL(context,
                                                    viewIdRedirect);

      // if redirectString contains ?, append queryString with &,
      // otherwise append queryString with &
      char sep = (redirectString.indexOf('?') != -1) ? '&' : '?';
      contentFrame.setSource(redirectString + sep + queryString);
    }
    else
    {
      String internalRedirect = requestParameters.get("_red");
      if (internalRedirect != null)
      {
        String path = GenericEntry.getGenericEntryURL(context,
                                                      internalRedirect);
        // Prepend an extra slash to avoid re-prepending the context path
        contentFrame.setSource("/" + path + "&" + queryString);
      }
    }

    HtmlFrameBorderLayout frameSet = new HtmlFrameBorderLayout();
    frameSet.setShortDesc(contentStr); // for accessibility
    frameSet.setCenter(contentFrame);
    // this border attribute is a "secret" attribute set to fix
    // 4339153 DIALOGS IN FIREFOX HAVE WHITE LINE AT THE BOTTOM
    frameSet.getAttributes().put("border", Boolean.FALSE);
    // see bug 3198336 apss accessibility violations
    CoreOutputText alternateContent = new CoreOutputText();
    alternateContent.setValue(arc.getTranslatedString("NO_FRAMES_MESSAGE"));
    frameSet.setAlternateContent(alternateContent);

    // Set the title to the title of the content, and then shrink (or expand)
    // the window to fit the content.  The 25 pixel fudge factor is purely
    // a hack to handle calendarDialog, which regularly needs to grow
    // a line or two.  A better method that accounts for font heights
    // would be good...

    // Bug #2464627: Allow support for forcing a minimum size
    StringBuilder onload = new StringBuilder(_FRAMESET_ONLOAD_TEXT.length()
                                           // space for the param plus the 'W:'
                                           + (gotWidth
                                              ? widthParam.length() + 2
                                              : 0)
                                           // space for the param plus the 'H:'
                                           + (gotHeight
                                              ? heightParam.length() + 2
                                              : 0)
                                           // space for commas, brackets,
                                           // and closing paren
                                           + 5);

    onload.append(_FRAMESET_ONLOAD_TEXT);
    if (gotWidth || gotHeight)
    {
      // open the parameter object
      onload.append(",{");
      if (gotWidth)
      {
        // add in the width parameter
        onload.append("W:");
        onload.append(widthParam);
      }
      if (gotHeight)
      {
        // If something preceded this param, separate with a comma
        if (gotWidth)
          onload.append(",");
        onload.append("H:");
        onload.append(heightParam);
      }
      // close the parameter object
      onload.append("}");
    }
    // close the handler
    onload.append(")");

    frameSet.setOnload(onload.toString());
    
    // http://issues.apache.org/jira/browse/ADFFACES-191    
    // Following code was once in CoreRenderKit.launchDialog.

    if (returnId != null)
    {
      StringBuilder onunload = new StringBuilder(53 + returnId.length());
      onunload.append(_FRAMESET_ONUNLOAD_TEXT);
      onunload.append(";window.opener.setTimeout(");
      onunload.append("'ADFDialogReturn[").append(returnId).append("]();'");
      onunload.append(",1)");
      frameSet.setOnunload(onunload.toString());
    }
    else
    {
      frameSet.setOnunload(_FRAMESET_ONUNLOAD_TEXT);
    }

    root.getChildren().add(frameSet);
  }

  static private String _getQueryString(
    Map<String, String[]> parameters) throws IOException
  {
    // Bug #3419817 support request dispatch for Portal
    // build up the encoded query string from request parameters
    // this will work for both direct requests and include/forward requests
    StringBuffer buf = new StringBuffer();
    Iterator<String> paramNames = parameters.keySet().iterator();
    boolean isFirst = true;

    // iterate through the request parameter names
    while (paramNames.hasNext())
    {
      String paramName = paramNames.next();

      // add the parameter to the query string unless skipped
      if (!_SKIP_PARAMS.contains(paramName))
      {
        // the parameter might appear on the request more than once
        String[] paramValues = parameters.get(paramName);

        for (int i=0; i < paramValues.length; i++)
        {
          if (!isFirst)
            buf.append("&");

          String paramValue = paramValues[i];
          EncoderUtils.appendUIXQueryParameter(buf, paramName);
          buf.append("=");
          EncoderUtils.appendUIXQueryParameter(buf, paramValue);

          isFirst = false;
        }
      }
    }

    return buf.toString();
  }

  static private final String _VIEW_ID_REDIRECT_PARAM = "_vir";
  static private final String _MIN_WIDTH_PARAM = "_minWidth";
  static private final String _MIN_HEIGHT_PARAM = "_minHeight";
  static private final String _RETURN_ID_PARAM = "_rtrnId";

  static private final String _BLOCK_RELOAD_TEXT =
    "document.onkeydown=_noReload;var _blockReload=1;";
  static private final String _FIX_DIALOG_TITLE =
    "function _fixDialogTitle(){window.document.title=window.frames[0].document.title};";

  // NOTE: There is NO CLOSING PAREN here. This will have the closing paren
  // added to it in the service method above (we may or may not add a window
  // sizing parameter object as a last argument).
  static private final String _FRAMESET_ONLOAD_TEXT =
    "_fixDialogTitle(); _sizeWin(window.frames[0],0,30";
  static private final String _FRAMESET_ONUNLOAD_TEXT =
    "_checkUnload(event)";

  static private final Collection<String> _SKIP_PARAMS =
    Arrays.asList(new String[]
                  {
                    GenericEntry.__ENTRY_KEY_PARAM,
                    "redirect",
                    _MIN_WIDTH_PARAM,
                    _MIN_HEIGHT_PARAM,
                    "_red",
                    _VIEW_ID_REDIRECT_PARAM,
                    _RETURN_ID_PARAM,
                    PageFlowScopeProviderImpl.TOKEN_PARAMETER_NAME,
                  });

  static private final String _FRAME_CONTENT = "FRAME_CONTENT";
  static private final String _DEFAULT_CONTENT_STRING = "Content";
}
