/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.wicket.markup.repeater;

import java.util.Iterator;

import org.apache.wicket.markup.repeater.util.ModelIteratorAdapter;
import org.apache.wicket.model.IModel;
import org.apache.wicket.version.undo.Change;


/**
 * An abstract repeater view that provides refreshing functionality to its subclasses. The view is
 * refreshed every request, making it well suited for displaying dynamic data.
 * <p>
 * The view is populated by implementing {@link RefreshingView#getItemModels() } and
 * {@link RefreshingView#populateItem(Item) } methods. RefreshingView builds the items that will be
 * rendered by looping over the models retrieved from {@link RefreshingView#getItemModels() } and
 * calling the {@link RefreshingView#newItem(String, int, IModel) } to generate the child item
 * container followed by a call to {@link RefreshingView#populateItem(Item) } to let the user
 * populate the newly created item container with custom components.
 * </p>
 * <p>
 * The provided {@link ModelIteratorAdapter} can make implementing
 * {@link RefreshingView#getItemModels() } easier if you have an iterator over item objects.
 * </p>
 * 
 * @see RepeatingView
 * @see ModelIteratorAdapter
 * 
 * @author Igor Vaynberg (ivaynberg)
 * 
 */
public abstract class RefreshingView extends RepeatingView
{
	private static final long serialVersionUID = 1L;

	/**
	 * The item reuse strategy that will be used to recycle items when the page is changed or the
	 * view is redrawn.
	 * 
	 * @see IItemReuseStrategy
	 */
	private IItemReuseStrategy itemReuseStrategy;

	/**
	 * Constructor
	 * 
	 * @param id
	 *            component id
	 */
	public RefreshingView(String id)
	{
		super(id);
	}

	/**
	 * Constructor
	 * 
	 * @param id
	 *            component id
	 * @param model
	 *            model
	 */
	public RefreshingView(String id, IModel model)
	{
		super(id, model);
	}

	/**
	 * Refresh the items in the view. Delegates the creation of items to the selected item reuse
	 * strategy
	 */
	protected final void onPopulate()
	{

		IItemFactory itemFactory = new IItemFactory()
		{

			public Item newItem(int index, IModel model)
			{
				String id = RefreshingView.this.newChildId();
				Item item = RefreshingView.this.newItem(id, index, model);
				RefreshingView.this.populateItem(item);
				return item;
			}

		};

		Iterator models = getItemModels();
		Iterator items = getItemReuseStrategy().getItems(itemFactory, models, getItems());
		removeAll();
		addItems(items);
	}

	/**
	 * Returns an iterator over models for items that will be added to this view
	 * 
	 * @return an iterator over models for items that will be added to this view
	 */
	protected abstract Iterator getItemModels();

	/**
	 * Populate the given Item container.
	 * <p>
	 * <b>be carefull</b> to add any components to the item and not the view itself. So, don't do:
	 * 
	 * <pre>
	 * add(new Label(&quot;foo&quot;, &quot;bar&quot;));
	 * </pre>
	 * 
	 * but:
	 * 
	 * <pre>
	 * item.add(new Label(&quot;foo&quot;, &quot;bar&quot;));
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param item
	 *            The item to populate
	 */
	protected abstract void populateItem(final Item item);

	/**
	 * Factory method for Item container. Item containers are simple MarkupContainer used to
	 * aggregate the user added components for a row inside the view.
	 * 
	 * @see Item
	 * @param id
	 *            component id for the new data item
	 * @param index
	 *            the index of the new data item
	 * @param model
	 *            the model for the new data item
	 * 
	 * @return DataItem created DataItem
	 */
	protected Item newItem(final String id, int index, final IModel model)
	{
		return new Item(id, index, model);
	}

	/**
	 * @return iterator over item instances that exist as children of this view
	 */
	public Iterator getItems()
	{
		return iterator();
	}

	/**
	 * Add items to the view. Prior to this all items were removed so every request this function
	 * starts from a clean slate.
	 * 
	 * @param items
	 *            item instances to be added to this view
	 */
	protected void addItems(Iterator items)
	{
		while (items.hasNext())
		{
			add((Item)items.next());
		}
	}

	// /////////////////////////////////////////////////////////////////////////
	// ITEM GENERATION
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * @return currently set item reuse strategy. Defaults to <code>DefaultItemReuseStrategy</code>
	 *         if none was set.
	 * 
	 * @see DefaultItemReuseStrategy
	 */
	public IItemReuseStrategy getItemReuseStrategy()
	{
		if (itemReuseStrategy == null)
		{
			return DefaultItemReuseStrategy.getInstance();
		}
		return itemReuseStrategy;
	}

	/**
	 * Sets the item reuse strategy. This strategy controls the creation of {@link Item}s.
	 * 
	 * @see IItemReuseStrategy
	 * 
	 * @param strategy
	 *            item reuse strategy
	 * @return this for chaining
	 */
	public RefreshingView setItemReuseStrategy(IItemReuseStrategy strategy)
	{
		if (strategy == null)
		{
			throw new IllegalArgumentException();
		}

		if (!strategy.equals(itemReuseStrategy))
		{
			if (isVersioned())
			{
				addStateChange(new Change()
				{
					private static final long serialVersionUID = 1L;

					private final IItemReuseStrategy old = itemReuseStrategy;

					public void undo()
					{
						itemReuseStrategy = old;
					}

					public String toString()
					{
						return "ItemsReuseStrategyChange[component: " + getPath() + ", reuse: " +
								old + "]";
					}
				});
			}
			itemReuseStrategy = strategy;
		}
		return this;
	}


}

@@ -4894,16 +4894,33 @@
 ems)%0A%09%7B%0A
+%09%09int index = 0;%0A
 %09%09while 
@@ -4944,20 +4944,28 @@
 %0A%09%09%7B%0A%09%09%09
-add(
+Item item = 
 (Item)it
@@ -4974,17 +4974,67 @@
 s.next()
-)
+;%0A%09%09%09item.setIndex(index);%0A%09%09%09add(item);%0A%09%09%09++index
 ;%0A%09%09%7D%0A%09%7D
@@ -6352,17 +6352,16 @@
 se: %22 +%0A
-%09
 %09%09%09%09%09%09%09o

update indexes for preserved items