/*
 * @copyright 2012 Philip Warner
 * @license GNU General Public License
 * 
 * This file is part of Book Catalogue.
 *
 * Book Catalogue is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Book Catalogue 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Book Catalogue.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.eleybourn.bookcatalogue.properties;

import android.app.AlertDialog;
import android.graphics.Typeface;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;

import com.eleybourn.bookcatalogue.BookCatalogueApp;
import com.eleybourn.bookcatalogue.R;
import com.eleybourn.bookcatalogue.utils.HintManager;
import com.eleybourn.bookcatalogue.utils.ViewTagger;

import java.util.ArrayList;
import java.util.Iterator;

/**
 * Implement a generic list-of-values property.
 * 
 * @author Philip Warner
 *
 * @param <T>		Base type of list items
 */
public abstract class ListProperty<T extends Object> extends ValuePropertyWithGlobalDefault<T> {
	/** List of valid values */
	protected ItemEntries<T> mList = null;

	/** Accessor */
	public ItemEntries<T> getListItems() { return mList; };
	/** Accessor */
	public void setListItems(ItemEntries<T> list) { mList= list; };

	public ListProperty(ItemEntries<T> list, String uniqueId, PropertyGroup group, int nameResourceId, T value, String defaultPref, T defaultValue) {
		super(uniqueId, group, nameResourceId, value, defaultPref, defaultValue);
		mList = list;
	}
	public ListProperty(ItemEntries<T> list, String uniqueId, PropertyGroup group, int nameResourceId, T value, T defaultValue) {
		super(uniqueId, group, nameResourceId, value, null, defaultValue);
		mList = list;
	}

	/**
	 * Return the default list editor view with associated event handlers.
	 */
	@Override
	public View getView(final LayoutInflater inflater) {
		View v = inflater.inflate(R.layout.property_value_list, null);
		ViewTagger.setTag(v, R.id.TAG_PROPERTY, this);
		// Display the list of values when clicked.
		v.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				handleClick(v, inflater);
			}
		});

		// Set the name
		TextView text = (TextView) v.findViewById(R.id.name);
		text.setText(getName());
		
		// Try to find the list item that corresponds to the current stored value.
		ItemEntry<T> val = null;
		for(ItemEntry<T> e: getListItems() ) {
			if (e.value == null) {
				if (get() == null) {
					val = e;
				}
			} else {
				if (get() != null && get().equals(e.value)) {
					val = e;					
				}
			}
		}
		// Display current value
		setValueInView(v, val);

		return v;
	}

	private void handleClick(final View base, final LayoutInflater inflater) {
		final ItemEntries<T> items = getListItems();
		if (this.hasHint()) {
			HintManager.displayHint(base.getContext(), this.getHint(), new Runnable(){
				@Override
				public void run() {
					displayList(base, inflater, items);
				}});
		} else {
			displayList(base, inflater, items);
		}
	}

	/**
	 * Class to represent all items in a list-of-values property
	 * 
	 * @author Philip Warner
	 *
	 * @param <T>		Type of underlying list item
	 */
	public static class ItemEntry<T extends Object> {
		/** Actual value */
		T value;
		/** Test description of the meaning of that value */
		int textId;
        Object[] textArgs;

		/** Constructor. Instantiates string. */
		public ItemEntry(T value, int resourceId, Object... args) {
			this.value = value;
			this.textId = resourceId; //BookCatalogueApp.getResourceString(resourceId);
            this.textArgs = args;
		}
//		/** Constructor */
//		public ItemEntry(T value, String text) {
//			this.value = value;
//			this.text = text;
//		}

        /** Accessor */
        public String getString() {
            return BookCatalogueApp.getResourceString(textId, textArgs);
        }

        /** Accessor */
        public T getValue() {
            return value;
        }

        /** Accessor */
        public void setString(int value, Object... args) {
            textId = value;
            textArgs = args;
        }

        @Override
		public String toString() {
			return getString();
		}
	}

	/**
	 * Class to represent a collection of list entries for a list-of-values property
	 * 
	 * @author Philip Warner
	 *
	 * @param <T>		Underlying list item data type.
	 */
	public static class ItemEntries<T> implements Iterable<ItemEntry<T>> {
		ArrayList<ItemEntry<T>> mList = new ArrayList<ItemEntry<T>>();

		/**
		 * Utility to make adding items easier.
		 * 
		 * @param value		Underlying value
		 * @param stringId	String ID of description
		 * @return
		 */
		public ItemEntries<T> add(T value, int stringId, Object... args) {
			mList.add(new ItemEntry<T>(value, stringId, args));
			return this;
		}
//		/**
//		 * Utility to make adding items easier.
//		 *
//		 * @param value		Underlying value
//		 * @param stringId	Description
//		 * @return
//		 */
//		public ItemEntries<T> add(T value, int string) {
//			mList.add(new ItemEntry<T>(value, string));
//			return this;
//		}
		/** Iterator access */
		@Override
		public Iterator<ItemEntry<T>> iterator() {
			return mList.iterator();
		}
	}
	
	/**
	 * Holder class for list items 
	 * 
	 * @author Philip Warner
	 *
	 * @param <T>
	 */
	private static class Holder<T extends Object> {
		ItemEntry<T> item;
		View baseView;
		public Holder(ItemEntry<T> item, View baseView) {
			this.item = item;
			this.baseView = baseView;
		}
	}

	/**
	 * Set the 'value' field in the passed view to match the passed item.
	 * 
	 * @param baseView
	 * @param item
	 */
	private void setValueInView(View baseView, ItemEntry<T> item) {
		TextView text = (TextView) baseView.findViewById(R.id.value);

		if (item == null) {
			text.setText("");		
		} else {
			if (isDefault(item.value))
				text.setTypeface(null, Typeface.NORMAL);
			else
				text.setTypeface(null, Typeface.BOLD);

			text.setText(item.getString());					
		}
	}

	/**
	 * Called to display a list of values for this property.
	 * 
	 * @param base		Specific view that was clicked
	 * @param inflater	LayoutInflater
	 * @param items		All list items
	 * @return
	 */
	private boolean displayList(final View base, final LayoutInflater inflater, ItemEntries<T> items) {

		// Get the view and the radio group
		View root = inflater.inflate(R.layout.property_value_list_list, null);
		RadioGroup grp = (RadioGroup) root.findViewById(R.id.values);

		// Get the current value
		T curr = get();

		final AlertDialog dialog = new AlertDialog.Builder(inflater.getContext()).setView(root).create();

		// Create a listener that responds to any click on the list
		OnClickListener l = new OnClickListener() {
			@Override
			public void onClick(View v) {
				dialog.dismiss();
				Holder<T> h = ViewTagger.getTag(v, R.id.TAG_HOLDER);
				set(h.item.value);
				setValueInView(h.baseView, h.item);
			}
		};

		// Add each entry to the list
		for(ItemEntry<T> e: items) {
			// If we are looking at global-only values, NULL is invalid
			if (e.value != null || !isGlobal()) {
				// Check if this value is the currently selected value
				boolean selected = false;
				if (e.value == null && curr == null)
					selected = true;
				else if (e.value != null && curr != null && curr.equals(e.value))
					selected = true;
				// Make the view for this item
				View v = inflater.inflate(R.layout.property_value_list_item, null);
				TextView name = (TextView) v.findViewById(R.id.name);
				RadioButton sel = (RadioButton) v.findViewById(R.id.selector);
				//Set the various values
				sel.setChecked(selected);
				name.setText(e.getString());
				// Listen for clicks
				sel.setOnClickListener(l);
				v.setOnClickListener(l);
				// Set the tacks used by the listeners
				ViewTagger.setTag(v, R.id.TAG_HOLDER, new Holder<T>(e, base));
				ViewTagger.setTag(sel, R.id.TAG_HOLDER, new Holder<T>(e, base));
				// Add it to the group
				grp.addView(v);		
			}
		}

		// Display dialog
		dialog.show();
		return true;
	}
}

