/*
  Copyright (C) 2006 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.elements;

import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.opengroupware.jope.appserver.core.WOAssociation;
import org.opengroupware.jope.appserver.core.WOContext;
import org.opengroupware.jope.appserver.core.WODynamicElement;
import org.opengroupware.jope.appserver.core.WOElement;
import org.opengroupware.jope.appserver.core.WOElementWalker;
import org.opengroupware.jope.appserver.core.WORequest;
import org.opengroupware.jope.appserver.core.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);
    }
  }

  @Override
  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 */
  
  @Override
  public Object 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);
    }
  }
  
  @Override
  public void appendToResponse(WOResponse _r, WOContext _ctx) {
    WOListWalkerOperation op = 
      (this.separator != null && !_ctx.isRenderingDisabled())
      ? new AppendSepOperation(this.template, this.separator, _r)
      : new AppendOperation(this.template, _r);
      
    this.walker.walkList(op, _ctx);
  }
  
  /* walker */
  
  protected static class GenWalkOperation implements WOListWalkerOperation {
    protected WOElement       element;
    protected WOElement       template;
    protected WOElementWalker walker;
    
    public GenWalkOperation
      (WOElement _rep, WOElement _template, WOElementWalker _walker)
    {
      this.element  = _rep;
      this.template = _template;
      this.walker   = _walker;
    }
    
    public void processItem(int _idx, Object _item, WOContext _ctx) {
      this.walker.processTemplate(this.element, this.template, _ctx);
    }
  }
  
  @Override
  public void walkTemplate(WOElementWalker _walker, WOContext _ctx) {
    if (this.template == null) /* nothing to walk over */
      return;
    
    WOListWalkerOperation op =
      new GenWalkOperation(this, this.template, _walker);
    
    this.walker.walkList(op, _ctx);
  }
}
