/* FormatCellCtrl.java

{{IS_NOTE
	Purpose:
		
	Description:
		
	History:
		Dec 1, 2010 11:32:52 AM , Created by Sam
}}IS_NOTE

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

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

import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;

import org.zkoss.util.resource.Labels;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.SelectEvent;
import org.zkoss.zk.ui.select.annotation.Listen;
import org.zkoss.zk.ui.select.annotation.Wire;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zkex.zul.Colorbox;
import org.zkoss.zss.api.model.CellStyle.FillPattern;
import org.zkoss.zss.api.model.Color;
import org.zkoss.zss.ui.Spreadsheet;
import org.zkoss.zssex.ui.dialog.impl.DialogCallbackEvent;
import org.zkoss.zssex.ui.dialog.impl.RefocusDialogCtrlBase;
import org.zkoss.zul.Combobox;
import org.zkoss.zul.ListModelList;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.Messagebox;
import org.zkoss.zul.Textbox;
import org.zkoss.zul.Window;
import org.zkoss.zul.event.ListDataEvent;
import org.zkoss.zul.event.ListDataListener;

/**
 * @author Sam
 * @author dennis
 * @author JerryChen
 */
public class FormatCellCtrl extends RefocusDialogCtrlBase {

	private static final long serialVersionUID = 1L;
	
	public final static String ARG_FORMAT_CODE = "formatCode";
	public final static String ARG_BACKGROUND_CODE = "backgroundCode";
	public final static String ARG_FILL_COLOR_CODE = "fillColorCode";
	public final static String ARG_FILL_PATTERN_CODE = "fillPatternCode";
	public final static String ARG_ROTATION_CODE = "rotationCode";
	protected final static String URI = "~./zssex/dlg/formatNumber.zul";
	//ZSS-879: lookup format dialog listitem per the format pattern
	protected final static Map<String, FormatItem> FORMAT_ITEMS = new HashMap<String, FormatItem>();
	static {
		// mfn must be 1st to setup the map; so other mfn_xxx can override it!
		// mfn_custom
	    putFormatItem("mfn_custom", 0, "General", 0);
	    putFormatItem("mfn_custom", 1, "0", 1);
	    putFormatItem("mfn_custom", 2, "0.00", 2);
	    putFormatItem("mfn_custom", 3, "#,##0", 3);
	    putFormatItem("mfn_custom", 4, "#,##0.00", 4);
	    putFormatItem("mfn_custom", 5, "#,##0_);(#,##0)", 5);
	    putFormatItem("mfn_custom", 6, "#,##0_);[Red](#,##0)", 6);
	    putFormatItem("mfn_custom", 7, "#,##0.00_);(#,##0.00)", 7);
	    putFormatItem("mfn_custom", 8, "#,##0.00_);[Red](#,##0.00)", 8);
	    putFormatItem("mfn_custom", 9, "$#,##0_);($#,##0)", 9);
	    putFormatItem("mfn_custom", 10, "$#,##0_);[Red]($#,##0)", 10);
	    putFormatItem("mfn_custom", 11, "$#,##0.00_);($#,##0.00)", 11);
	    putFormatItem("mfn_custom", 12, "$#,##0.00_);[Red]($#,##0.00)", 12);
	    putFormatItem("mfn_custom", 13, "0%", 13);
	    putFormatItem("mfn_custom", 14, "0.00%", 14);
	    putFormatItem("mfn_custom", 15, "0.00E+00", 15);
	    putFormatItem("mfn_custom", 16, "##0.0E+0", 16);
	    putFormatItem("mfn_custom", 17, "# ?/?", 17);
	    putFormatItem("mfn_custom", 18, "# ??/??", 18);
        putFormatItem("mfn_custom", 19, "m/d/yyyy", 19);
        putFormatItem("mfn_custom", 20, "d-mmm-yy", 20);
        putFormatItem("mfn_custom", 21, "d-mmm", 21);
        putFormatItem("mfn_custom", 22, "mmm-yy", 22);
        putFormatItem("mfn_custom", 23, "h:mm AM/PM", 23);
        putFormatItem("mfn_custom", 24, "h:mm:ss AM/PM", 24);
        putFormatItem("mfn_custom", 25, "h:mm", 25);
        putFormatItem("mfn_custom", 26, "h:mm:ss", 26);
        putFormatItem("mfn_custom", 27, "m/d/yyyy h:mm", 27);
        putFormatItem("mfn_custom", 28, "mm:ss", 28);
        putFormatItem("mfn_custom", 29, "mm:ss.0", 29);
        putFormatItem("mfn_custom", 30, "@", 30);
        putFormatItem("mfn_custom", 31, "[h]:mm:ss", 31);
        putFormatItem("mfn_custom", 32, "_($* #,##0_);_($* (#,##0);_($* \"-\"_);_(@_)", 32);
        putFormatItem("mfn_custom", 33, "_(* #,##0_);_(* (#,##0);_(* \"-\"_);_(@_)", 33);
        putFormatItem("mfn_custom", 34, "_($* #,##0.00_);_($* (#,##0.00);_($* \"-\"??_);_(@_)", 34);
        putFormatItem("mfn_custom", 35, "_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)", 35);
		
		// mfn_general
		putFormatItem("mfn_general", 0, "General", 0);
		
		// mfn_number
		putFormatItem("mfn_number", 0, "0.00", 2);
		putFormatItem("mfn_number", 1, "0.00;[Red]0.00", -1);
		putFormatItem("mfn_number", 2, "0.00_);(0.00)", -1);
		putFormatItem("mfn_number", 3, "0.00_);[Red](0.00)", -1);
		
		// mfn_currency
		putFormatItem("mfn_currency", 0, "$#,##0.00", -1);
		putFormatItem("mfn_currency", 1, "$#,##0.00;[Red]$#,##0.00", -1);
		putFormatItem("mfn_currency", 2, "$#,##0.00_);($#,##0.00)", 11);
		putFormatItem("mfn_currency", 3, "$#,##0.00_);[Red]($#,##0.00)", 12);

		// mfn_accounting
		putFormatItem("mfn_accounting", 0, "_($* #,##0.00_);_($* (#,##0.00);_($* \"-\"??_);_(@_)", 34);

		// mfn_date
		putFormatItem("mfn_date", 0, "m/d/yyyy", 19);
        putFormatItem("mfn_date", 1, "dddd, mmmm dd, yyyy", -1);
        putFormatItem("mfn_date", 2, "m/d;@", -1);
		putFormatItem("mfn_date", 3, "m/d/yy;@", -1);
		putFormatItem("mfn_date", 4, "mm/dd/yy;@", -1);
        putFormatItem("mfn_date", 5, "d-mmm;@", -1);
        putFormatItem("mfn_date", 6, "d-mmm-yy;@", -1);
        putFormatItem("mfn_date", 7, "dd-mmm-yy;@", -1);
        putFormatItem("mfn_date", 8, "mmm-yy;@", -1);
        putFormatItem("mfn_date", 9, "mmmm-yy;@", -1);
		putFormatItem("mfn_date", 10, "mmmm d, yyyy;@", -1);
        putFormatItem("mfn_date", 11, "m/d/yy h:mm AM/PM;@", -1);
        putFormatItem("mfn_date", 12, "m/d/yy h:mm;@", -1);
        putFormatItem("mfn_date", 13, "mmmmm;@", -1);
		putFormatItem("mfn_date", 14, "mmmmm-yy;@", -1);
		putFormatItem("mfn_date", 15, "m/d/yyyy;@", -1);  
        putFormatItem("mfn_date", 16, "d-mmm-yyyy;@", -1);

        // mfn_time
		putFormatItem("mfn_time", 0, "h:mm:ss AM/PM", -1);
		putFormatItem("mfn_time", 1, "h:mm;@", -1);
		putFormatItem("mfn_time", 2, "h:mm AM/PM;@", -1);
		putFormatItem("mfn_time", 3, "h:mm:ss;@", -1);
		putFormatItem("mfn_time", 4, "h:mm:ss AM/PM;@", -1);
		putFormatItem("mfn_time", 5, "mm:ss.0;@", -1);
		putFormatItem("mfn_time", 6, "[h]:mm:ss;@", -1);
		putFormatItem("mfn_time", 7, "m/d/yy h:mm AM/PM;@", -1);
		putFormatItem("mfn_time", 8, "m/d/yy h:mm;@", -1);

		// mfn_percentage
		putFormatItem("mfn_percentage", 0, "0.00%", 14);
		
		// mfn_fraction
		putFormatItem("mfn_fraction", 0, "# ?/?", 17);
		putFormatItem("mfn_fraction", 1, "# ??/??", 18);
		putFormatItem("mfn_fraction", 2, "# ???/???", -1);
		putFormatItem("mfn_fraction", 3, "# ?/2", -1);
		putFormatItem("mfn_fraction", 4, "# ?/4", -1);
		putFormatItem("mfn_fraction", 5, "# ?/8", -1);
		putFormatItem("mfn_fraction", 6, "# ??/16", -1);
		putFormatItem("mfn_fraction", 7, "# ?/10", -1);
		putFormatItem("mfn_fraction", 8, "# ??/100", -1);

		// mfn_scientific
		putFormatItem("mfn_scientific", 0, "0.00E+00", 15);
		
		// mfn_text
		putFormatItem("mfn_text", 0, "@", 30);
		
		// mfn_special
		putFormatItem("mfn_special", 0, "00000", -1);
		putFormatItem("mfn_special", 1, "00000-0000", -1);
		putFormatItem("mfn_special", 2, "[<=9999999]###-####;(###)###-####", -1);
		putFormatItem("mfn_special", 3, "000-00-0000", -1);
	}
	
	protected String formatCode;
	protected Color backColor;
	protected Color fillColor;
	protected FillPattern pattern;
	protected int rotation;
	protected String[] categories = { "mfn_general", "mfn_number",
			"mfn_currency", "mfn_accounting", "mfn_date", "mfn_time",
			"mfn_percentage", "mfn_fraction", "mfn_scientific", "mfn_text",	"mfn_special", "mfn_custom" };
	protected ListModelList<InnerFillPattern> patternListmodel = new ListModelList<InnerFillPattern>(InnerFillPattern.values());
	protected boolean patternChanged = false;
	protected ListModelList<InnerRotation> rotationListmodel = new ListModelList<InnerRotation>(InnerRotation.values());

	@Wire
	protected Listbox mfn_category;
	@Wire
	protected Listbox mfn_general;
	@Wire
	protected Component mfn_custom_box;
	@Wire
	protected Textbox mfn_custom_value;
	@Wire
	protected Listbox selectedCategory;
	
	@Wire
	protected Colorbox backColorbox;
	@Wire
	protected Colorbox fillColorbox;
	@Wire
	protected Combobox patternCombobox;
	@Wire
	protected Combobox rotationCombobox;
	
	public static void show(EventListener<DialogCallbackEvent> callback, 
			String formatCode, Color backColor, Color fillColor, FillPattern fillPattern, int rotation, Spreadsheet ss) {
		Map arg = newArg(callback);
		arg.put(ARG_FORMAT_CODE, formatCode);
		arg.put(ARG_BACKGROUND_CODE, backColor);
		arg.put(ARG_FILL_COLOR_CODE, fillColor);
		arg.put(ARG_FILL_PATTERN_CODE, fillPattern);
		arg.put(ARG_ROTATION_CODE, rotation);
		
		arg.put(ARG_SPREADSHEET, ss);
		
		Window comp = (Window)Executions.createComponents(URI, null, arg);
		comp.doModal();
		return;
	}
	
	public void doAfterCompose(Window comp) throws Exception {
		super.doAfterCompose(comp);
		
		Map arg = Executions.getCurrent().getArg();
		
		formatCode = (String)arg.get(ARG_FORMAT_CODE);
		backColor = (Color) arg.get(ARG_BACKGROUND_CODE);
		fillColor = (Color) arg.get(ARG_FILL_COLOR_CODE);
		pattern = (FillPattern) arg.get(ARG_FILL_PATTERN_CODE);
		rotation = (Integer) arg.get(ARG_ROTATION_CODE);
		
		backColorbox.setColor(backColor.getHtmlColor());
		fillColorbox.setColor(fillColor.getHtmlColor());

		patternListmodel.addToSelection(InnerFillPattern.values()[pattern.ordinal()]);
		patternListmodel.sort(new Comparator<FormatCellCtrl.InnerFillPattern>() {
			@Override
			public int compare(InnerFillPattern o1, InnerFillPattern o2) {
				return o1.order - o2.order;
			}
		}, true);
		patternListmodel.addListDataListener(new ListDataListener() {
			@Override
			public void onChange(ListDataEvent arg0) {
				patternChanged = true;
			}
		});
		patternCombobox.setModel(patternListmodel);
		
		rotationListmodel.addToSelection(InnerRotation.getRotation(rotation));
		rotationCombobox.setModel(rotationListmodel);

		mfn_custom_value.setValue(formatCode);
		final FormatItem fi = FORMAT_ITEMS.get(formatCode);
		if (fi == null) {
			openFormatList("mfn_custom", -1);
		} else {
			openFormatList(fi.category, fi.categoryIndex);
		}
	}

	@Listen("onSelect=Listbox[id^=mfn_") // all Listbox with id starts with "mfn_"
	public void onSelect$mfn_category(SelectEvent event) {
		if ("mfn_category".equals(event.getTarget().getId())) {
			// pick name of the selected category
			String cat = (String)mfn_category.getSelectedItem().getValue();
			final FormatItem fi = FORMAT_ITEMS.get(formatCode);
			int index = -1;
			if ("mfn_custom".equals(cat)) {
				// in custom list, use index in mfn_custom listbox
				index = fi == null ? -1 : fi.customIndex;
			} else {
				// otherwise if same category; restore selection; otherwise pick 0
				index = fi == null ? 0 : fi.category.equals(cat) ? fi.categoryIndex : 0; 
			}
			openFormatList(cat, index);
		} else
			selectOption((Listbox)event.getTarget());
	}
	
	protected void selectOption(Listbox lb) {
		if (lb != null) {
			final Listitem li = lb.getSelectedItem();
			if (li != null) {
				mfn_custom_value.setValue(formatCode = (String)li.getValue());
				Clients.scrollIntoView(li);
			}
		}
	}
	protected void openFormatList(String category, int index) {
		mfn_custom_box.setVisible("mfn_custom".equals(category));
		for (int i = 0; i < categories.length; i++){
			final Listitem catli = mfn_category.getItemAtIndex(i);
			final String cat0 = (String) catli.getValue();
			Listbox lb = (Listbox) getSelf().getFellow(cat0);
			if(lb != null){
				if(category.equals(cat0)){
					lb.setSelectedIndex(index);
					selectedCategory = lb;
					mfn_category.setSelectedItem(catli);
					selectOption(lb);
					lb.setVisible(true);
				} else {
					lb.setVisible(false);
				}
			}				
		}
	}

	@Listen("onOK=window; onClick=#okBtn")
	public void onClick$okBtn() {
		Listitem  seldItem = mfn_category.getSelectedItem();
		if (seldItem == null) {
			showSelectFormatDialog();
			return;
		}
		if (selectedCategory == null) {
			showSelectFormatDialog();
			return;
		}
		
		String formatCodes = mfn_custom_value.getValue();
		String backColor = backColorbox.getColor();
		String fillColor = fillColorbox.getColor();
		FillPattern fillPattern = FillPattern.values()[patternListmodel.getSelection().iterator().next().ordinal()];
		int rotation = rotationListmodel.getSelection().iterator().next().getRotation();

		detach();
		postCallback(ON_OK, newMap(
				newEntry(ARG_FORMAT_CODE, formatCodes),
				newEntry(ARG_BACKGROUND_CODE, backColor),
				newEntry(ARG_FILL_COLOR_CODE, fillColor),
				newEntry(ARG_FILL_PATTERN_CODE, fillPattern),
				newEntry(ARG_ROTATION_CODE, rotation)));
	}

	protected void showSelectFormatDialog() {
		Messagebox.show(Labels.getLabel("zssex.dlg.format.select_format_code"));
	}
	
	@Listen("onChange=#backColorbox")
	public void onBackColorChange() {
		if(!patternChanged && patternListmodel.getSelection().iterator().next() == InnerFillPattern.NONE) {
			patternListmodel.clearSelection();
			patternListmodel.addToSelection(InnerFillPattern.SOLID);
		}
	}
	
	@Listen("onCancel=window; onClick=#cancelBtn")
	public void onCancel() {
		detach();
		postCallback(ON_CANCEL, null);
	}
		
	public enum InnerFillPattern{
		NONE(Labels.getLabel("zssex.dlg.format.fill.none"), 0), //NO_FILL
		SOLID(Labels.getLabel("zssex.dlg.format.fill.solid"), 1), //SOLID_FOREGROUND
		MEDIUM_GRAY(Labels.getLabel("zssex.dlg.format.fill.mediumGray"), 3), //FINE_DOTS
		DARK_GRAY(Labels.getLabel("zssex.dlg.format.fill.darkGray"), 2), //ALT_BARS
		LIGHT_GRAY(Labels.getLabel("zssex.dlg.format.fill.lightGray"), 4), //SPARSE_DOTS
		DARK_HORIZONTAL(Labels.getLabel("zssex.dlg.format.fill.darkHorizontal"), 11), //THICK_HORZ_BANDS
		DARK_VERTICAL(Labels.getLabel("zssex.dlg.format.fill.darkVertical"), 12), //THICK_VERT_BANDS
		DARK_DOWN(Labels.getLabel("zssex.dlg.format.fill.darkDown"), 13), //THICK_BACKWARD_DIAG
		DARK_UP(Labels.getLabel("zssex.dlg.format.fill.darkUp"), 14), //THICK_FORWARD_DIAG
		DARK_GRID(Labels.getLabel("zssex.dlg.format.fill.darkGrid"), 15), //BIG_SPOTS
		DARK_TRELLIS(Labels.getLabel("zssex.dlg.format.fill.darkTrellis"), 16), //BRICKS
		LIGHT_HORIZONTAL(Labels.getLabel("zssex.dlg.format.fill.lightHorizontal"), 21), //THIN_HORZ_BANDS
		LIGHT_VERTICAL(Labels.getLabel("zssex.dlg.format.fill.lightVertical"), 22), //THIN_VERT_BANDS
		LIGHT_DOWN(Labels.getLabel("zssex.dlg.format.fill.lightDown"), 23), //THIN_BACKWARD_DIAG
		LIGHT_UP(Labels.getLabel("zssex.dlg.format.fill.lightUp"), 24), //THIN_FORWARD_DIAG
		LIGHT_GRID(Labels.getLabel("zssex.dlg.format.fill.lightGrid"), 25), //SQUARES
		LIGHT_TRELLIS(Labels.getLabel("zssex.dlg.format.fill.lightTrellis"), 26), //DIAMONDS
		GRAY125(Labels.getLabel("zssex.dlg.format.fill.gray125"), 5), //LESS_DOTS 
		GRAY0625(Labels.getLabel("zssex.dlg.format.fill.gray0625"), 6); //LEAST_DOTS
		
		String label;
		int order;
		
		InnerFillPattern(String label, int order) {
			this.label = label;
			this.order = order;
		}

		@Override
		public String toString() {
			return label;
		}
	}
	
	public enum InnerRotation{		
		HORIZONTAL(Labels.getLabel("zssex.dlg.format.alignment.horizontal"), 0),
		VERTIACAL(Labels.getLabel("zssex.dlg.format.alignment.vertical"), 255),
		NINETY_DEGREE(Labels.getLabel("zssex.dlg.format.alignment.ninetydeg"), 90),
		NEGATIVE_NINETY_DEGREE(Labels.getLabel("zssex.dlg.format.alignment.negativeninetydeg"), -90); //ZSS-1020
		
		
		String label;
		int rotation;
		
		InnerRotation(String label, int rotation) {
			this.label = label;
			this.rotation = rotation;
		}

		public static InnerRotation getRotation(int rotation) {
			for(InnerRotation r : values()) {
				if(r.rotation == rotation)
					return r;
			}
			return null;
		}
		
		public int getRotation() {return rotation;}

		@Override
		public String toString() {
			return label;
		}
	}

	//ZSS-879
	protected static void putFormatItem(String c, int i, String f, int ci) {
		FORMAT_ITEMS.put(f, new FormatItem(c, i, f, ci));
	}
	//ZSS-879
	protected static class FormatItem {
		protected final String category;	// which category
		protected final int categoryIndex;	// index in category
		protected final String format;	// format text
		protected final int customIndex;  // index in mfn_custom listbox
		
		FormatItem(String c, int i, String f, int ci) {
			category = c;
			categoryIndex = i;
			format = f;
			customIndex = ci;
		}
	}
}