/* autofilter.js

	Purpose:
		
	Description:
		
	History:
		Thu May 12 16:08:02     2011, Created by henrichen

Copyright (C) 2011 Potix Corporation. All Rights Reserved.
*/
(function () {
	var zsscmp$overrode = {},
		zssrow$overrode = {},
		zsscbc$overrode = {},
		zsssht$overrode = {},
		zssdp$overrode = {};

zk.override(zss.Spreadsheet.prototype, zsscmp$overrode, {
	//ZSS-988
	setTableFilters: function (tbafs) {
		var sheet = this.sheetCtrl;
		if (sheet) {
			for (var tbname in tbafs) {
				var af = tbafs[tbname];
				this._prepareAutoFilter(sheet, this._tableFilters ? this._tableFilters[tbname] : null, af);
			}
		}
		this._tableFilters = tbafs;
	},
	//ZSS-988
	_prepareAutoFilter: function (sheet, oldaf, af) {
		var oldrowidx = oldaf ? oldaf.range.top : -1,
			oldcolidx = oldaf ? oldaf.range.left : -1,
			oldcolidx2 = oldaf ? oldaf.range.right : -1;
		
		// ZSS-555: should remove old filter if exists
		if (oldaf) { 
			var rowobjs = oldrowidx >= 0 ? sheet.getRow(oldrowidx, oldcolidx) : null;
			if (rowobjs) {
				for(var j = 0, len = rowobjs.length; j < len; ++j) {
					var rowobj = rowobjs[j];
					if (rowobj)
						rowobj.cleanFilterBtns(oldcolidx, oldcolidx2);
					else
						break;
				}
			}
		}
		
		// add new one if exists; otherwise update old _autoFilter
		if (af) {
			var rowidx = af.range.top,
				colidx = af.range.left,
				rowobjs = sheet.getRow(rowidx, colidx);
			if (rowobjs)
				for(var j = 0, len = rowobjs.length; j < len; ++j) {
					var rowobj = rowobjs[j];
					if (rowobj)
						rowobj.prepareFilterBtns_(af);
					else
						break;
				}
			
		}
	},
	//ZSS-988
	setATableFilter: function (tbafs0) {
		var tbafs = jq.evalJSON(tbafs0); //ZSS-985: consider table filter
		var sheet = this.sheetCtrl;
		if (sheet) {
			for (var tbname in tbafs) {
				var af = tbafs[tbname];
				this._prepareAutoFilter(sheet, this._tableFilters ? this._tableFilters[tbname] : null, af);
				if (af)
					this._tableFilters[tbname] = af;
				else
					delete this._tableFilters[tbname];
			}
		}
	},
	setAutoFilter: function (af) {
		var sheet = this.sheetCtrl;
		if (sheet) {
			this._prepareAutoFilter(sheet, this._autoFilter, af);
		}
		this._autoFilter = af;
	},
	getAutoFilter: function () {
		return this._autoFilter;
	},
	//ZSS-988
	getTableFilters: function () {
		return this._tableFilters;
	},
	setAutoFilterPopup: function (afp) {
		var sheet = this.sheetCtrl;
		if (sheet) {
			sheet.openAutoFilterPopup_(afp);
		}
	},
	setDataValidations: function (dvs) { 
		var sheet = this.sheetCtrl;
		if (sheet) {
			// ZSS-648: clear all old data validations
			this._clearDataValidations();
			
			// ZSS-648: then add all new data validations
			// Use dv.id to manage the dvs
			var ndvs = dvs ? {} : null;
			var len = (dvs ? dvs.length : 0);
			for (var j = 0; j < len; ++j) {
				var dv = dvs[j];
				this._setDataValidation(dv, dv.id);
				ndvs[dv.id] = dv;
			}
			this._dataValidations = ndvs;
			this._dvs = dvs; //ZSS-934
			
			// ZSS-648: in case the focus is now on showValidateBtn_; 
			// must update its status
			var dp = sheet.dp,
				pos = sheet.getLastFocus();
			
			if (dp && pos)
				dp.showValidateBtn_(pos.row, pos.column);
		}
	},
	_setDataValidation: function (dv, dvj) { //@see Spreadsheet.java#convertDataValidationToJSON(DataValidation dv);
		if (dv.showPrompt) {
			this.sheetCtrl.addValidationPromptPopup_(dvj, dv.promptTitle, dv.promptText);
		}
		
		var lists = dv.lists,
			len0 = (lists ? lists.length : 0);
		// for each List
		for (var listj = 0; listj < len0; ++listj) {
			var list = lists[listj],
				rngs = list ? list.rangeList : null,
				len = (rngs ? rngs.length : 0);
			for (var j = 0; j < len; ++j) {
				var range = rngs[j];
				this._setValidateBtns(dvj, listj, range);
			}
			if (list.validationList) {
				this.sheetCtrl.addValidatePopup_(dvj, listj, list.validationList);
			}
		}
		
	},
	// ZSS-648: should handle column range
	_setValidateBtns: function (dvj, listj, range) {
		var l = range.left,
			r = range.right,
			t = range.top,
			b = range.bottom,
			sheet = this.sheetCtrl;
		for (var row = t; row <= b; ++row) {
			var rowobjs = sheet.getRow(row, col);
			if (rowobjs)
				for(var j = 0, len = rowobjs.length; j < len; ++j) {
					var rowobj = rowobjs[j];
					if (rowobj) {
						for (var col = l; col <= r; ++col) {
							rowobj.prepareDataValidation_(dvj, listj, col);
						}
					} else
						break;
				}
		}
	},
	getDataValidations: function () {
		return this._dvs; //ZSS-934
	},
	_clearDataValidations: function () {
		var sheet = this.sheetCtrl,
			dvs = this._dataValidations;
		if (sheet && dvs) {
			for (var id in dvs) {
				this._clearDataValidation(dvs[id], id);
			}
			this._dataValidations = null;
			this._dvs = null; //ZSS-934
		}
	},
	_clearDataValidation: function (dv, dvj) {
		//ZSS-648
		if (dv.showPrompt) {
			this.sheetCtrl.clearValidationPromptPopup_(dvj);
		}
		var lists = dv.lists,
		    len0 = (lists ? lists.length : 0);
		for (var listj = 0; listj < len0; ++listj) {
			var list = lists[listj],
				rngs = list ? list.rangeList : null,
				len = (rngs ? rngs.length : 0);
			for (var j = 0; j < len; ++j) {
				var range = rngs[j];
				this._clearValidateBtns(range);
			}
			
			if (list.validationList) {
				this.sheetCtrl.clearValidatePopup_(dvj, listj);
			}
		}
	},
	_clearValidateBtns: function (range) {
		var l = range.left,
			r = range.right,
			t = range.top,
			b = range.bottom,
			sheet = this.sheetCtrl;
		for (var row = t; row <= b; ++row) {
			var rowobjs = sheet.getRow(row, col);
			if (rowobjs)
				for(var j = 0, len = rowobjs.length; j < len; ++j) {
					var rowobj = rowobjs[j];
					if (rowobj) { // ZSS-648: should handle column range
						for (var col = l; col <= r; ++col) {
							rowobj.cleanvbtns(col);
						}
					} else
						break;
				}
		}
	}
});

zk.override(zss.DataPanel.prototype, zssdp$overrode, {
	moveFocus: function(row, col, scroll, selection, noevt, noslevt) {	
		zssdp$overrode.moveFocus.apply(this, arguments);
		this.showValidateBtn_(row, col);
	},
	_gainFocus: function (trigger, noevt, noslloc) {
		zssdp$overrode._gainFocus.apply(this, arguments);
		var pos = this.sheet.getLastFocus();
		if (pos)
			this.showValidateBtn_(pos.row, pos.column);
	},
	showValidateBtn_: function(row, col) {
		var sheet = this.sheet;
		sheet.cleanValidateBtns_();
		var rowobjs = sheet.getRow(row, col);
		if (rowobjs)
			for(var j = 0, len = rowobjs.length; j < len; ++j) {
				var rowobj = rowobjs[j];
				if (rowobj) {
					var dv = rowobj.getDataValidation_(col);
					if (dv) {
						if (dv.showButton) {
							var vbtn = rowobj.showValidateBtn_(col);
							if (vbtn) {
								sheet.addValidateBtn_(vbtn);
							}
						}
						var prompt = sheet.getValidationPromptPopup_(dv.id);
						if (prompt) {
							prompt.open(row, col);
						}
					}
				}
			}
	}
});

zk.override(zss.Row.prototype, zssrow$overrode, {
	$init: function (sheet, block, row, src) {
		zssrow$overrode.$init.apply(this, arguments);
		this.btns = {}; //filter button
		this.vbtns = {}; //validation drop down button index of DataValidation in List<DataValidation>
	},
	_prepareValidateBtns: function () {
		var dvs = this.sheet._wgt._dataValidations;
		if (dvs) {
			for (var dvj in dvs) {
				var dv = dvs[dvj];
				if (dv) {
					var lists = dv.lists,
						len0 = (lists ? lists.length : 0);
					//ZSS-934 for each List
					for (var listj = 0; listj < len0; ++listj) {
						var list = lists[listj],
							rngs = list ? list.rangeList : null,
							rngLen = (rngs ? rngs.length : 0);
						for (var i = 0; i < rngLen; ++i) {
							var range = rngs[i],
								row = this.r,
								t = range.top,
								b = range.bottom,
								l = range.left,
								r = range.right;
							if (row >= t && row <= b) {
								for (var col = l; col <= r; col++) {
									this.prepareDataValidation_(dvj, listj, col);
								}
							}
						}
					}
				}
			}
		}
	},
	//ZSS-988
	_prepareAllFilterBtns: function () {
		// sheet's auto filter
		var af = this.sheet._wgt._autoFilter;
		this._prepareAutoFilterBtns(af);
		
		// sheet's table filter
		var tbafs = this.sheet._wgt._tableFilters;
		for (var tbname in tbafs) {
			this._prepareAutoFilterBtns(tbafs[tbname]);
		}
	},
	_prepareAutoFilterBtns: function (af) {
		if (af) {
			var rowidx = af.range.top;
			if (rowidx == this.r) {
				this.prepareFilterBtns_(af);
			}
		}
	},
	bind_: function () {
		zssrow$overrode.bind_.apply(this, arguments);
		this._prepareValidateBtns();
		this._prepareAllFilterBtns();
	},
	unbind_: function () {
		zssrow$overrode.unbind_.apply(this, arguments);
		this.cleanbtns();
		this.cleanvbtns();
	},
	cleanbtns: function () {
		if (this.btns) {
			for(var key in this.btns) {
				var btn = this.btns[key];
				if (!btn)
					break;
				btn.cleanup();
			}
			this.btns = {};
		}
	},
	//ZSS-988
	cleanFilterBtns: function(col1, col2) {
		if (this.btns) {
			for (var c = col1; c <= col2; ++c) {
				var key = 'c'+c,
					btn = this.btns[key];
				if (!btn) continue;
				btn.cleanup();
				delete this.btns[key];
			}
		}
	},
	cleanvbtns: function (col) { //ZSS-648
		if (this.vbtns) {
			if (col) {
				delete this.vbtns['c'+col];
			} else {
				this.vbtns = {};
			}
		}
	},
	/**
	 * Returns the {@link zss.Btn}
	 * @param int col column index
	 * @return zss.Btn
	 */
	getBtnAt: function (col) {
		return this.btns['c'+col];
	},
	//ZSS-988
	prepareFilterBtns_: function(af) { //called by CellBlockCtrl#loadByComp()
		var row = this.r,
			rowelm = this.$n();//,
		
		if (!af) {
			return;
		}
		
		//remove old btns!
		this.cleanFilterBtns(af.range.left, af.range.right);
		
		var rng = af.range,
			afl = rng.left,
			afr = rng.right,
			fcs = af.filterColumns, //fcs can be null
			size = this.cells.length;
		
		//ZSS-1212: no cells at all; skip.
		if (size <= 0) {
			return;
		}
		
		var	left0 = this.cells[0].c, // ZSS-1212: row limit left
			right0 = this.cells[size-1].c, // ZSS-1212: row limit right
			left = Math.max(afl, left0), //ZSS-1212: handle overlap
			right = Math.min(afr, right0), //ZSS-1212: handle overlap
			fcj = 0, //filter column array index
			fc = fcs && fcs.length > 0 ? fcs[fcj] : null, //filter column
			filters = null, //filters
			colorpat = null, //ZSS-1191
			valueft = null, //ZSS-1192
			dynaft = null, //ZSS-1193: SDynamicFilter
			top10ft = null, //ZSS-1193: STop10Filter
			on = true, //showButton
			field = null, //index for dropdown values
			prefc = null; //field id (merge cell can make colId != field
			
		for(var col = left; col <= right; ++col) { //ZSS-1212: handle overlap only
			if (fc && fc.col == col) {
				field = fc.field;
				var matchCol = (afl + field - 1) == col;
				filters = matchCol ? fc.filter : prefc ? prefc.filter : null;
				colorpat = matchCol ? fc.colorpat : prefc ? prefc.colorpat : null; //ZSS-1191
				valueft = matchCol ? fc.f1 : prefc ? prefc.f1 : null; //ZSS-1192
				dynaft = matchCol ? fc.dynaf : prefc ? prefc.dynaf : null; //ZSS-1193, ZSS-1234: SDynamicFilter
				top10ft = matchCol ? (fc.filterVal != undefined) : prefc ? (prefc.filterVal != undefined) : null; //ZSS-1193: STop10Filter
				on = fc.on;
				if (on)
					prefc = null;
				else if (!prefc)
					prefc = fc;
				
				if (++fcj < fcs.length)
					fc = fcs[fcj];
			} else {
				on = true;
				if (prefc) { //ZSS-705
					field = prefc.field;
					filters = prefc.filter;
					colorpat = prefc.colorpat; //ZSS-1191
					valueft = prefc.f1; //ZSS-1192
					dynaft = prefc.dynaf; //ZSS-1193, ZSS-1234: SDynamicFilter
					top10ft = prefc.filterVal != undefined; //ZSS-1193: STop10Filter
					prefc = null;
				} else {
					filters = null;
					colorpat = null; //ZSS-1191
					valueft = null; //ZSS-1192
					dynaft = null; //ZSS-1193: SDynamicFilter
					top10ft = null; //ZSS-1193: STop10Filter
					field = col - afl + 1;
				}
			}
			if (on)
				this.btns['c'+col] = new zssex.Btn(this.sheet, row, col, rowelm, (filters && filters.length > 0) 
						|| colorpat || valueft || dynaft || top10ft ? 'zsfilter' : 'zsdropdown', field, 'af', left0);
		}
	},
	prepareDataValidation_: function (dvj, listj, col) {
		var ids = {};
		this.vbtns['c'+col] = ids;
		ids["dvj"] = dvj;
		ids["listj"] = listj;
	},
	getDataValidation_: function (col) {
		var ids = this.vbtns['c'+col];
		if (ids == null)
			return null;
		var dvs = this.sheet._wgt._dataValidations;
		if (dvs)
			return dvs[ids.dvj];
	},
	showValidateBtn_: function(col) { //called by DataPanel#moveFocus()
		var ids = this.vbtns['c'+col];
		if (ids == null)
			return null;
		var left = this.cells[0].c; //row limit left; ZSS-954
		return new zssex.Btn(this.sheet, this.r, col, this.$n(), 'zsdropdown', ids, 'dv', left);
	}
});

zk.override(zss.CellBlockCtrl.prototype, zsscbc$overrode, {
	/**
	 * Returns the button associated to the cell of the spreadsheet
	 * @param int row
	 * @param int col
	 */
	getBtn: function (row, col) {
		var af = this.getFilterByRowCol(row, col),
			range = af ? af.range : null,
			r = range ? this.rows[row - this.range.top] : null;
		
		return r?r.getBtnAt(col):null;
	},
	//ZSS-988
	getFilterByRowCol: function (row, col) {
		//check sheet autoFilter
		var af = this.sheet._wgt._autoFilter;
		if (this._isValidFilter(af, row, col))
			return af;
		
		//check sheet tableFilters
		var tbafs = this.sheet._wgt._tableFilters;
		for (var tbname in tbafs) {
			var af1 = tbafs[tbname];
			if (this._isValidFilter(af1, row, col)) {
				return af1;
			}
		}
		return null;
	},
	//ZSS-988, ZSS-1045
	_isValidFilter: function (af, row, col) {
		if (!af) return null;
		var range = af.range;
		if (row != range.top || col < range.left || col > range.right)
			return null;
		return af;
	}
});

zk.override(zss.SSheetCtrl.prototype, zsssht$overrode, {
	_doBtndown: function (evt, type, element, btn) {
		var sheet = this;
		if (sheet.state == zss.SSheetCtrl.NOFOCUS) {
			sheet.dp.gainFocus();
			return;
		}
		var	wgt = sheet._wgt,
			elm = (element) ? element : evt.domTarget,
			cmp,
			mx = my = 0,//mouse offset, against body 
			shx = shy = 0,//mouse offset against sheet
			row, 
			col,
			md1 = zkS._getMouseData(evt, this.comp),
			mdstr = "";
		if ((cmp = zkS.parentByZSType(elm, "SBtn", 0)) != null) { //SBtn must before SRow because SBtn is child of SRow
			var cellcmp = cmp,
				sheetofs = zk(sheet.comp).revisedOffset(),
			//TODO there is a bug in opera, when a cell is overflow, zk.revisedOffset can get correct component offset 
				cmpofs = zk(cellcmp).revisedOffset();
			
			mx = evt.pageX;
			my = evt.pageY;
			shx = Math.round(mx - sheetofs[0]);
			shy = Math.round(my - sheetofs[1]);
			
			var x = mx - cmpofs[0],
				cellpos = zss.SSheetCtrl._calCellPos(sheet, mx, my, false, type == 'dv'); // ZSS-1266
			row = cellpos[0];
			col = cellpos[1];
			mdstr = "c_" + row + "_" + col;
			
			if ('dv' == type) {
				col -= 1; ////data validation btn align at the start of the next cell
				//ZSS-805
				var cell = sheet.getCell(row, col);
				if (cell) {
					if (cell.merl && cell.merl < col) {
						col = cell.merl;
					}
					if (cell.mert && cell.mert < row) {
						row = cell.mert;
					}
				}
				var ids = btn.field,
					popup = this.getValidatePopup_(ids.dvj, ids.listj);
				if (popup) {
					popup.open(row, col);
				}
			}
	
			wgt.fireCellEvt(type, shx, shy, md1[2], row, col, mx, my, btn.field);
		}
	},
	_syncAutoFilterBtns: function(af, row, col) { //ZSS-988
		//adjust autofilter button
		if (af) {
			if (row < 0) 
				row = af.range.top;
			if (row != af.range.top)
				return;
			if (col < 0)
				col = af.range.left;
			var right = af.range.right;
			for(var c = col; c <= right; ++c) {
				btn = this.getBtn(row, c);
				if (btn)
					btn.relocate(row, c);
			}
		}
	},
	//ZSS-988
	_syncAllFilterBtns: function(row, col) {
		var af = this._wgt._autoFilter;
		this._syncAutoFilterBtns(af, row, col);
		
		var tbafs = this._wgt._tableFilters;
		for (var tbname in tbafs) {
			var af1 = tbafs[tbname];
			this._syncAutoFilterBtns(af1, row, col);
		}
	},
	/**
	 * Returns the button associated with the cell
	 * @param int row row index
	 * @param int col column index
	 * @param zss.Btn
	 */
	getBtn: function (row, col) {
		var fzr = this.frozenRow,
			fzc = this.frozenCol,
			btn = null;
		
		if (row <= fzr && col <= fzc)
			btn = this.cp.block.getBtn(row, col);
		else if(row <= fzr) 
			btn = this.tp.block.getBtn(row, col);
		else if(col <= fzc) 
			btn = this.lp.block.getBtn(row, col);
		else 
			btn = this.activeBlock.getBtn(row, col);
		return btn;
	},
	_setRowHeight: function(row, height, fireevent, loadvis, hidden, metaid) {
		zsssht$overrode._setRowHeight.apply(this, arguments);
		//adjust autofilter button
		this._syncAllFilterBtns(row, -1); //ZSS-988
	},
	_setColumnWidth: function (col, width, fireevent, loadvis, hidden, metaid) {
		zsssht$overrode._setColumnWidth.apply(this, arguments);
		//adjust autofilter button
		this._syncAllFilterBtns(-1, col); //ZSS-988
	},
	addValidateBtn_: function (vbtn) {
		if (!this._vbtns)
			this._vbtns = [];
		if (vbtn)
			this._vbtns.push(vbtn);
	},
	cleanValidateBtns_: function () {
		if (this._vbtns) {
			for(var j = 0, len= this._vbtns.length; j < len; ++j) {
				var vbtn = this._vbtns[j];
				vbtn.cleanup();
			}
			this._vbtns = [];
		}
	},
	_index: function (dvj, listj) {
		return dvj + "_" + listj;
	},
	getValidatePopup_: function (dvj, listj) {
		if (!this._vpops)
			return null;
		return this._vpops[this._index(dvj, listj)];
	},
	addValidatePopup_: function (dvj, listj, list) {
		if (!this._vpops)
			this._vpops = {};
		if (list) {
			var w = new zssex.ValidationPopup(this);
			this.appendChild(w, true);
			w.setValidationList(list);
			this._vpops[this._index(dvj, listj)] = w;
		}
	},
	clearValidatePopup_: function (dvj, listj) { // ZSS-648
		if (!this._vpops) return;
		var index = this._index(dvj, listj);
		var w = this._vpops[index];
		if (w) this.removeChild(w);
		delete this._vpops[index];
	},
	getValidationPromptPopup_: function (dvj) {
		if (!this._vppops)
			return null;
		return this._vppops[dvj];
	},
	addValidationPromptPopup_: function (dvj, title, text) {
		if (!this._vppops)
			this._vppops = {};
		if (title || text) {
			var w = new zssex.ValidationPromptPopup(this, title, text);
			this.appendChild(w, true);
			this._vppops[dvj] = w;
		}
	},
	clearValidationPromptPopup_: function (dvj) { // ZSS-648
		if (!this._vppops) return;
		var w = this._vppops[dvj];
		if (w) this.removeChild(w);
		delete this._vppops[dvj];
	},
	openAutoFilterPopup_: function (afp) {
		var p = this._autoFilterPopup;
		if (p)
			p.detach();
		var w = this._autoFilterPopup = new zssex.AutoFilterPopup(this, afp);
		this.appendChild(w, true);
		w.open(afp.row, afp.col);
	}
});
})();
