/**
 * 
 */
package org.richfaces.model;

import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import javax.faces.context.FacesContext;
import javax.faces.model.DataModel;

import org.ajax4jsf.model.DataVisitor;
import org.ajax4jsf.model.ExtendedDataModel;
import org.ajax4jsf.model.Range;
import org.ajax4jsf.model.SequenceDataModel;

/**
 * Dual map-based extended data model for model-translating components like list shuttle
 * @author Nick Belaevski
 *
 */
public class ListShuttleDataModel extends ExtendedDataModel {

	private ListShuttleRowKey rowKey;

	private SequenceDataModel sourceModel;
	private SequenceDataModel targetModel;

	private Map data;

	private Object wrappedData;
	
	/* (non-Javadoc)
	 * @see org.ajax4jsf.model.ExtendedDataModel#getRowKey()
	 */
	public Object getRowKey() {
		return rowKey;
	}

	/* (non-Javadoc)
	 * @see org.ajax4jsf.model.ExtendedDataModel#setRowKey(java.lang.Object)
	 */
	public void setRowKey(Object key) {
		this.rowKey = (ListShuttleRowKey) key;
	}

	public void walk(final FacesContext context, final DataVisitor visitor, final Range range,
			final Object argument) throws IOException {
		if (data != null) {
			Iterator iterator = data.entrySet().iterator();
			
			while (iterator.hasNext()) {
				Entry entry =(Map.Entry) iterator.next();
				
				visitor.process(context, entry.getKey(), argument);
			}
			
		} else {
			sourceModel.walk(context, new DataVisitor() {

				public void process(FacesContext context, Object rowKey,
						Object argument) throws IOException {

					ListShuttleRowKey key = new ListShuttleRowKey(rowKey, true);
					visitor.process(context, key, argument);
				}
			}, range, argument);

			targetModel.walk(context, new DataVisitor() {

				public void process(FacesContext context, Object rowKey,
						Object argument) throws IOException {

					ListShuttleRowKey key = new ListShuttleRowKey(rowKey, false);
					visitor.process(context, key, argument);
				}
				
			}, range, argument);
		}
	}

	public int getRowCount() {
		if (data != null) {
			return data.size();
		} else {
			return sourceModel.getRowCount() + targetModel.getRowCount();
		}
	}

	public Object getRowData() {
		if (data != null) {
			return data.get(rowKey);
		} else {
			if (this.rowKey != null) {
				if (this.rowKey.isSource()) {
					return sourceModel.getRowData();
				} else {
					return targetModel.getRowData();
				}
			} else {
				return null;
			}
		}
	}

	public int getRowIndex() {
		// TODO Auto-generated method stub
		return 0;
	}

	public Object getWrappedData() {
		return wrappedData;
	}

	public boolean isRowAvailable() {
		if (data != null) {
			return data.containsKey(rowKey);
		} else {
			if (rowKey != null) {
				if (rowKey.isSource()) {
					return sourceModel.isRowAvailable();
				} else {
					return targetModel.isRowAvailable();
				}
			} else {
				return false;
			}
		}
	}

	public void setRowIndex(int rowIndex) {
		// TODO Auto-generated method stub
		
	}

	public void setWrappedData(Object data) {
		this.rowKey = null;
		this.wrappedData = data;
		
		if (data instanceof Map) {
			this.data = (Map) data;
		} else {
			DataModel[] models = (DataModel[]) data;
			this.sourceModel = new SequenceDataModel(models[0]);
			this.targetModel = new SequenceDataModel(models[1]);
		}
	}

}
