/*
 * Copyright (c) 1998-2012 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source 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, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *   Free SoftwareFoundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.server.webapp;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;

import javax.servlet.FilterChain;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestWrapper;
import javax.servlet.ServletResponse;
import javax.servlet.ServletResponseWrapper;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.caucho.server.dispatch.Invocation;
import com.caucho.server.http.CauchoRequest;
import com.caucho.server.http.CauchoResponse;
import com.caucho.server.http.RequestAdapter;
import com.caucho.util.L10N;

class NamedDispatcherImpl implements RequestDispatcher {

  private static final L10N L = new L10N(NamedDispatcherImpl.class);

  private WebApp _webApp;
  
  private FilterChain _includeFilterChain;  
  private FilterChain _forwardFilterChain;  
  
  private String _queryString;

  private Invocation _forwardInvocation;

  NamedDispatcherImpl(FilterChain includeFilterChain,
                      FilterChain forwardFilterChain,
                      Invocation invocation,
                      String queryString, 
                      WebApp webApp)
  {
    _includeFilterChain = includeFilterChain;
    _forwardFilterChain = forwardFilterChain;
    
    _forwardInvocation = invocation;

    _queryString = queryString;
    _webApp = webApp;
  }

  public void include(ServletRequest topRequest, ServletResponse topResponse)
    throws IOException, ServletException
  {
    HttpServletRequest parentReq;
    ServletRequestWrapper reqWrapper = null;

    if (topRequest instanceof ServletRequestWrapper) {
      ServletRequest request = topRequest;

      while (request instanceof ServletRequestWrapper) {
        reqWrapper = (ServletRequestWrapper) request;

        request = ((ServletRequestWrapper) request).getRequest();
      }

      parentReq = (HttpServletRequest) request;
    } else if (topRequest instanceof HttpServletRequest) {
      parentReq = (HttpServletRequest) topRequest;
    } else {
      throw new IllegalStateException(L.l(
        "expected instance of ServletRequestWrapper at `{0}'", topResponse));
    }

    HttpServletResponse parentRes;
    ServletResponseWrapper resWrapper = null;

    if (topResponse instanceof ServletResponseWrapper) {
      ServletResponse response = topResponse;

      while (response instanceof ServletResponseWrapper) {
        resWrapper = (ServletResponseWrapper) response;

        response = ((ServletResponseWrapper) response).getResponse();
      }

      parentRes = (HttpServletResponse) response;
    } else if (topResponse instanceof HttpServletResponse) {
      parentRes = (HttpServletResponse) topResponse;
    } else {
      throw new IllegalStateException(L.l(
        "expected instance of ServletResponse at `{0}'", topResponse));
    }

    RequestAdapter subRequest = RequestAdapter.create();
    subRequest.init(parentReq, parentRes, _webApp);

    DispatchResponse subResponse = new DispatchResponse();
    subResponse.init(parentRes);

    if (reqWrapper != null) {
      reqWrapper.setRequest(subRequest);
    } else {
      topRequest = subRequest;
    }

    if (resWrapper != null) {
      resWrapper.setResponse(subResponse);
    } else {
      topResponse = subResponse;
    }

    try {
      _includeFilterChain.doFilter(topRequest, topResponse);
    } finally {
      subResponse.finish();
      RequestAdapter.free(subRequest);

      if (reqWrapper != null)
        reqWrapper.setRequest(parentReq);

      if (resWrapper != null)
        resWrapper.setResponse(parentRes);
    }

    //_includeFilterChain.doFilter(req, new DispatchResponse(res));

    //AbstractResponseStream s = res.getResponseStream();
    // s.setDisableClose(true);

    /* XXX:
    DispatchResponse subResponse = DispatchResponse.createDispatch();
    // XXX: subResponse.init(req);
    subResponse.setNextResponse(res);
    // subResponse.init(req, s);
    subResponse.startRequest(null);
    
    try {
      _includeFilterChain.doFilter(req, subResponse);
    } finally {
      subResponse.finishInvocation();
      subResponse.finishRequest();
    }

    if (reqAdapt != null)
      RequestAdapter.free(reqAdapt);

    DispatchResponse.free(subResponse);
    */
  }

  /**
   * Forward the request to the named servlet.
   */
  @Override
  public void forward(ServletRequest req, ServletResponse res)
    throws ServletException, IOException
  {
    res.resetBuffer();
    
    res.setContentLength(-1);
    
    // server/1653
    CauchoRequest subRequest = new ForwardNamedRequest((HttpServletRequest) req,
                                                   (HttpServletResponse) res,
                                                   _forwardInvocation);

    _forwardFilterChain.doFilter(subRequest, res);

    // this is not in a finally block so we can return a real error message
    // if it's not handled.
    // server/1328, server/125i
    if (res instanceof CauchoResponse) {
      CauchoResponse cRes = (CauchoResponse) res;
      
      cRes.close();
    }
    else {
        try {
          OutputStream os = res.getOutputStream();
          if (os != null)
            os.close();
        } catch (IllegalStateException e) {
        }

        try {
          PrintWriter out = res.getWriter();
          if (out != null)
            out.close();
        } catch (IllegalStateException e1) {
        }

    }

    /*
    ServletResponse ptr = res;
    while (ptr instanceof HttpServletResponseWrapper) {
      ptr = ((HttpServletResponseWrapper) ptr).getResponse();

      if (ptr instanceof AbstractHttpResponse)
        ((AbstractHttpResponse) ptr).finish();
    }
    */

    /*
    if (res instanceof AbstractHttpResponse)
      ((AbstractHttpResponse) res).finish(true);

    if (res instanceof Response)
      ((Response) res).finish(true);
    */
  }

  public String toString()
  {
    return getClass().getSimpleName() + "[" + _includeFilterChain + "]";
  }
}

