/*
  Copyright (C) 2007 Helge Hess

  This file is part of JOPE.

  JOPE is free software; you can redistribute it and/or modify it under
  the terms of the GNU Lesser General Public License as published by the
  Free Software Foundation; either version 2, or (at your option) any
  later version.

  JOPE 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 GNU Lesser General Public
  License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with JOPE; see the file COPYING.  If not, write to the
  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
  02111-1307, USA.
*/
package org.opengroupware.jope.appserver.publisher;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.opengroupware.jope.appserver.core.WOActionResults;
import org.opengroupware.jope.appserver.core.WOApplication;
import org.opengroupware.jope.appserver.core.WOComponent;
import org.opengroupware.jope.appserver.core.WOContext;
import org.opengroupware.jope.appserver.core.WOElement;
import org.opengroupware.jope.appserver.core.WOMessage;
import org.opengroupware.jope.appserver.core.WOResponse;
import org.opengroupware.jope.foundation.NSException;
import org.opengroupware.jope.foundation.NSJavaRuntime;
import org.opengroupware.jope.foundation.NSObject;

/*
 * JoDefaultRenderer
 * 
 * This is the default renderer which can deal with all the regular call
 * results. That is WOResponse, WOComponent, String, etc.
 */
public class JoDefaultRenderer extends NSObject implements JoObjectRenderer {
  protected static final Log log = LogFactory.getLog("JoDefaultRenderer");
  
  public static final JoDefaultRenderer sharedRenderer =
    new JoDefaultRenderer();

  /* control rendering */
  
  public boolean canRenderObjectInContext(Object _object, WOContext _ctx) {
    if (_object instanceof WOComponent     ||
        _object instanceof WOResponse      ||
        _object instanceof WOActionResults ||
        _object instanceof WOElement       ||
        _object instanceof String          ||
        _object instanceof Exception       ||
        _object instanceof WOApplication)
      return true;

    return false;
  }
  
  /* rendering */

  public Exception renderObjectInContext(Object _object, WOContext _ctx) {
    if (_object instanceof WOComponent)
      return this.renderComponent((WOComponent)_object, _ctx);
    
    if (_object instanceof WOResponse)
      return this.renderResponse((WOResponse)_object, _ctx);
    
    if (_object instanceof WOActionResults)
      return this.renderActionResults((WOActionResults)_object, _ctx);
    
    if (_object instanceof Exception)
      return this.renderException((Exception)_object, _ctx);
    
    if (_object instanceof WOElement)
      return this.renderElement((WOElement)_object, _ctx);
    
    if (_object instanceof String)
      return this.renderString(_object, _ctx);
    
    
    /* standard fallback for WOApplication */
    
    if (_object instanceof WOApplication) {
      /* This is if someone enters the root URL, per default we either redirect
       * to the DirectAction or to the Main page.
       */
      WOResponse r = ((WOApplication)_object).redirectToApplicationEntry(_ctx);
      return this.renderResponse(r, _ctx);
    }
    
    /* failed rendering, should not happen with proper call to canRender.. */
    
    log.error("cannot render object: " + _object);
    return new JoInternalErrorException("cannot render given object");
  }
  
  /* specific renderers */
  
  public Exception renderResponse(WOResponse _response, WOContext _ctx) {
    if (_response == null)
      return new JoInternalErrorException("got no response to render");
    
    if (_ctx.response() == _response) /* response is already active */
      return null; /* everything OK */
    
    // TODO
    log.error("custom WOResponse'es not yet supported: " + _response);
    return new NSException("unimplemented: cannot render response");
  }

  public Exception renderException(Exception _exception, WOContext _ctx) {
    if (_exception == null)
      return new JoInternalErrorException("got no exception to render");
    
    /* determine status */
    
    int httpStatus = NSJavaRuntime.intValueForKey(_exception, "httpStatus");
    if (httpStatus < 100 || httpStatus > 999) {
      if (httpStatus != 0) {
        log.warn("got invalid httpStatus " + httpStatus + " for exception: " +
                 _exception);
      }
      httpStatus = WOMessage.HTTP_STATUS_INTERNAL_ERROR;
    }
    if (httpStatus == 500)
      log.warn("delivering internal error exception", _exception);
    
    /* render exception */
    
    WOResponse r = _ctx.response();
    r.setStatus(httpStatus);
    
    // TODO: we should check the request 'accept' header and then decide
    r.setHeaderForKey("text/html", "content-type");
    
    // TODO: we might want to render more
    
    r.appendContentHTMLString
      ("Exception: " + _exception.getClass().getCanonicalName());
    r.appendContentString("<br />");
    
    r.appendContentHTMLString("Cause: " + _exception.getCause());
    r.appendContentString("<br />");
    
    r.appendContentHTMLString("Message: " + _exception.getMessage());
    r.appendContentString("<br />");
    
    return null /* everything is great */;
  }

  public Exception renderComponent(WOComponent _page, WOContext _ctx) {
    /* reuse context response for WOComponent */
    WOResponse r = _ctx.response();
    if (_page == null)
      return new JoInternalErrorException("got no page to render");
    
    if (log.isDebugEnabled()) log.debug("delivering page: " + _page);
    
    _ctx.setPage(_page);
    _page.ensureAwakeInContext(_ctx);
    _ctx.enterComponent(_page, null /* component-content */);
    _page.appendToResponse(r, _ctx);
    _ctx.leaveComponent(_page);
    
    return null /* everything OK */;
  }
  
  public Exception renderElement(WOElement _e, WOContext _ctx) {
    if (_e == null)
      return new JoInternalErrorException("got no element to render");
    
    WOResponse r = _ctx.response();
    _e.appendToResponse(r, _ctx);
    return null /* everything OK */;
  }
  
  public Exception renderActionResults(WOActionResults _r, WOContext _ctx) {
    if (_r == null)
      return new JoInternalErrorException("got no actionresults to render");
    
    WOResponse r = _r.generateResponse();
    if (r == null) {
      return new JoInternalErrorException
      ("got no actionresults response to render");
    }

    return this.renderResponse(r, _ctx);
  }
  
  public Exception renderString(Object _o, WOContext _ctx) {
    String s = _o.toString();
    if (s == null)
      return new JoInternalErrorException("got no string to render");

    WOResponse r = _ctx.response();
    r.setStatus(WOMessage.HTTP_STATUS_OK);
    // TODO: we should check the request 'accept' header and then decide
    r.setHeaderForKey("text/html", "content-type");
    r.appendContentHTMLString(s);
    
    return null; /* everything is awesome O */
  }
}
