import java.util.ArrayList;
import java.util.List;

import com.smartgwt.client.data.DSCallback;
import com.smartgwt.client.data.DSRequest;
import com.smartgwt.client.data.DSResponse;
import com.smartgwt.client.data.DataSource;
import com.smartgwt.client.data.DataSourceField;
import com.smartgwt.client.data.Record;
import com.smartgwt.client.data.fields.DataSourceBooleanField;
import com.smartgwt.client.data.fields.DataSourceImageField;
import com.smartgwt.client.data.fields.DataSourceTextField;
import com.smartgwt.client.widgets.form.fields.SelectItem;
import com.smartgwt.client.widgets.grid.HoverCustomizer;
import com.smartgwt.client.widgets.grid.ListGrid;
import com.smartgwt.client.widgets.grid.ListGridField;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.grid.events.RecordClickEvent;
import com.smartgwt.client.widgets.grid.events.RecordClickHandler;


public class SortablePickListItem extends SelectItem
{
	private static final String DISPLAY_VALUE= "displayValue";
	private static final String VALUE= "value";
	private static final String CHECKED= "checked";
	private static final String TOOLTIP= "tooltip";
	private static final String UP= "up";
	private static final String DOWN= "down";
	
	
	private final SortablePickListDataSource ds;
	private boolean manualSorting= true;
	private boolean selectedFirst= true;
	private boolean hideBorderArrows= true;
	
	private String iconUp;
	private String iconDown;
	
	
	public SortablePickListItem(boolean manualSorting, boolean selectedFirst, boolean hideBorderArrows, String iconUp, String iconDown)
	{
		this(null, manualSorting, selectedFirst, hideBorderArrows, iconUp, iconDown);
	}
	
	public SortablePickListItem(SortablePickListItem old)
	{
		this(old, old.manualSorting, old.selectedFirst, old.hideBorderArrows, old.iconUp, old.iconDown);
	}
	
	private SortablePickListItem(SortablePickListItem old, final boolean manualSorting, 
			final boolean selectedFirst, boolean hideBorderArrows, String iconUp, String iconDown)
	{
		this.manualSorting= manualSorting;
		this.selectedFirst= selectedFirst;
		this.hideBorderArrows= hideBorderArrows;
		this.iconUp= iconUp;
		this.iconDown= iconDown;

		this.ds= old == null ? new SortablePickListDataSource(manualSorting) : old.ds;

		setMultiple(true);
		setAllowEmptyValue(true);
		setFilterLocally(true);
		setValueField(VALUE);
		setDisplayField(DISPLAY_VALUE);
		
		if(manualSorting)
			setPickListFields(new ListGridField[]{new ListGridField(DISPLAY_VALUE, "100%"), new ListGridField(UP, 16), new ListGridField(DOWN, 16)});
		else
			setPickListFields(new ListGridField[]{new ListGridField(DISPLAY_VALUE, "100%")});
		
		setPickListProperties(new ListGrid(){{
			
			setShowHeader(false);
			setCanHover(true);  
	        setShowHover(true);
	        
	        setHoverCustomizer(new HoverCustomizer(){
				@Override
				public String hoverHTML(Object value, ListGridRecord record, int rowNum, int colNum)
				{
					return colNum!=1 ? null : /*"<div "/*style='width:300px;text-align:justify;'>"+*/record.getAttribute(TOOLTIP)/*+"</div>"*/;
				}
			});
	        
	        addRecordClickHandler(new RecordClickHandler(){
				@Override
				public void onRecordClick(RecordClickEvent event)
				{
					boolean checkedBefore= event.getRecord().getAttributeAsBoolean(CHECKED);
					String value= event.getRecord().getAttribute(VALUE);
					
					Record[] records= ds.getCacheData();
					int col= event.getFieldNum();
					int row= event.getRecordNum();
					if(col==0)
					{
						List<Record> checked= new ArrayList<Record>();
						List<Record> unchecked= new ArrayList<Record>();
						
						for(Record record : records)
						{
							if(getRealValue(record).equals(value))
								flipChecked(record);
							
							if(!selectedFirst || isChecked(record))
								checked.add(record);
							else
								unchecked.add(record);
						}

						checked.addAll(unchecked);
						adjustData(checked);
					}
					else if(manualSorting && col==2 && row>0 && (!selectedFirst || checkedBefore || !isChecked(records[row-1])))
					{
						List<Record> newOrder= new ArrayList<Record>();
						
						for(int i=0; i<row-1; i++)
							newOrder.add(records[i]);
						
						newOrder.add(records[row]);
						newOrder.add(records[row-1]);

						for(int i=row+1; i<records.length; i++)
							newOrder.add(records[i]);
						
						adjustData(newOrder);
					}
					else if(manualSorting && col==3 && row<records.length-1 && (!selectedFirst || !checkedBefore || isChecked(records[row+1])))
					{
						clearValue();
						
						List<Record> newOrder= new ArrayList<Record>();
						
						for(int i=0; i<row; i++)
							newOrder.add(records[i]);

						newOrder.add(records[row+1]);
						newOrder.add(records[row]);

						for(int i=row+2; i<records.length; i++)
							newOrder.add(records[i]);
						
						adjustData(newOrder);
					}
				}
			});
		}});
		
		setOptionDataSource(ds);
		if(old!=null)
		{
			setValues(old.getValues());
		}
	}
	
	public void populateInitial(List<SortablePickListRecord> records)
	{
		adjustData(records);
	}
	
	private void adjustData(List<? extends Record> records)
	{
		if(ds.getCacheData().length>0)
			clearValue();
		
		final List<String> checkedIds= new ArrayList<String>(records.size());
		for(int i=0; i<records.size(); i++)
		{
			if(hideBorderArrows)
			{
				if(i==0)
					setFirst(records.get(i), true);
				else
					setFirst(records.get(i), false);
				
				if(i==records.size()-1)
					setLast(records.get(i), true);
				else
					setLast(records.get(i), false);
			}
			
			if(isChecked(records.get(i)))
				checkedIds.add(getRealValue(records.get(i)));

			if(ds.getCacheData().length>0)
				ds.removeData(records.get(i));
		}
		
		for(int i=0; i<records.size()-1; i++)
			ds.addData(records.get(i));
		
		if(records.size()>0)
		{
			ds.addData(records.get(records.size()-1), new DSCallback(){
				@Override
				public void execute(DSResponse response, Object rawData, DSRequest request)
				{
					setValues(checkedIds.toArray(new String[checkedIds.size()]));
				}
			});
		}
	}
	
	
	private static class SortablePickListDataSource extends DataSource
	{
		private static final DataSourceField displayValueField= new DataSourceTextField(DISPLAY_VALUE);
		private static final DataSourceField valueField= new DataSourceTextField(VALUE);
		private static final DataSourceField tooltipField= new DataSourceTextField(TOOLTIP);
		private static final DataSourceField checkedField= new DataSourceBooleanField(CHECKED);
		private static final DataSourceField upField= new DataSourceImageField(UP);
		private static final DataSourceField downField= new DataSourceImageField(DOWN);
		
		static
		{
			displayValueField.setPrimaryKey(true);
			valueField.setHidden(true);
			tooltipField.setHidden(true);
			checkedField.setHidden(true);
		}
		
		private SortablePickListDataSource(boolean manualSorting)
		{
			super();
			
			if(manualSorting)
				setFields(displayValueField, valueField, tooltipField, checkedField, upField, downField);
			else
				setFields(displayValueField, valueField, tooltipField, checkedField);
			
			setClientOnly(true);
			setCacheData(new SortablePickListRecord[0]);
		}
	}
	
	
	class SortablePickListRecord extends Record
	{
		public SortablePickListRecord(String displayValue, String value, String tooltip, boolean selected)
		{
			super();
			
			setAttribute(DISPLAY_VALUE, displayValue);
			setAttribute(VALUE, value);
			setAttribute(TOOLTIP, tooltip);
			setAttribute(CHECKED, selected);
			setAttribute(UP, iconUp);
			setAttribute(DOWN, iconDown);
		}
		
		public SortablePickListRecord(SortablePickListRecord r)
		{
			this(getDisplayValue(r), getRealValue(r), getTooltip(r), isChecked(r));
		}
	}
	
	private static String getDisplayValue(Record r)
	{
		return r.getAttribute(DISPLAY_VALUE);
	}
	
	private static String getRealValue(Record r)
	{
		return r.getAttribute(VALUE);
	}
	
	private static String getTooltip(Record r)
	{
		return r.getAttribute(TOOLTIP);
	}
	
	private static boolean isChecked(Record r)
	{
		return r.getAttributeAsBoolean(CHECKED);
	}
	
	private static void flipChecked(Record r)
	{
		r.setAttribute(CHECKED, !isChecked(r));
	}
	
	private void setFirst(Record r, boolean first)
	{
		r.setAttribute(UP, first ? null : iconUp);
	}
	
	private void setLast(Record r, boolean last)
	{
		r. setAttribute(DOWN, last ? null : iconDown);
	}
	
	public void setHideBorderArrows(boolean hideBorderArrows)
	{
		this.hideBorderArrows= hideBorderArrows;
	}
	
	public void setSortSelectedBeforeDeselected(boolean selectedFirst)
	{
		this.selectedFirst= selectedFirst;
	}
}