package org.opengroupware.jope.appserver;

import static org.opengroupware.jope.foundation.NSJavaRuntime.NSAllocateObject;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class WODirectActionRequestHandler extends WORequestHandler {

  protected final Log daLog = LogFactory.getLog("WODirectActions");
  
  public WODirectActionRequestHandler(WOApplication _app) {
    super(_app);
  }
  
  /* Zope style :action form values */
  
  public String formActionFromRequest(WORequest _rq) {
    // TODO: Zope can also do other form value conversions, maybe we want to
    //       support them as well. (see Zope DevGuide, Object Publishing)
    if (_rq == null) return null;
    
    for (String formValueKey: _rq.formValueKeys()) {
      int l = formValueKey.length();
      if (l < 7) continue;
      
      if (l >= 7 && formValueKey.endsWith(":action")) {
        return l == 7
          ? _rq.stringFormValueForKey(formValueKey)
          : formValueKey.substring(0, l - 7);
      }
      
      /* Note: image submits have no values (only coordinates) */
      if (l > 9 && formValueKey.endsWith(":action.x"))
        return formValueKey.substring(0, l - 9);
    }
    return null;
  }
  
  /* request handling */

  public WOResponse handleRequest(WORequest _rq, WOContext _ctx, WOSession _s) {
    boolean isDebugOn = this.daLog.isDebugEnabled();
    WOResponse response      = null;
    String   actionClassName = null;
    String   actionName;
    String[] handlerPath;
    
    /* first we scan form parameters for a ":action" form value */
    
    if ((actionName = this.formActionFromRequest(_rq)) != null) {
      if (isDebugOn)
        this.daLog.debug("located action in form values: " + actionName);
      
      int idx = actionName.indexOf('/');
      if (idx != -1) {
        actionClassName = actionName.substring(0, idx);
        actionName      = actionName.substring(idx + 1);
      }
    }
    
    /* decode URL */
    
    handlerPath = _rq.requestHandlerPathArray();
    switch (handlerPath.length) {
      case 0: {
        if (actionClassName == null) actionClassName = "DirectAction";
        if (actionName      == null) actionName      = "default";
        if (isDebugOn)
          this.daLog.debug("no DA path, using DirectAction/default");
        break;
      }
      case 1: {
        if (actionName != null) {
          /* form name overrides path values */
          if (actionClassName == null)
            actionClassName = handlerPath[0];
          if (isDebugOn)
            this.daLog.debug("single form path, action:" + actionName);
        }
        else {
          actionClassName = "DirectAction";
          actionName      = handlerPath[0];
          if (isDebugOn)
            this.daLog.debug("simple path, using DirectAction/" + actionName);
        }
        break;
      }
      default: {
        if (actionClassName == null) actionClassName = handlerPath[0];
        if (actionName      == null) actionName      = handlerPath[1];
        if (isDebugOn)
          this.daLog.debug("full path: " + actionClassName + "/" + actionName);
        break;
      }
    }
    
    if (actionClassName.length() == 0) actionClassName = "DirectAction";
    if (actionName.length()      == 0) actionName      = "default";
    
    int idx;
    
    /* discard everything after a point, to allow for better download URLs */
    if ((idx = actionName.indexOf('.')) != -1)
      actionName = actionName.substring(0, idx);
 
    /* find direct action class */
    
    WOResourceManager rm = _ctx.application().resourceManager();
    Class daClass = rm.lookupClass(actionClassName);
    
    if (daClass == null) {
      this.daLog.error("did not find action class: "+ actionClassName);
      return null;
    }
    else if (isDebugOn)
      this.daLog.debug("using DA class: " + daClass);
    
    /* setup fragment rendering mode */
    
    if (_rq.isFragmentIDInRequest()) {
      /* for fragments, we initially disable rendering of elements */
      _ctx.disableRendering();
    }
    
    /* instantiate object and call it */
    
    WOActionResults results;
    if (WOComponent.class.isAssignableFrom(daClass)) {
      WOComponent page;
      
      page = rm.pageWithName(actionClassName, _ctx);
      if (page == null) {
        this.daLog.error("could not instantiate page: "+ actionClassName);
        return null;
      }
      
      _ctx.setPage(page);
      page.ensureAwakeInContext(_ctx);
      _ctx.enterComponent(page, null);
      {
        if (page.shouldTakeValuesFromRequest(_rq, _ctx))
          page.takeValuesFromRequest(_rq, _ctx);
        
        // TODO: can we move this to invokeAction() somehow?
        results = page.performActionNamed(actionName);
      }
      _ctx.leaveComponent(page);
      _ctx.setPage(null);
      
      if (results == null)
        results = page;
    }
    else {
      WOAction da = (WOAction)NSAllocateObject(daClass, WOContext.class, _ctx);
      if (da == null) {
        this.daLog.error("could not instantiate action: "+ actionClassName);
        return null;
      }

      results = da.performActionNamed(actionName);
    }
    
    /* process results */
    
    if (results instanceof WOComponent) {
      /* reuse context response for WOComponent */
      WOComponent page = (WOComponent)results;
      
      if (isDebugOn) this.daLog.debug("delivering page: " + page);
      
      _ctx.setPage(page);
      page.ensureAwakeInContext(_ctx);
      _ctx.enterComponent(page, null);
      {
        response = _ctx.response();
        page.appendToResponse(response, _ctx);
      }
      _ctx.leaveComponent(page);
    }
    else if (results != null) {
      if (isDebugOn) this.daLog.debug("delivering results: " + results);
      response = results.generateResponse();
    }
    
    /* tear down */
    // TODO: tear down, sleep components etc
    
    return response;
  }
}
