package wicket.quickstart.dataview;

import java.io.Serializable;
import java.util.List;

import wicket.markup.MarkupStream;
import wicket.markup.html.WebMarkupContainer;
import wicket.markup.html.list.ListItem;
import wicket.model.IModel;
import wicket.model.Model;

public abstract class DataView extends WebMarkupContainer
{
	private int firstIndex = 0;

	private int viewSize = Integer.MAX_VALUE;

	public DataView(final String id)
	{
		super(id);
	}

	public DataView(final String id, final IModel model)
	{
		super(id, model);

		if (model == null)
		{
			throw new IllegalArgumentException(
					"Null models are not allowed. If you have no model, you may prefer a Loop instead");
		}
	}

	public DataView(final String id, final DataProvider dp) {
		this(id, new Model((Serializable) dp));
	}

	public final DataProvider getDataProvider() {
		return (DataProvider)getModelObject();
	}

	public final int getStartIndex()
	{
		return this.firstIndex;
	}

	public int getViewSize()
	{
		int size = this.viewSize;

		final DataProvider dp = getDataProvider();

		final int modelSize = dp.getCount();
		if (firstIndex > modelSize)
		{
			return 0;
		}

		if ((size == Integer.MAX_VALUE) || ((firstIndex + size) > modelSize))
		{
			size = modelSize - firstIndex;
		}

		// firstIndex + size must be smaller than Integer.MAX_VALUE
		if ((Integer.MAX_VALUE - size) < firstIndex)
		{
			throw new IllegalStateException(
					"firstIndex + size must be smaller than Integer.MAX_VALUE");
		}

		return size;
	}


	public DataView setStartIndex(final int startIndex)
	{
		this.firstIndex = startIndex;

		if (firstIndex < 0)
		{
			firstIndex = 0;
		}
		else if (firstIndex > getDataProvider().getCount())
		{
			firstIndex = 0;
		}

		return this;
	}

	public DataView setViewSize(final int size)
	{
		this.viewSize = size;

		if (viewSize < 0)
		{
			viewSize = Integer.MAX_VALUE;
		}

		return this;
	}


	protected void internalOnBeginRequest()
	{
		removeAll();

		// Get number of items to be displayed
		final int size = getViewSize();

		if (size > 0)
		{
			DataProvider dp=getDataProvider();
			List data=dp.getElements(firstIndex, size);
			
			// Loop through the markup in this container for each item
			for (int i = 0; i < size; i++)
			{
				// Get index
				final int index = firstIndex + i;

				// Create item for index
				ListItem item = newItem(index, data.get(i));

				// Add list item
				add(item);

				// Populate the list item
				onBeginPopulateItem(item);
				populateItem(item);
			}
		}
	}

	protected ListItem newItem(final int index, final Object object)
	{
		return new ListItem(index, getDataProvider().getObjectModel(object));
	}

	protected void onBeginPopulateItem(final ListItem item)
	{
	}

	protected void onRender()
	{
		// Ask parents for markup stream to use
			final MarkupStream markupStream = findMarkupStream();

			// Save position in markup stream
			final int markupStart = markupStream.getCurrentIndex();

			// Get number of items to be displayed
			final int size = getViewSize();
			if (size > 0)
			{
				// Loop through the markup in this container for each item
				for (int i = 0; i < size; i++)
				{
					// Get index
					final int index = firstIndex + i;

					// If this component does not already exist, populate it
					ListItem item = (ListItem)get(Integer.toString(index));

					// Rewind to start of markup for kids
					markupStream.setCurrentIndex(markupStart);

					// Render
					renderItem(item);
				}
			}
			else
			{
				markupStream.skipComponent();
			}
		}

		/**
		 * Populate a given item.
		 * <p>
		 * <b>be carefull</b> to add any components to the list item. So, don't do:
		 * <pre>
		 *  add(new Label("foo", "bar"));
		 * </pre>
		 * but:
		 * <pre>
		 *  item.add(new Label("foo", "bar"));
		 * </pre>
		 * </p>
		 * @param item
		 *            The item to populate
		 */
		protected abstract void populateItem(final ListItem item);

		protected void renderItem(final ListItem item)
		{
			item.render();
		}
	}
