import Module from '../../core/Module.js';

class SelectRow extends Module{
	
	constructor(table){
		super(table);
		
		this.selecting = false; //flag selecting in progress
		this.lastClickedRow = false; //last clicked row
		this.selectPrev = []; //hold previously selected element for drag drop selection
		this.selectedRows = []; //hold selected rows
		this.headerCheckboxElement = null; // hold header select element
		
		this.registerTableOption("selectable", "highlight"); //highlight rows on hover
		this.registerTableOption("selectableRangeMode", "drag");  //highlight rows on hover
		this.registerTableOption("selectableRollingSelection", true); //roll selection once maximum number of selectable rows is reached
		this.registerTableOption("selectablePersistence", true); // maintain selection when table view is updated
		this.registerTableOption("selectableCheck", function(data, row){return true;}); //check whether row is selectable
		
		this.registerTableFunction("selectRow", this.selectRows.bind(this));
		this.registerTableFunction("deselectRow", this.deselectRows.bind(this));
		this.registerTableFunction("toggleSelectRow", this.toggleRow.bind(this));
		this.registerTableFunction("getSelectedRows", this.getSelectedRows.bind(this));
		this.registerTableFunction("getSelectedData", this.getSelectedData.bind(this));
		
		//register component functions
		this.registerComponentFunction("row", "select", this.selectRows.bind(this));
		this.registerComponentFunction("row", "deselect", this.deselectRows.bind(this));
		this.registerComponentFunction("row", "toggleSelect", this.toggleRow.bind(this));
		this.registerComponentFunction("row", "isSelected", this.isRowSelected.bind(this));
	}
	
	initialize(){
		if(this.table.options.selectable !== false){
			this.subscribe("row-init", this.initializeRow.bind(this));
			this.subscribe("row-deleting", this.rowDeleted.bind(this));
			this.subscribe("rows-wipe", this.clearSelectionData.bind(this));
			this.subscribe("rows-retrieve", this.rowRetrieve.bind(this));
			
			if(this.table.options.selectable && !this.table.options.selectablePersistence){
				this.subscribe("data-refreshing", this.deselectRows.bind(this));
			}
		}
	}
	
	rowRetrieve(type, prevValue){
		return type === "selected" ? this.selectedRows : prevValue;
	}
	
	rowDeleted(row){
		this._deselectRow(row, true);
	}
	
	clearSelectionData(silent){
		this.selecting = false;
		this.lastClickedRow = false;
		this.selectPrev = [];
		this.selectedRows = [];
		
		if(silent !== true){
			this._rowSelectionChanged();
		}
	}
	
	initializeRow(row){
		var self = this,
		element = row.getElement();
		
		// trigger end of row selection
		var endSelect = function(){
			
			setTimeout(function(){
				self.selecting = false;
			}, 50);
			
			document.body.removeEventListener("mouseup", endSelect);
		};
		
		row.modules.select = {selected:false};
		
		//set row selection class
		if(self.table.options.selectableCheck.call(this.table, row.getComponent())){
			element.classList.add("tabulator-selectable");
			element.classList.remove("tabulator-unselectable");
			
			if(self.table.options.selectable && self.table.options.selectable != "highlight"){
				if(self.table.options.selectableRangeMode === "click"){
					element.addEventListener("click", this.handleComplexRowClick.bind(this, row));
				}else{
					element.addEventListener("click", function(e){
						if(!self.table.modExists("edit") || !self.table.modules.edit.getCurrentCell()){
							self.table._clearSelection();
						}
						
						if(!self.selecting){
							self.toggleRow(row);
						}
					});
					
					element.addEventListener("mousedown", function(e){
						if(e.shiftKey){
							self.table._clearSelection();
							
							self.selecting = true;
							
							self.selectPrev = [];
							
							document.body.addEventListener("mouseup", endSelect);
							document.body.addEventListener("keyup", endSelect);
							
							self.toggleRow(row);
							
							return false;
						}
					});
					
					element.addEventListener("mouseenter", function(e){
						if(self.selecting){
							self.table._clearSelection();
							self.toggleRow(row);
							
							if(self.selectPrev[1] == row){
								self.toggleRow(self.selectPrev[0]);
							}
						}
					});
					
					element.addEventListener("mouseout", function(e){
						if(self.selecting){
							self.table._clearSelection();
							self.selectPrev.unshift(row);
						}
					});
				}
			}
			
		}else{
			element.classList.add("tabulator-unselectable");
			element.classList.remove("tabulator-selectable");
		}
	}
	
	handleComplexRowClick(row, e){
		if(e.shiftKey){
			this.table._clearSelection();
			this.lastClickedRow = this.lastClickedRow || row;
			
			var lastClickedRowIdx = this.table.rowManager.getDisplayRowIndex(this.lastClickedRow);
			var rowIdx = this.table.rowManager.getDisplayRowIndex(row);
			
			var fromRowIdx = lastClickedRowIdx <= rowIdx ? lastClickedRowIdx : rowIdx;
			var toRowIdx = lastClickedRowIdx >= rowIdx ? lastClickedRowIdx : rowIdx;
			
			var rows = this.table.rowManager.getDisplayRows().slice(0);
			var toggledRows = rows.splice(fromRowIdx, toRowIdx - fromRowIdx + 1);
			
			if(e.ctrlKey || e.metaKey){
				toggledRows.forEach((toggledRow)=>{
					if(toggledRow !== this.lastClickedRow){
						
						if(this.table.options.selectable !== true && !this.isRowSelected(row)){
							if(this.selectedRows.length < this.table.options.selectable){
								this.toggleRow(toggledRow);
							}
						}else{
							this.toggleRow(toggledRow);
						}
					}
				});
				this.lastClickedRow = row;
			}else{
				this.deselectRows(undefined, true);
				
				if(this.table.options.selectable !== true){
					if(toggledRows.length > this.table.options.selectable){
						toggledRows = toggledRows.slice(0, this.table.options.selectable);
					}
				}
				
				this.selectRows(toggledRows);
			}
			this.table._clearSelection();
		}
		else if(e.ctrlKey || e.metaKey){
			this.toggleRow(row);
			this.lastClickedRow = row;
		}else{
			this.deselectRows(undefined, true);
			this.selectRows(row);
			this.lastClickedRow = row;
		}
	}
	
	//toggle row selection
	toggleRow(row){
		if(this.table.options.selectableCheck.call(this.table, row.getComponent())){
			if(row.modules.select && row.modules.select.selected){
				this._deselectRow(row);
			}else{
				this._selectRow(row);
			}
		}
	}
	
	//select a number of rows
	selectRows(rows){
		var rowMatch;
		
		switch(typeof rows){
			case "undefined":
				this.table.rowManager.rows.forEach((row) => {
					this._selectRow(row, true, true);
				});
			
				this._rowSelectionChanged();
				break;
			
			case "string":
			
				rowMatch = this.table.rowManager.findRow(rows);
			
				if(rowMatch){
					this._selectRow(rowMatch, true, true);
				}else{
					this.table.rowManager.getRows(rows).forEach((row) => {
						this._selectRow(row, true, true);
					});
				}
			
				this._rowSelectionChanged();
				break;
			
			default:
				if(Array.isArray(rows)){
					rows.forEach((row) => {
						this._selectRow(row, true, true);
					});
				
					this._rowSelectionChanged();
				}else{
					this._selectRow(rows, false, true);
				}
				break;
		}
	}
	
	//select an individual row
	_selectRow(rowInfo, silent, force){
		//handle max row count
		if(!isNaN(this.table.options.selectable) && this.table.options.selectable !== true && !force){
			if(this.selectedRows.length >= this.table.options.selectable){
				if(this.table.options.selectableRollingSelection){
					this._deselectRow(this.selectedRows[0]);
				}else{
					return false;
				}
			}
		}
		
		var row = this.table.rowManager.findRow(rowInfo);
		
		if(row){
			if(this.selectedRows.indexOf(row) == -1){
				row.getElement().classList.add("tabulator-selected");
				if(!row.modules.select){
					row.modules.select = {};
				}
				
				row.modules.select.selected = true;
				if(row.modules.select.checkboxEl){
					row.modules.select.checkboxEl.checked = true;
				}
				
				this.selectedRows.push(row);
				
				if(this.table.options.dataTreeSelectPropagate){
					this.childRowSelection(row, true);
				}
				
				this.dispatchExternal("rowSelected", row.getComponent());
				
				this._rowSelectionChanged(silent);
			}
		}else{
			if(!silent){
				console.warn("Selection Error - No such row found, ignoring selection:" + rowInfo);
			}
		}
	}
	
	isRowSelected(row){
		return this.selectedRows.indexOf(row) !== -1;
	}
	
	//deselect a number of rows
	deselectRows(rows, silent){
		var self = this,
		rowCount;
		
		if(typeof rows == "undefined"){
			
			rowCount = self.selectedRows.length;
			
			for(let i = 0; i < rowCount; i++){
				self._deselectRow(self.selectedRows[0], true);
			}
			
			if(rowCount){
				self._rowSelectionChanged(silent);
			}
			
		}else{
			if(Array.isArray(rows)){
				rows.forEach(function(row){
					self._deselectRow(row, true);
				});
				
				self._rowSelectionChanged(silent);
			}else{
				self._deselectRow(rows, silent);
			}
		}
	}
	
	//deselect an individual row
	_deselectRow(rowInfo, silent){
		var self = this,
		row = self.table.rowManager.findRow(rowInfo),
		index;
		
		if(row){
			index = self.selectedRows.findIndex(function(selectedRow){
				return selectedRow == row;
			});
			
			if(index > -1){
				
				row.getElement().classList.remove("tabulator-selected");
				if(!row.modules.select){
					row.modules.select = {};
				}
				
				row.modules.select.selected = false;
				if(row.modules.select.checkboxEl){
					row.modules.select.checkboxEl.checked = false;
				}
				self.selectedRows.splice(index, 1);
				
				if(this.table.options.dataTreeSelectPropagate){
					this.childRowSelection(row, false);
				}
				
				this.dispatchExternal("rowDeselected", row.getComponent());
				
				self._rowSelectionChanged(silent);
			}
		}else{
			if(!silent){
				console.warn("Deselection Error - No such row found, ignoring selection:" + rowInfo);
			}
		}
	}
	
	getSelectedData(){
		var data = [];
		
		this.selectedRows.forEach(function(row){
			data.push(row.getData());
		});
		
		return data;
	}
	
	getSelectedRows(){
		
		var rows = [];
		
		this.selectedRows.forEach(function(row){
			rows.push(row.getComponent());
		});
		
		return rows;
	}
	
	_rowSelectionChanged(silent){
		if(this.headerCheckboxElement){
			if(this.selectedRows.length === 0){
				this.headerCheckboxElement.checked = false;
				this.headerCheckboxElement.indeterminate = false;
			} else if(this.table.rowManager.rows.length === this.selectedRows.length){
				this.headerCheckboxElement.checked = true;
				this.headerCheckboxElement.indeterminate = false;
			} else {
				this.headerCheckboxElement.indeterminate = true;
				this.headerCheckboxElement.checked = false;
			}
		}
		
		if(!silent){
			this.dispatchExternal("rowSelectionChanged", this.getSelectedData(), this.getSelectedRows());
		}
	}
	
	registerRowSelectCheckbox (row, element) {
		if(!row._row.modules.select){
			row._row.modules.select = {};
		}
		
		row._row.modules.select.checkboxEl = element;
	}
	
	registerHeaderSelectCheckbox (element) {
		this.headerCheckboxElement = element;
	}
	
	childRowSelection(row, select){
		var children = this.table.modules.dataTree.getChildren(row, true);
		
		if(select){
			for(let child of children){
				this._selectRow(child, true);
			}
		}else{
			for(let child of children){
				this._deselectRow(child, true);
			}
		}
	}
}

SelectRow.moduleName = "selectRow";

export default SelectRow;