package nl.nl2312.mimicry.sample.adapters;

import java.util.ArrayList;

import android.database.DataSetObserver;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Adapter;
import android.widget.BaseAdapter;
import android.widget.ListAdapter;
import android.widget.SectionIndexer;
import android.widget.TextView;

/**
 * An adapter that can contain many other adapters and shows them in sequence. Taken from
 * http://stackoverflow.com/questions/7964259/android-attaching-multiple-adapters-to-one-adapter and based on the Apache
 * 2-licensed CommonsWare MergeAdapter.
 * @author Eric Kok
 * @author Alex Amiryan
 * @author Mark Murphy
 */
public class MergeAdapter extends BaseAdapter implements SectionIndexer {

	protected ArrayList<ListAdapter> pieces = new ArrayList<ListAdapter>();
	protected String noItemsText;

	/**
	 * Stock constructor, simply chaining to the superclass.
	 */
	public MergeAdapter() {
		super();
	}

	/**
	 * Adds a new adapter to the roster of things to appear in the aggregate list.
	 * @param adapter Source for row views for this section
	 */
	public void addAdapter(ListAdapter adapter) {
		pieces.add(adapter);
		adapter.registerDataSetObserver(new CascadeDataSetObserver());
	}

	/**
	 * Get the data item associated with the specified position in the data set.
	 * @param position Position of the item whose data we want
	 */
	public Object getItem(int position) {
		for (ListAdapter piece : pieces) {
			int size = piece.getCount();

			if (position < size) {
				return (piece.getItem(position));
			}

			position -= size;
		}

		return (null);
	}

	public void setNoItemsText(String text) {
		noItemsText = text;
	}

	/**
	 * Get the adapter associated with the specified position in the data set.
	 * @param position Position of the item whose adapter we want
	 */
	public ListAdapter getAdapter(int position) {
		for (ListAdapter piece : pieces) {
			int size = piece.getCount();

			if (position < size) {
				return (piece);
			}

			position -= size;
		}

		return (null);
	}

	/**
	 * How many items are in the data set represented by this {@link Adapter}.
	 */
	public int getCount() {
		int total = 0;

		for (ListAdapter piece : pieces) {
			total += piece.getCount();
		}

		if (total == 0 && noItemsText != null) {
			total = 1;
		}

		return (total);
	}

	/**
	 * Returns the number of types of {@link View}s that will be created by {@link #getView(int, View, ViewGroup)}.
	 */
	@Override
	public int getViewTypeCount() {
		int total = 0;

		for (ListAdapter piece : pieces) {
			total += piece.getViewTypeCount();
		}

		return (Math.max(total, 1)); // needed for setListAdapter() before
										// content add'
	}

	/**
	 * Get the type of {@link View} that will be created by {@link #getView(int, View, ViewGroup)} for the specified
	 * item.
	 * @param position Position of the item whose data we want
	 */
	@Override
	public int getItemViewType(int position) {
		int typeOffset = 0;
		int result = -1;

		for (ListAdapter piece : pieces) {
			int size = piece.getCount();

			if (position < size) {
				result = typeOffset + piece.getItemViewType(position);
				break;
			}

			position -= size;
			typeOffset += piece.getViewTypeCount();
		}

		return (result);
	}

	/**
	 * Are all items in this {@link ListAdapter} enabled? If yes it means all items are selectable and clickable.
	 */
	@Override
	public boolean areAllItemsEnabled() {
		return (false);
	}

	/**
	 * Returns true if the item at the specified position is not a separator.
	 * @param position Position of the item whose data we want
	 */
	@Override
	public boolean isEnabled(int position) {
		for (ListAdapter piece : pieces) {
			int size = piece.getCount();

			if (position < size) {
				return (piece.isEnabled(position));
			}

			position -= size;
		}

		return (false);
	}

	/**
	 * Get a {@link View} that displays the data at the specified position in the data set.
	 * @param position Position of the item whose data we want
	 * @param convertView View to recycle, if not null
	 * @param parent ViewGroup containing the returned View
	 */
	public View getView(int position, View convertView, ViewGroup parent) {
		for (ListAdapter piece : pieces) {
			int size = piece.getCount();

			if (position < size) {

				return (piece.getView(position, convertView, parent));
			}

			position -= size;
		}

		if (noItemsText != null) {
			TextView text = new TextView(parent.getContext());
			text.setText(noItemsText);
			return text;
		}

		return (null);
	}

	/**
	 * Get the row id associated with the specified position in the list.
	 * @param position Position of the item whose data we want
	 */
	public long getItemId(int position) {
		for (ListAdapter piece : pieces) {
			int size = piece.getCount();

			if (position < size) {
				return (piece.getItemId(position));
			}

			position -= size;
		}

		return (-1);
	}

	public final int getPositionForSection(int section) {
		int position = 0;

		for (ListAdapter piece : pieces) {
			if (piece instanceof SectionIndexer) {
				Object[] sections = ((SectionIndexer) piece).getSections();
				int numSections = 0;

				if (sections != null) {
					numSections = sections.length;
				}

				if (section < numSections) {
					return (position + ((SectionIndexer) piece).getPositionForSection(section));
				} else if (sections != null) {
					section -= numSections;
				}
			}

			position += piece.getCount();
		}

		return (0);
	}

	public final int getSectionForPosition(int position) {
		int section = 0;

		for (ListAdapter piece : pieces) {
			int size = piece.getCount();

			if (position < size) {
				if (piece instanceof SectionIndexer) {
					return (section + ((SectionIndexer) piece).getSectionForPosition(position));
				}

				return (0);
			} else {
				if (piece instanceof SectionIndexer) {
					Object[] sections = ((SectionIndexer) piece).getSections();

					if (sections != null) {
						section += sections.length;
					}
				}
			}

			position -= size;
		}

		return (0);
	}

	public final Object[] getSections() {
		ArrayList<Object> sections = new ArrayList<Object>();

		for (ListAdapter piece : pieces) {
			if (piece instanceof SectionIndexer) {
				Object[] curSections = ((SectionIndexer) piece).getSections();

				if (curSections != null) {
					for (Object section : curSections) {
						sections.add(section);
					}
				}
			}
		}

		if (sections.size() == 0) {
			return (null);
		}

		return (sections.toArray(new Object[0]));
	}

	private class CascadeDataSetObserver extends DataSetObserver {
		@Override
		public void onChanged() {
			notifyDataSetChanged();
		}

		@Override
		public void onInvalidated() {
			notifyDataSetInvalidated();
		}
	}

}
