/* ComposeFormulaDialog.java

{{IS_NOTE
	Purpose:
		
	Description:
		
	History:
		Nov 26, 2010 10:41:33 AM , Created by Sam
}}IS_NOTE

Copyright (C) 2009 Potix Corporation. All Rights Reserved.

*/
package org.zkoss.zssex.ui.dialog;

import java.util.LinkedList;
import java.util.List;

import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.HtmlBasedComponent;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.SerializableEventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.select.SelectorComposer;
import org.zkoss.zk.ui.select.annotation.Listen;
import org.zkoss.zk.ui.select.annotation.Wire;
import org.zkoss.zul.Label;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listcell;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.ListitemRenderer;
import org.zkoss.zul.Messagebox;
import org.zkoss.zul.SimpleListModel;
import org.zkoss.zul.Textbox;
import org.zkoss.zul.impl.InputElement;

/**
 * @author Hawk
 *
 */
public class ComposeFormulaCtrl extends SelectorComposer<Component> {
	
	private static final long serialVersionUID = 1L;
	@Wire
	private Label formulaStart;
	@Wire
	private Textbox composeFormulaTextbox;
	@Wire
	private Listbox argsListbox;
	@Wire
	private Label description;

	private FormulaMetaInfo metaInfo;
	private int focusToIndex = -1;
	private List<ArgWrapper> args;

	private List<InputElement> inputs = new LinkedList<InputElement>();
	
	/* whether move focus to next input component or not */
	private boolean movedToNext = false;

	/* current focus component */
	private InputElement focusComponent;

	@Override
	public void doAfterCompose(Component comp) throws Exception {
		super.doAfterCompose(comp);
		
		composeFormulaTextbox.addEventListener(Events.ON_CHANGE, new SerializableEventListener() {
			private static final long serialVersionUID = -8155889223889099793L;

			public void onEvent(Event event) throws Exception {
				decomposeFormula();
			}
		});
		argsListbox.setItemRenderer(new ListitemRenderer() {
			public void render(Listitem item, Object data) throws Exception {
				final ArgWrapper arg = (ArgWrapper)data;
				item.setValue(arg);
				item.appendChild(new Listcell(arg.getName()));
				final Textbox tb = new Textbox(arg.getValue());
				inputs.add(tb);
				
				tb.addEventListener(Events.ON_CHANGE, new SerializableEventListener() {
					private static final long serialVersionUID = 1L;
					public void onEvent(Event event) throws Exception {
						arg.setValue(tb.getValue());
						composeFormula();
						moveFocusToNext(tb);
						movedToNext = false;
					}
				});
				tb.addEventListener(Events.ON_FOCUS, new SerializableEventListener() {
					private static final long serialVersionUID = 1L;
					public void onEvent(Event event) throws Exception {
						ArgWrapper last = args.get(args.size() - 1);
						if (last.equals(arg) && metaInfo.isMultipleParameter()) {
							focusToIndex = args.size() - 1;
							args.add(createNextArg());
							argsListbox.setModel(newListModelInstance(args));
						} else {
							focusComponent = tb;
						}
						
						if (movedToNext == true && focusComponent != null)
							focusComponent.focus();
						movedToNext = false;
					}
				});
				
				Listcell cell = new Listcell();
				cell.appendChild(tb);
				item.appendChild(cell);
			}

			@Override
			public void render(Listitem item, Object data, int index)
					throws Exception {
				render(item, data);
			}
		});
		argsListbox.addEventListener("onAfterRender", new SerializableEventListener() {
			public void onEvent(Event event) throws Exception {
				int focusIdx = -1;
				if (focusToIndex >= 0 && focusToIndex < inputs.size()) {
					focusIdx = focusToIndex;
				} else if (inputs.size() > 1) {
					focusIdx = 0;
				}
				if (focusIdx >= 0) {
					inputs.get(focusIdx).focus();
					focusComponent = inputs.get(focusIdx);
				}
			}
		});
	}
	
	@Listen("onOpen = #_composeFormulaDialog")
	public void onOpen$_composeFormulaDialog(Event evt) {
		metaInfo = (FormulaMetaInfo) evt.getData();
		formulaStart.setValue("=" + metaInfo.getFunction()+"(");
		description.setValue(metaInfo.getDescription());
		composeFormulaTextbox.setText(null);
		args = createArgs(metaInfo.getRequiredParameter(), metaInfo.getParameterNames());
		argsListbox.setModel(newListModelInstance(args));
		composeFormulaTextbox.focus();
	}
	
	private SimpleListModel<ArgWrapper> newListModelInstance(List<ArgWrapper> functionArguments) {
		inputs.clear();
		focusComponent = null;
		return new SimpleListModel<ArgWrapper>(functionArguments);
	}
	
	private void moveFocusToNext(HtmlBasedComponent current) {
		for (int i = 0; i < inputs.size(); i++) {
			InputElement c = inputs.get(i);
			if (c == current && (i + 1) < inputs.size()) {
				InputElement next = inputs.get(i + 1);
				next.focus();
				focusComponent = next;
				movedToNext = true;
				break;
			}
		}
	}
	
	private void composeFormula() {
		StringBuilder strBuilder = new StringBuilder();
		boolean first = true;
		for (int i = 0; i < args.size(); i++) {
			ArgWrapper curArg = args.get(i);
			String arg = curArg.getValue();
			if (first)
				first = false;
			else if (!first) {
				if (!"".equals(arg))
					strBuilder.append(",");
				else {
					for (int j = i + 1; j < args.size(); j++) {
						String val = args.get(j).getValue();
						if (!"".equals(val)) {
							strBuilder.append(",");
							break;
						}
					}
				}
			}
			if (!"".equals(arg)) {
				strBuilder.append(arg);
			}
		}
		composeFormulaTextbox.setText(strBuilder.toString());
	}

	private ArgWrapper createNextArg() {
		Integer num = null;
		String argName = metaInfo.getMultipleParameter();
		ArgWrapper last = args.get(args.size() - 1);
		try {
			num = Integer.parseInt(last.getName().replace(argName, ""));
			num++;
		} catch (NumberFormatException ex) {
		}
		return new ArgWrapper(args.size(), metaInfo.getMultipleParameter() + (num != null ? num : ""), "");
	}
	
	private void decomposeFormula() {
		String input = composeFormulaTextbox.getText();
		String[] arg = input.split(",");
		if (arg.length > args.size())
			if (metaInfo.isMultipleParameter()) {
				int diff = arg.length - args.size();
				for (int i = 0; i < diff; i++) {
					args.add(createNextArg());
				}
			} else {
				Messagebox.show("You've entered too many arguments for this function");
				return;
			}
		for (int i = 0; i < args.size() && i < arg.length; i++) {
			String val = arg[i].trim();
			args.get(i).setValue(val);
		}
		argsListbox.setModel(newListModelInstance(args));
	}
	
	private List<ArgWrapper> createArgs(int numArg, String[] argNames) {
		LinkedList<ArgWrapper> ary = new LinkedList<ArgWrapper>();
		for (int i = 0; i < numArg; i++) {
			ary.add(new ArgWrapper(i, argNames[i], ""));
		}
		return ary;
	}
	
	private class ArgWrapper {
		Integer index;
		String name;
		String value;
		
		public ArgWrapper(Integer index, String name, String value) {
			super();
			this.index = index;
			this.name = name;
			this.value = value;
		}
		public String getName() {
			return name;
		}
		public void setName(String name) {
			this.name = name;
		}
		public Integer getIndex() {
			return index;
		}
		public void setIndex(Integer index) {
			this.index = index;
		}
		public String getValue() {
			return value;
		}
		public void setValue(String value) {
			this.value = value;
		}
	}
}