/* InsertFormulaCtrl.java

{{IS_NOTE
	Purpose:
		
	Description:
		
	History:
		Nov 25, 2010 5:54:06 PM , Created by Sam
}}IS_NOTE

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

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

import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.zkoss.util.resource.Labels;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.event.InputEvent;
import org.zkoss.zk.ui.event.SerializableEventListener;
import org.zkoss.zk.ui.select.annotation.Listen;
import org.zkoss.zk.ui.select.annotation.Wire;
import org.zkoss.zssex.ui.dialog.impl.DialogCallbackEvent;
import org.zkoss.zssex.ui.dialog.impl.DialogCtrlBase;
import org.zkoss.zul.Button;
import org.zkoss.zul.Combobox;
import org.zkoss.zul.Div;
import org.zkoss.zul.Label;
import org.zkoss.zul.ListModelList;
import org.zkoss.zul.Listbox;
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.Window;

/**
 * @author Hawk
 *
 */
public class InsertFormulaCtrl extends DialogCtrlBase {
	
	private static final long serialVersionUID = 1L;

	public static final String FORMULA = "formula";

	private final static String URI = "~./zssex/dlg/insertFormulaDialog.zul";
	
	private final static String ALL = "All";
	
	@Wire
	private Div findArea;
	@Wire
	private Textbox searchTextbox;
	@Wire
	private Combobox categoryCombobox;
	@Wire
	private Listbox functionListbox;
	@Wire
	private Label expression;
	@Wire
	private Label description;
	@Wire
	private Button selectButton;
	
	@Wire
	private Window _composeFormulaDialog;
	@Wire("#_composeFormulaDialog #formulaStart")
	private Label formulaStart;
	@Wire("#_composeFormulaDialog #formulaEnd")
	private Label formulaEnd;
	@Wire("#_composeFormulaDialog #composeFormulaTextbox")
	private Textbox composeFormulaTextbox;
	
	private LinkedHashMap<String, List<FormulaMetaInfo>> formulaInfos;
	
	public static void show(EventListener<DialogCallbackEvent> dialogEventListener) {
		Map arg = newArg(dialogEventListener);
		
		Window window = (Window)Executions.createComponents(URI, null, arg);
		window.doModal();
		return;
	}
	
	
	@Override
	public void doAfterCompose(Window comp) throws Exception{ 
		super.doAfterCompose(comp);
		
		formulaInfos = Formulas.getFormulaInfos();
		
		LinkedList<String> categoryArys = new LinkedList<String>();
		categoryArys.add(ALL);
		categoryArys.addAll(formulaInfos.keySet());
		categoryCombobox.setModel(
				new ListModelList(categoryArys, false));
		categoryCombobox.addEventListener("onAfterRender", new SerializableEventListener() {
			private static final long serialVersionUID = -7846551679007392114L;

			public void onEvent(Event event) throws Exception {
				categoryCombobox.setSelectedIndex(0);
				initFunctionListboxByCategory();
			}
		});
		
		searchTextbox.setText(null);
		searchTextbox.focus();
		functionListbox.setItemRenderer(new ListitemRenderer() {
			public void render(Listitem item, Object data) throws Exception {
				FormulaMetaInfo info = (FormulaMetaInfo)data;
				item.setLabel(info.getFunction());
				item.setValue(info);
				// ZSS-756: add event listener to listitem instead of listbox
				item.addEventListener(Events.ON_DOUBLE_CLICK, new SerializableEventListener<Event>() {
					private static final long serialVersionUID = -6064484957416645205L;

					public void onEvent(Event event) throws Exception {
						openComposeFormulaDialog();
					}
				});
			}

			@Override
			public void render(Listitem item, Object data, int index)
					throws Exception {
				render(item, data);
			}
		});
		//functionListbox.addEventListener(Events.ON_DOUBLE_CLICK, new EventListener() {
		//	public void onEvent(Event event) throws Exception {
		//		openComposeFormulaDialog();
		//	}
		//});
	}
	
	@Listen("onSelect = #categoryCombobox")
	public void onSelect$categoryCombobox() {
		initFunctionListboxByCategory();
	}
	
	private void initFunctionListboxByCategory() {
		String category = categoryCombobox.getSelectedItem().getValue();
		if (category == null)
			return;
		
		List<FormulaMetaInfo> categoriedFunctions = formulaInfos.get(category);
		if (categoriedFunctions == null) {
			//means all
			categoriedFunctions = new LinkedList<FormulaMetaInfo>();
			for (List<FormulaMetaInfo> infoAry : formulaInfos.values()) {
				categoriedFunctions.addAll(infoAry);
			}
		}
		functionListbox.setModel(new SimpleListModel<FormulaMetaInfo>(categoriedFunctions));
		selectButton.setDisabled(true);
	}
	
	@Listen("onSelect = #functionListbox")
	public void selectFunction() {
		FormulaMetaInfo info = (FormulaMetaInfo)functionListbox.getSelectedItem().getValue();
		expression.setValue(info.getExpression());
		description.setValue(info.getDescription());
		selectButton.setDisabled(false);
	}
	
	@Listen("onClick = #selectButton")
	public void confirmSelection() {
		openComposeFormulaDialog();
	}
	
	private void openComposeFormulaDialog() {
		Listitem item = (Listitem)functionListbox.getSelectedItem();
		if (item == null) {
			Messagebox.show(Labels.getLabel("zssex.dlg.insert_function.err.no_function_selected"));
			return;
		}	
		
		FormulaMetaInfo info = (FormulaMetaInfo) item.getValue();
		if (info.getRequiredParameter() == 0) {
			// ZSS-716: still need to assign formula if no parameter
			formulaStart.setValue("=" + info.getFunction() + "(");
			insertFunction();
		} else {
			openComposeFormulaDialog((FormulaMetaInfo)item.getValue());
		}
	}
	
	//TODO: shall I use echo event to show busy, need to test speed 
	//search in name, expression, and description 
	private List<FormulaMetaInfo> search(String keyword) {
		keyword = keyword.toLowerCase();
		LinkedList<FormulaMetaInfo> searchResult = new LinkedList<FormulaMetaInfo>();
		for (List<FormulaMetaInfo> infoAry : formulaInfos.values()) {
			for (FormulaMetaInfo info : infoAry) {
				if (info.getFunction().toLowerCase().contains(keyword) ||
					info.getExpression().toLowerCase().contains(keyword) ||
					info.getDescription().toLowerCase().contains(keyword))
					searchResult.add(info);
			}
		}
		Collections.sort(searchResult, new Comparator<FormulaMetaInfo>() {
			public int compare(FormulaMetaInfo o1, FormulaMetaInfo o2) {
				return o1.getFunction().compareTo(o2.getFunction());
			}
		});
		return searchResult;
	}
	
	@Listen("onChanging = #searchTextbox")
	public void startSearch(InputEvent event) {
		String keyword = event.getValue();
		if (keyword == null || "".equals(keyword)){
			initFunctionListboxByCategory();
		}else{
			functionListbox.setModel(new SimpleListModel<FormulaMetaInfo>(search(keyword)));
			selectButton.setDisabled(true);
		}
	}
	
	@Listen("onClick = #_composeFormulaDialog #insertBtn; onOK = #_composeFormulaDialog #composeFormulaTextbox")
	public void insertFunction(){
		postCallback(ON_OK, 
			newMap(newEntry(FORMULA, formulaStart.getValue() + composeFormulaTextbox.getText() + formulaEnd.getValue())));
		detach();
	}
	
	private void openComposeFormulaDialog(FormulaMetaInfo metainfo) {
		Events.sendEvent("onOpen", _composeFormulaDialog, metainfo);
		findArea.setVisible(false);
		_composeFormulaDialog.setVisible(true);
	}
}