package cat.diba.jee.mediatecagwt.client;

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

import com.allen_sauer.gwt.log.client.Log;
import com.smartgwt.client.data.RecordList;
import com.smartgwt.client.types.VerticalAlignment;
import com.smartgwt.client.widgets.IButton;
import com.smartgwt.client.widgets.TransferImgButton;
import com.smartgwt.client.widgets.events.ClickEvent;
import com.smartgwt.client.widgets.events.ClickHandler;
import com.smartgwt.client.widgets.form.DynamicForm;
import com.smartgwt.client.widgets.form.fields.CanvasItem;
import com.smartgwt.client.widgets.form.fields.FormItem;
import com.smartgwt.client.widgets.form.fields.events.FormItemInitHandler;
import com.smartgwt.client.widgets.form.fields.events.ShowValueEvent;
import com.smartgwt.client.widgets.form.fields.events.ShowValueHandler;
import com.smartgwt.client.widgets.form.validator.CustomValidator;
import com.smartgwt.client.widgets.form.validator.Validator;
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.layout.HLayout;
import com.smartgwt.client.widgets.layout.Layout;
import com.smartgwt.client.widgets.layout.VLayout;
import com.smartgwt.client.widgets.layout.VStack;

public class MultipleItem<T> extends CanvasItem {

	private static final String ATTR_VALUE = "v";

	private FormItem valueInputItem;
	private IButton addButton;

	private ListGrid grid;
	private Layout gridButtons;

	private Boolean required;
	
	
	private FormItemInitHandler initHandler = new FormItemInitHandler() {
		@Override
		public void onInit(FormItem item) {
			init();
		}
	};

	private int width;

	private final T[] emptyArray;


	public MultipleItem(String name, String title, FormItem valueInputItem, T[] emptyArray) {
		
		super(name, title);
		this.valueInputItem = valueInputItem;
		this.emptyArray = emptyArray;
		setInitHandler(initHandler);
	}
	
	public MultipleItem(FormItem valueInputItem, T[] emptyArray) {
		
		super();
		this.valueInputItem = valueInputItem;
		this.emptyArray = emptyArray;
		setInitHandler(initHandler);
	}

	private void init() {
		
		valueInputItem.setShowTitle(false);
		valueInputItem.setRequired(true);
		
		grid = new ListGrid();  
		grid.setShowAllRecords(true);
		grid.setShowHeader(false);
		grid.setFields( new ListGridField(ATTR_VALUE) );
		grid.setHeight(120);
		
		updateComponentsWidth();
		
		final DynamicForm form = new DynamicForm();
		form.setFields(valueInputItem);
		
		addButton = new IButton("+");
		addButton.setWidth(20);
		addButton.addClickHandler(new ClickHandler() {
			@SuppressWarnings("unchecked")
			@Override
			public void onClick(ClickEvent event) {
				Log.debug("Adding value");
				if( form.validate() ) {
					addValue((T)valueInputItem.getValue());
				}
			}
		});
		
		gridButtons = createGridButtons();
		
		Layout upperLayout = new HLayout(5);
		upperLayout.addMember(form);
		upperLayout.addMember(addButton);

		Layout bottomLayout = new HLayout(5);
		bottomLayout.addMember(grid);
		bottomLayout.addMember(gridButtons);

		Layout layout = new VLayout(5);
		layout.addMember(upperLayout);
		layout.addMember(bottomLayout);

		setCanvas(layout);
		
		addShowValueHandler(new ShowValueHandler() {
			@SuppressWarnings("unchecked")
			@Override
			public void onShowValue(ShowValueEvent event) {
				Log.warn("Show value event. Data value: " + Arrays.toString((T[])event.getDataValue() ));
//				Log.warn("Show value event. Display value: " + Arrays.toString((T[]) event.getDisplayValue() ));
				grid.setData( arrayToRecords((T[]) event.getDataValue()) );
			}
		});
		
		setShouldSaveValue(true);
		
		Validator requiredValidator = new CustomValidator() {
			@SuppressWarnings("unchecked")
			@Override
			protected boolean condition(Object value) {
//				Log.debug("Validator executed with value: " + value);
				return !required || isNotEmpty((T[])value);
			}			
		};
		
		requiredValidator.setErrorMessage("Value is required.");
		
		setValidators( requiredValidator );
	}
	
	@Override
	public void setWidth(int width) {
		this.width = width;
		super.setWidth(width);
		updateComponentsWidth();
	}
	
	private void updateComponentsWidth() {
		if( valueInputItem != null )
			valueInputItem.setWidth(width);
		if( grid != null )
			grid.setWidth(width + 1);
	}

	private void addValue(T value) {
		grid.addData(new ValueRecord<T>(value));
		fireGridUpdatedEvent();
	}

	private void fireGridUpdatedEvent() {
		
		T[] value = recordsToArray(grid.getRecords());
		
		Log.debug("Storing value: " + Arrays.toString(value));
		// TODO: Throws an exception!
		storeValue(value);
	}

	private ListGridRecord[] arrayToRecords(T[] value) {
		
		ListGridRecord[] records = new ListGridRecord[value.length];
		for (int i=0; i<value.length; i++) {
			records[i] = new ValueRecord<T>(value[i]);
		}
		return records;
	}
	
	@SuppressWarnings("unchecked")
	private T[] recordsToArray(ListGridRecord[] records) {
		
		List<T> result = new ArrayList<T>();
		
		for (ListGridRecord record : records) {
			ValueRecord<T> valueRecord = (ValueRecord<T>) record;
			result.add( valueRecord.getValue() );
		}
		
		return result.toArray(emptyArray);
	}
	
	@Override
	public void setRequired(Boolean required) {
		this.required = required;
		super.setRequired(required);
	}

	private static class ValueRecord<T> extends ListGridRecord {

		public ValueRecord(T value) {
			setAttribute(ATTR_VALUE, value);  
		}
		
		@SuppressWarnings("unchecked")
		public T getValue() {
			return (T) getAttributeAsObject(ATTR_VALUE);
		}
	}

	private Layout createGridButtons() {

		VStack modifyStack = new VStack(3);
		modifyStack.setWidth(20);
		modifyStack.setAlign(VerticalAlignment.CENTER);

		TransferImgButton up = new TransferImgButton(TransferImgButton.UP);
		up.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				ListGridRecord selectedRecord = grid.getSelectedRecord();
				if (selectedRecord != null) {
					int idx = grid.getRecordIndex(selectedRecord);
					if (idx > 0) {
						RecordList rs = grid.getRecordList();
						rs.removeAt(idx);
						rs.addAt(selectedRecord, idx - 1);
						fireGridUpdatedEvent();
					}
				}
			}
		});

		TransferImgButton upFirst = new TransferImgButton(TransferImgButton.UP_FIRST);
		upFirst.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				ListGridRecord selectedRecord = grid.getSelectedRecord();
				if (selectedRecord != null) {
					int idx = grid.getRecordIndex(selectedRecord);
					if (idx > 0) {
						RecordList rs = grid.getRecordList();
						rs.removeAt(idx);
						rs.addAt(selectedRecord, 0);
						fireGridUpdatedEvent();
					}
				}
			}
		});

		TransferImgButton down = new TransferImgButton(TransferImgButton.DOWN);
		down.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				ListGridRecord selectedRecord = grid.getSelectedRecord();
				if (selectedRecord != null) {
					RecordList rs = grid.getRecordList();
					int numRecords = rs.getLength();
					int idx = grid.getRecordIndex(selectedRecord);
					if (idx < numRecords - 1) {
						rs.removeAt(idx);
						rs.addAt(selectedRecord, idx + 1);
						fireGridUpdatedEvent();
					}
				}
			}
		});

		TransferImgButton downLast = new TransferImgButton(TransferImgButton.DOWN_LAST);
		downLast.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				ListGridRecord selectedRecord = grid.getSelectedRecord();
				if (selectedRecord != null) {
					RecordList rs = grid.getRecordList();
					int numRecords = rs.getLength();
					int idx = grid.getRecordIndex(selectedRecord);
					if (idx < numRecords - 1) {
						rs.removeAt(idx);
						rs.addAt(selectedRecord, rs.getLength());
						fireGridUpdatedEvent();
					}
				}
			}
		});

		TransferImgButton delete = new TransferImgButton(TransferImgButton.DELETE);
		delete.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				ListGridRecord[] selectedRecords = grid.getSelectedRecords();
				if (selectedRecords != null) {
					for (ListGridRecord selectedRecord : selectedRecords) {
						grid.removeData(selectedRecord);
						fireGridUpdatedEvent();
					}
				}
			}
		});

		modifyStack.addMember(upFirst);
		modifyStack.addMember(up);
		modifyStack.addMember(down);
		modifyStack.addMember(downLast);
		modifyStack.addMember(delete);
		return modifyStack;
	}
	
	private static <E> boolean isNotEmpty(E[] array) {
		return array != null && array.length > 0;
	}
}
