/* eslint-disable */

return function (vm, lang, ma, $timeout) {
	var grid,
		gridOnDblClick,
		gridOnRedraw,
		gridOnSelectedRowsChangedCallback,
		gridOnViewportChanged,
		l,
		loadParam,
		loadRows,
		mainScope,
		onSort,
		out,
		preloadCount,
		prevOut,
		prevTable,
		rowLoadBatchHalfSize,
		rowUpdateCalled,
		rowViewportPrev,
		rowsInSelection,
		rowsInTable,
		rowsLoaded,
		rowsLoadedArr,
		saveParam,
		selectedIdArr,
		setQueryField,
		table,
		updateSortIndicator,
		updateToolbar,
		viewportChanged
	vm.l = lang.Current // in pug you can use: "{{ l('Add') }}"
	l = lang.Current
	vm.ma = ma
	vm.window = window
	mainScope = ma.mainScope()
	vm.popup = {}
	vm.rec = {}
	vm.popup.tab = [
		{
			"value": "",
			"show": "            " // contains non-braking-spaces to show tab initial value to prevent flickering
		}
	]
	vm.rec.tab = vm.popup.tab[0].value // form name
	grid = null
	out = null // use loadParam() later
	prevOut = null // use SaveParam() when out changes
	prevTable = ""
	table = ""
	selectedIdArr = function () {
		var colName,
			idArr,
			output,
			ref,
			ref1,
			sel,
			selected
		if (!table || table === "") {
			return
		}
		if (((ref = vm.grid) != null ? (ref1 = ref.output) != null ? ref1.grid : void 0 : void 0) == null) {
			// this is ok here, grid may not be drawn yet
			return null
		}
		output = vm.grid.output
		if (!output.data || output.data.length < 1) {
			return
		}
		sel = output.grid.getSelectedRows().sort(ma.compareNumbers)
		// console.log("selectedIdArr", sel)
		colName = table + "_record_id"
		if (output.data[0][colName] == null) {
			colName = "id"
		}
		if (output.data[0][colName] == null) {
			alert(l("Field '" + colName + l("' was not found or is empty")))
			return []
		}
		// debugger
		// for i of output.data
		// output.data[i][colName] = i
		// return null
		if (sel.length < 1) {
			return []
		}
		// sel = ma.range(0, data.length-1) # select all
		// alert(l "Select some rows before this action")
		selected = function (i) { // map function, gets called once for every element of array 'sel'
			return output.data[i][colName]
		}
		idArr = sel.map(selected)
		if (idArr === null) {
			alert(l("Selection is empty"))
		}
		return idArr
	}
	saveParam = function () {
		var idArr,
			output,
			param
		if (table && table !== "") {
			idArr = selectedIdArr()
			if (idArr !== null) {
				param = {}
				param.selection = idArr // this is common for input/output/begin
				param.selection_record = 0 // 0-based array index of idArr
				ma.setParam(param,
					"ma_selection_" + table)
			}
			output = ma.getParam("ma_output")
			if (output === null) {
				output = {}
			}
			output[table] = out
			ma.setParam(output,
				"ma_output")
			return prevOut = angular.copy(out)
		}
	}
	updateToolbar = function (rec,
		pop) {
		var key,
			rec2,
			val
		rec2 = {}
		for (key in rec) {
			val = rec[key]
			if (key === "tab" || key.search("order") >= 0) {

			} else if (key === "query_field") {
				rec2.field = val
			} else {
				rec2[key] = val
			}
		}
		return mainScope.updateToolbar(rec2,
			pop)
	}
	setQueryField = function (order_field) {
		out.query_field = '["' + order_field + '"]'
		updateToolbar({
			field: out.query_field
		})
		return saveParam()
	}
	updateSortIndicator = function () {
		grid = vm.grid.output
		if (grid.setSortColumn && out.order_field !== "") {
			if (out.order_ascend === ">") {
				return grid.setSortColumn(out.order_field,
					true)
			} else if (out.order_ascend === "<") {
				return grid.setSortColumn(out.order_field,
					false)
			}
		}
	}
	loadParam = function () {
		var output,
			param
		if (!table || table === "") {
			param = ma.getParam()
			if (param && param.table) {
				table = param.table
			} else {
				table = mainScope.table() || ""
			}
		}
		if (table && table !== "") {
			output = ma.getParam("ma_output")
			if (output !== null) {
				out = output[table]
			}
		}
		if (!out) {
			out = {}
			out.order_field = ""
			out.order_ascend = ""
			out.query_field = ""
			out.operator = ""
			out.query_value1 = ""
			out.query_value2 = ""
			out.tab = ""
		}
		// param =  ma.getParam("ma_selection_" + table)
		return prevOut = angular.copy(out)
	}
	vm.init = function () {
		console.log("output: init()")
		loadParam()
		mainScope.initPlugin(vm,
			"output",
			table)
		return vm.callServer("init")
	}
	vm.triggerAction = function (action,
		option) {
		var deleteCallback,
			docName,
			docType,
			idArr,
			ok,
			param,
			printCallback,
			save,
			selectionToStructureCallback
		param = {}
		if (!option || option !== "no-selection") {
			idArr = selectedIdArr()
			if (idArr === null || idArr.length < 1) {
				alert("select record(s) for action: " + action)
				return
			}
			param.selection = idArr
		}
		if (option) {
			param.option = option
		}
		param.table = table
		console.log("output: triggerMenu()",
			action,
			param)
		if (action === "delete") {
			ok = false
			if (idArr.length === 1) {
				ok = confirm("Do you really want to delete 1 record?")
			} else {
				ok = confirm("Do you really want to delete " + idArr.length + " records?")
			}
			if (ok) {
				out.tab = vm.rec.tab
				deleteCallback = function (ret) {
					return vm.callServer("query",
						out.tab)
				}
				return ma.callServer(vm,
					action,
					param,
					deleteCallback)
			}
		} else if (action.search("download") === 0) {
			selectionToStructureCallback = function (ret) {
				var txt
				txt = angular.toJson(ret.selection)
				return mainScope.showTextDialog(txt)
			}
			return ma.callServer(vm,
				action,
				param,
				selectionToStructureCallback)
			// else if action is "upload"
			// ma.callServer(vm, action, param, selectionToStructureCallback)
		} else if (action.search("print") === 0) {
			docName = option.report_name
			docType = option.document_type
			save = option.save || false
			printCallback = function (ret) {
				/*
				if ma.isSafari()
					mainScope.showPictureDialog(data)
				else
					blob = new Blob([data], {'type': "image/svg+xml"})
					url = URL.createObjectURL(blob)
					win = window.open(url, docName)
					win.focus()
				*/
				var blob,
					data,
					ref,
					url,
					win
				data = ret != null ? (ref = ret.rec) != null ? ref.picture : void 0 : void 0
				if (!data) { // show error message?
					return
				}
				docName = docName + "." + docType
				if (docType === "svg") {
					return mainScope.showPictureDialog(data)
				} else {
					blob = ma.base64toBlob(data,
						"application/" + docType)
					if (save || ma.isSafari()) {
						// window.location = url
						return saveAs(blob,
							docName)
					} else {
						url = URL.createObjectURL(blob)
						win = window.open(url,
							docName) // , title ,'_blank')
						// win.document.write("<title>" + docName + "." + docType + "</title>")
						if (win) {
							return win.focus()
						} else {
							return ma.info(vm,
								"Opening window for print failed, allow popup windows for this url to see report in browser.")
						}
					}
				}
			}
			return ma.callServer(vm,
				action,
				param,
				printCallback)
		} else {
			return ma.callServer(vm,
				action,
				param) // , selectionToStructureCallback)
		}
	}
	vm.modifyRecord = function () {
		var url
		saveParam()
		url = table + "_input1"
		console.log(url)
		return ma.gotoUrl("input/modify")
	}
	gridOnSelectedRowsChangedCallback = function (e,
		args) {
		// item = args.grid.getData()[args.row]
		// console.log('gridOnSelectedRowsChangedCallback: ', args, e) #, item)
		return saveParam()
	}
	rowsLoaded = 0
	rowsInSelection = 0
	rowsInTable = 0
	rowsLoadedArr = []
	preloadCount = 75
	rowLoadBatchHalfSize = 100
	rowViewportPrev = {}
	rowUpdateCalled = false
	gridOnDblClick = function (e,
		args) {
		var count,
			item,
			key
		// debugger
		item = args.grid.getDataItem(args.row)
		// console.log("gridOnDblClick:", args, e, item)
		count = 0
		for (key in item) {
			count++
			if (count > 1) {
				break
			}
		}
		if (count > 1) { // if item has only 1 key (id) => is virtual not-loaded row
			return vm.modifyRecord()
		}
	}
	viewportChanged = function (vp,
		prealod) {
		var bottom,
			i,
			limit,
			param,
			prevBottom,
			prevTop,
			top,
			up
		prevTop = rowViewportPrev.top
		prevBottom = rowViewportPrev.bottom
		top = vp.top
		bottom = vp.bottom
		rowViewportPrev.top = top
		rowViewportPrev.bottom = bottom
		if (!rowUpdateCalled) {
			up = top < prevTop
			if (bottom > rowsInSelection - 1) { // bottom is 0-based
				bottom = rowsInSelection - 1
			}
			i = 0
			while (i < rowsLoadedArr.length) {
				if (top >= rowsLoadedArr[i].top && bottom <= rowsLoadedArr[i].bottom) {
					if (prealod) {
						if (up && top - rowsLoadedArr[i].top <= preloadCount) {
							// start loading rows before top == has time to load before drawing
							top = rowsLoadedArr[i].top
							if (top <= 0) { // no scroll before first row
								return
							}
							break // do load
						}
						if (!up && rowsLoadedArr[i].bottom - bottom <= preloadCount) {
							bottom = rowsLoadedArr[i].bottom
							if (bottom >= rowsInSelection - 1) { // no scroll after last row
								return
							}
							break // is in loaded range, do nothing
						}
					}
					return
				}
				i = i + 1
			}
			if (i >= rowsLoadedArr.length) { // not inside any block, search near block
				i = 0
				while (i < rowsLoadedArr.length) {
					// fill small gaps
					if (up && top <= rowsLoadedArr[i].top && rowsLoadedArr[i].top - top <= rowLoadBatchHalfSize) {
						// debugger
						top = rowsLoadedArr[i].top
						break
					}
					if (!up && bottom >= rowsLoadedArr[i].bottom && bottom - rowsLoadedArr[i].bottom <= rowLoadBatchHalfSize) {
						// debugger
						bottom = rowsLoadedArr[i].bottom
						break
					}
					i = i + 1
				}
			}
			rowUpdateCalled = true
			console.log("gridOnViewportChanged:",
				top,
				rowViewportPrev.top,
				bottom,
				rowViewportPrev.bottom)
			param = {}
			param.add_to_grid = {}
			param.add_to_grid.up = up
			limit = 0 // set limit, bottom -> next top
			if (i >= rowsLoadedArr.length) {
				// not inside any block or near any block, moved by mouse in scrollbar
				// load half of data up and half down from scroll point because we don't know the next direction
				param.add_to_grid.offset = top - 1 - rowLoadBatchHalfSize
			} else if (up) {
				param.add_to_grid.offset = top - 1
			} else {
				param.add_to_grid.offset = bottom + 1
			}
			param.add_to_grid.limit = limit
			vm.callServer("load",
				null,
				param)
		}
	}
	gridOnViewportChanged = function (e,
		args) {
		var i,
			vp
		vp = args.grid.getViewport()
		i = 0
		return viewportChanged(vp,
			true)
	}
	loadRows = function (param) {
		var bottom,
			i,
			top
		if (vm.grid && vm.grid.output) {
			vm.grid.output.table = table
			if (!vm.grid.output.callback) {
				vm.grid.output.callback = {}
				vm.grid.output.callback.onSelectedRowsChanged = gridOnSelectedRowsChangedCallback
				vm.grid.output.callback.onDblClick = gridOnDblClick
				vm.grid.output.callback.onViewportChanged = gridOnViewportChanged
				vm.grid.output.callback.onRedraw = gridOnRedraw
				vm.grid.output.callback.onSort = onSort
			}
		}
		if (param.load_batch_size) {
			// debugger
			rowLoadBatchHalfSize = Math.floor(param.load_batch_size / 2)
			if (param.preload_count) {
				preloadCount = param.preload_count
			}
		}
		if (param.info) {
			rowsInSelection = param.info.rowCountTotal
			rowsInTable = param.info.rowCountTable
			if (rowsLoaded === 0 && param.grid && param.grid.output && param.grid.output.data) {
				rowsLoaded = param.grid.output.data.length // param.info.rowCount
				rowsLoadedArr = []
				rowsLoadedArr.push({
					top: 0,
					bottom: rowsLoaded - 1
				})
			}
			if (param.add_to_grid && param.grid_data.length > 0) {
				grid.beginUpdate()
				top = param.add_to_grid.offset // array index is 0-based
				i = 0
				while (i < param.grid_data.length) {
					grid.data[top + i] = param.grid_data[i]
					grid.data[top + i].id = top + i + 1 // id is 1-based
					i++
				}
				bottom = top + i - 1
				grid.endUpdate()
				grid.updateRowCount()
				// debugger
				grid.refreshRows()
				i = 0
				while (i < rowsLoadedArr.length) { // make previous block bigger
					if (top <= rowsLoadedArr[i].bottom + 1 && bottom >= rowsLoadedArr[i].top - 1) {
						// debugger
						if (top < rowsLoadedArr[i].top) {
							rowsLoadedArr[i].top = top
						}
						if (bottom > rowsLoadedArr[i].bottom) {
							rowsLoadedArr[i].bottom = bottom
						}
						break
					} else if (bottom < rowsLoadedArr[i].top) {
						rowsLoadedArr.splice(i,
							0,
							{
								top: top,
								bottom: bottom // insert in array
							})
						break
					}
					i++
				}
				if (i >= rowsLoadedArr.length) { // new block
					rowsLoadedArr.push({
						top: top,
						bottom: bottom
					})
				}
				rowsLoaded = rowsLoaded + param.grid_data.length
				rowUpdateCalled = false
				// check if current viewport is loaded after previous load
				if (rowViewportPrev.top) {
					viewportChanged(rowViewportPrev,
						false)
				}
			}
			rowUpdateCalled = false // must be also here to recover from errors
			// debugger
			if (param.rec) {
				if (param.rec.order_field && param.rec.order_ascend) {
					if (param.rec.order_field !== out.order_field || param.rec.order_ascend !== out.order_ascend) {
						out.order_ascend = param.rec.order_ascend
						out.order_field = param.rec.order_field
						updateSortIndicator()
					}
				}
			}
			if (!param.add_to_grid && grid && grid.redrawRows) {
				return grid.redrawRows()
			}
		}
	}
	// grid.refreshRows()
	onSort = function (field,
		ascend) {
		out.order_field = field
		if (ascend) {
			out.order_ascend = ">"
		} else {
			out.order_ascend = "<"
		}
		ma.deleteParam("ma_selection_" + table) // saveParam()
		if (rowsLoaded === rowsInSelection) { // false and
			return true // better to use db sort always
		}
		vm.callServer("load",
			null)
		return false // do not let grid do it's default sort
	}
	gridOnRedraw = function (area,
		newScopeData) {
		var add
		updateSortIndicator()
		if (rowsInSelection > rowsLoaded && newScopeData.data.length < rowsInSelection) {
			console.log("gridOnRedraw change:",
				rowsLoaded,
				rowsInSelection,
				newScopeData.data.length)
			add = rowsInSelection - rowsLoaded
			if (newScopeData.data.length > rowsLoaded) {
				add = rowsInSelection - newScopeData.data.length
			}
			return add
		}
		return null
	}
	vm.query = function (keyCode,
		shiftKey) {
		if (!keyCode || keyCode === 13) {
			out.query_field = mainScope.toolbar.rec.field
			out.operator = mainScope.toolbar.rec.operator
			out.query_value1 = mainScope.toolbar.rec.query_value1 || ""
			out.query_value2 = mainScope.toolbar.rec.query_value1 || ""
			vm.callServer("query")
			return true // run event.preventDefault()
		}
		return false
	}
	return vm.callServer = function (action,
		tabRec,
		option) {
		var callb,
			key,
			param,
			return_object,
			val
		callb = function (param) {
			var mainParam,
				pop,
				rec
			prevOut = angular.copy(out)
			loadRows(param)
			// get new values from server (rec) or use previous values (out) adn refresh main toolbar
			pop = param.popup
			rec = param.rec
			if (rec) {
				if (rec.table && rec.table !== out.table) {
					out.table = rec.table
					mainParam = ma.getParam()
					if (mainParam === null) {
						mainParam = {} // do not clear other data
					}
					mainParam.table = out.table
					ma.setParam(mainParam)
				}
				if (rec.field) {
					out.query_field = rec.field
				}
				if (rec.operator) {
					out.operator = rec.operator
				}
				if (rec.query_value1) {
					out.query_value1 = rec.query_value1
				}
				if (rec.query_value2) {
					out.query_value2 = rec.query_value2
				}
				if (!ma.equals(out,
					prevOut)) {
					saveParam()
				}
				// $timeout ->
				rec = angular.copy(out)
				rec.rowsLoaded = rowsLoaded
				rec.rowsInSelection = rowsInSelection
				rec.rowsInTable = rowsInTable
				return updateToolbar(rec,
					pop)
			}
		}
		param = {}
		param.rec = {}
		return_object = []
		if (action === "init") {
			return_object.push("load_batch_size")
			return_object.push("grid")
			return_object.push("popup.tab")
			if (!mainScope.toolbar.popup.table || mainScope.toolbar.popup.table.length === 0) {
				return_object.push("popup.table")
			}
			if (!mainScope.toolbar.popup.operator || mainScope.toolbar.rec.operator === "") {
				return_object.push("popup.operator")
			}
			if (table !== prevTable || table === "") {
				prevTable = table
				return_object.push("popup.field")
			}
			action = "query"
		}
		// else if action is "query" or action is "load"
		if (action === "query" || action === "tab") {
			rowsLoaded = 0
		}
		return_object.push("grid")
		if (out.order_field) {
			param.order_field = out.order_field
			param.order_ascend = out.order_ascend
		}
		if (tabRec) { // called by clicking the tab
			vm.rec.tab = tabRec
		}
		param.rec.tab = vm.rec.tab || ""
		param.rec.field = out.query_field
		param.rec.operator = out.operator
		param.rec.value1 = out.query_value1
		param.rec.value2 = out.query_value2
		out.tab = param.rec.tab
		if (!param.rec.table) {
			param.rec.table = table
		}
		param.return_object = return_object
		param.action = action
		// param.action = action
		if (!ma.equals(out,
			prevOut)) {
			saveParam()
		}
		if (option) {
			for (key in option) {
				val = option[key]
				param[key] = val
			}
		}
		return ma.callServer(vm,
			"output",
			param,
			callb)
	}
}
