package org.opengroupware.jope.appserver.elements;

import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.opengroupware.jope.appserver.WOActionResults;
import org.opengroupware.jope.appserver.WOAssociation;
import org.opengroupware.jope.appserver.WOContext;
import org.opengroupware.jope.appserver.WODynamicElement;
import org.opengroupware.jope.appserver.WOElement;
import org.opengroupware.jope.appserver.WORequest;
import org.opengroupware.jope.appserver.WOResponse;

/*
 * WORepetition
 * 
 * This iterate over a subsection multiple times based on the given List. During
 * that the item/index/etc bindings are updated with the current value from the
 * list. This way the containing elements can refer to the current item.
 * 
 * Sample:
 *   Countries: WORepetition {
 *     list = countries;
 *     item = country;
 *   }
 *   
 * Renders:
 *   This element doesn't render anything (well, a separator when available).
 *   
 * Bindings:
 *   list       [in]  - java.util.List | Collection | Java array | DOM Node
 *   count      [in]  - int
 *   item       [out] - object
 *   index      [out] - int
 *   startIndex [in]  - int
 *   identifier [in]  - string (TODO: currently unescaped)
 *   separator  [in]  - string
 *   sublist    [in]  - java.util.List | Collection | Java array | DOM Node
 *   
 * TODO: document 'list' binding behaviour
 * TODO: document 'sublist' for processing trees
 */
public class WORepetition extends WODynamicElement {

  protected Log log = LogFactory.getLog("WORepetition");

  protected WOAssociation separator;
  protected WOElement     template;
  protected WOListWalker  walker;
  
  public WORepetition(String _name, Map<String, WOAssociation> _assocs,
                      WOElement _template)
  {
    super(_name, _assocs, _template);
    
    this.walker    = new WOListWalker(_assocs);
    this.separator = grabAssociation(_assocs, "separator");
    this.template  = _template;
  }
  
  /* list processing */
  
  protected enum ListOperation {
    /* yes, we could pass in a Method, but this isn't necessarily faster .. */
    takeValuesFromRequest,
    appendToResponse
  };

  /* responder */

  protected static class TakeValueOperation implements WOListWalkerOperation {
    protected WOElement element;
    protected WORequest request;
    
    public TakeValueOperation(WOElement _element, WORequest _request) {
      this.element = _element;
      this.request = _request;
    }
    
    public void processItem(int _idx, Object _item, WOContext _ctx) {
      this.element.takeValuesFromRequest(this.request, _ctx);
    }
  }
  public void takeValuesFromRequest(WORequest _rq, WOContext _ctx) {
    if (this.template == null)
      /* no point in iterating the list if there is no taker ... */
      return;
    
    this.walker.walkList(new TakeValueOperation(this.template, _rq), _ctx);
  }
  
  /* handle component actions */
  
  public WOActionResults invokeAction(WORequest _rq, WOContext _ctx) {
    // TODO: implement me for WOComponentActions
    this.log.error("WORepetition not implemented for component actions ...");
    return null;
  }
  
  /* generate response */
  
  protected static class AppendOperation implements WOListWalkerOperation {
    protected WOElement  element;
    protected WOResponse response;
    
    public AppendOperation(WOElement _element, WOResponse _response) {
      this.element  = _element;
      this.response = _response;
    }
    
    public void processItem(int _idx, Object _item, WOContext _ctx) {
      if (this.element != null)
        this.element.appendToResponse(this.response, _ctx);
    }
  }
  protected static class AppendSepOperation extends AppendOperation {
    protected WOAssociation separator;
    
    public AppendSepOperation
      (WOElement _element, WOAssociation _sep, WOResponse _response)
    {
      super(_element, _response);
      this.separator = _sep;
    }
    
    public void processItem(int _idx, Object _item, WOContext _ctx) {
      if (_idx > 0) { /* Note: does not work with 'startIndex' */
        String s = this.separator.stringValueInComponent(_ctx.cursor());
        if (s != null) this.response.appendContentString(s);
      }
      
      super.processItem(_idx, _item, _ctx);
    }
  }
  
  public void appendToResponse(WOResponse _r, WOContext _ctx) {
    WOListWalkerOperation op = 
      this.separator != null
      ? new AppendSepOperation(this.template, this.separator, _r)
      : new AppendOperation(this.template, _r);
      
    this.walker.walkList(op, _ctx);
  }
  
}
