/*******************************************************************************
 * Copyright (c) 2014, 2015 Scott Clarke (scott@dawg6.com).
 *
 * This file is part of Dawg6's Common GWT Libary.
 *
 * Dawg6's Common GWT Libary 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.
 *
 * Dawg6's Common GWT Libary 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 *******************************************************************************/
package com.dawg6.gwt.client.widgets;

import java.util.List;
import java.util.Vector;

import com.dawg6.gwt.common.util.AsyncTaskHandler;
import com.dawg6.gwt.common.util.DefaultCallback;
import com.dawg6.gwt.common.util.ObservableList;
import com.dawg6.gwt.common.util.ObservableListInterface;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.ListBox;

public abstract class GenericComboBox<T> extends ListBox
		implements ObservableListInterface<T> {

	protected final ObservableList<T> list = new ObservableList<T>();
	private final List<SelectionChangedHandler<T>> selectionChangedHandlers = new Vector<SelectionChangedHandler<T>>();

	public interface SelectionChangedHandler<T> {
		void selectionChanged(T value);
	}

	public GenericComboBox() {
		this.addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				selectionChanged();
			}
		});

		this.addChangeHandler(new ChangeHandler() {

			@Override
			public void onChange(ChangeEvent event) {
				selectionChanged();
			}
		});
	}

	public void addSelectionChangedHandler(SelectionChangedHandler<T> handler) {
		if (!selectionChangedHandlers.contains(handler))
			selectionChangedHandlers.add(handler);
	}

	protected void selectionChanged() {
		T value = getSelectedItem();

		for (SelectionChangedHandler<T> h : selectionChangedHandlers)
			h.selectionChanged(value);
	}

	public void removeSelectionChangedHandler(SelectionChangedHandler<T> handler) {
		selectionChangedHandlers.remove(handler);
	}

	@Override
	public void onLoad() {
		refresh();
	}

	protected abstract void loadObjects(AsyncCallback<List<T>> handler);

	@Override
	public void clear() {
		super.clear();
		list.clear();
	}

	public void refresh(AsyncTaskHandler handler) {
		final T item = getSelectedItem();

		loadObjects(new DefaultCallback<List<T>>(handler) {

			@Override
			public void doOnSuccess(List<T> result) {
				loadObjects(result);
				setSelectedItem(item);
			}
		});
	}

	public void refresh() {
		refresh(null);
	}
	
	protected void loadObjects(List<T> result) {
		clear();

		if ((result == null) || result.isEmpty()) {
			super.addItem(this.getEmptyText(), (String)null);
			list.add(null);
		} else {
			list.addAll(result);
	
			for (T item : result) {
				String text = getDisplayText(item);
				super.addItem(text, getId(item).toString());
			}
		}

		super.setSelectedIndex(0);
	}

	protected abstract String getDisplayText(T type);

	public T getSelectedItem() {
		int index = getSelectedIndex();

		if (index < 0)
			return null;

		return list.get(index);
	}

	public void setSelectedItem(T item) {

		if (item == null) {
			this.setSelectedIndex(0);
			return;
		}
		
		setSelectedId(getId(item));
	}

	@Override
	public void addObserver(
			ObservableListInterface.ListChangedHandler<T> handler) {
		list.addObserver(handler);
	}

	@Override
	public void removeObserver(
			ObservableListInterface.ListChangedHandler<T> handler) {
		list.removeObserver(handler);
	}

	public void setSelectedId(String id) {
		if (id == null) {
			this.setSelectedIndex(0);
			return;
		}

		int count = this.getItemCount();

		for (int i = 0; i < count; i++) {
			String text = this.getValue(i);

			if (id.equals(text)) {
				this.setSelectedIndex(i);
				return;
			}
		}

		this.setSelectedIndex(0);
	}

	public T getItem(int i) {
		return list.get(i);
	}

	@Override
	public void removeItem(int i) {
		super.removeItem(i);
		list.remove(i);
	}

	public void insertItem(T item, int i) {
		
		if (item == null) {
			super.addItem(getEmptyText(), (String)null);
		} else {
			String text = getDisplayText(item);
			
			super.insertItem(text, getId(item), i);
		}
		
		list.add(i, item);
	}

	public void addItem(T item) {
		
		if (item == null) {
			super.addItem(getEmptyText(), (String)null);
		} else {
			String text = getDisplayText(item);
			super.addItem(text, getId(item));
		}

		list.add(item);
	}

	public void addAll(List<T> items) {
		for (T item : items) {
			
			if (item == null) {
				super.addItem(getEmptyText(), (String)null);
			} else {
				String text = getDisplayText(item);
				super.addItem(text, getId(item));
			}
		}

		list.addAll(items);
	}
	
	public abstract String getEmptyText();
	public abstract String getId(T value);
}
