import { PathService } from '../path/path.service';
import { Fastdom } from '../services/fastdom';
import { GRID_PREFIX } from '../definition';

const MOUSE_LEFT_BUTTON = 1;
const VERTICAL_SCROLL_CLASS = `${GRID_PREFIX}-scroll-vertical`;
const HORIZONTAL_SCROLL_CLASS = `${GRID_PREFIX}-scroll-horizontal`;

export class BodyCtrl {
	constructor(model, view, table, bag) {
		this.model = model;
		this.view = view;
		this.bag = bag;
		this.table = table;
		this.rangeStartCell = null;
		this.isScrolling = null;
	}

	onScroll(e) {
		clearTimeout(this.isScrolling);
		this.isScrolling = setTimeout(this.onScrollEnd.bind(this), 100);

		const scroll = this.model.scroll;

		const oldValue = scroll();
		const newValue = {};
		let hasChanges = false;
		if (oldValue.top !== e.scrollTop) {
			this.table.view.addClass(VERTICAL_SCROLL_CLASS);
			newValue.top = e.scrollTop;
			hasChanges = true;
		}

		if (oldValue.left !== e.scrollLeft) {
			this.table.view.addClass(HORIZONTAL_SCROLL_CLASS);
			newValue.left = e.scrollLeft;
			hasChanges = true;
		}

		if (hasChanges) {
			scroll(newValue, {
				source: 'body.core',
				behavior: 'core'
			});
		}
	}

	onScrollEnd() {
		this.table.view.removeClass(VERTICAL_SCROLL_CLASS);
		this.table.view.removeClass(HORIZONTAL_SCROLL_CLASS);
	}

	onWheel(e) {
		const model = this.model;
		if (model.edit().state === 'view') {
			const scroll = model.scroll;
			const table = this.table;
			const upper = 0;

			Fastdom.measure(() => {
				const lower = table.view.scrollHeight() - table.view.height();
				const top = Math.min(lower, Math.max(upper, scroll().top + e.deltaY));

				scroll({ top }, { source: 'body.core' });
			});
		}
	}

	onMouseDown(e) {
		if (e.which === MOUSE_LEFT_BUTTON) {
			const selectionState = this.selection;
			if (selectionState.area !== 'body') {
				return;
			}

			const pathFinder = new PathService(this.bag.body);
			const cell = pathFinder.cell(e.path);

			if (selectionState.mode === 'range') {
				this.rangeStartCell = cell;

				if (this.rangeStartCell) {
					this.view.selection.selectRange(this.rangeStartCell, null, 'body');
				}
			}
		}
	}

	onMouseMove(e) {
		const pathFinder = new PathService(this.bag.body);
		const row = pathFinder.row(e.path);

		if (row) {
			const index = row.index;
			const highlightRow = this.view.highlight.row;
			if (highlightRow.canExecute(index)) {
				this.model
					.highlight()
					.rows
					.filter(i => i !== index)
					.forEach(i => highlightRow.execute(i, false));

				highlightRow.execute(index, true);
			}
		}

		if (this.selection.mode === 'range') {
			const startCell = this.rangeStartCell;
			const endCell = pathFinder.cell(e.path);

			if (startCell && endCell) {
				this.navigate(endCell);
				this.view.selection.selectRange(startCell, endCell, 'body');
			}
		}
	}

	onMouseLeave() {
		const highlightRow = this.view.highlight.row;
		this.model
			.highlight()
			.rows
			.forEach(i => highlightRow.execute(i, false));
	}

	onMouseUp(e) {
		const mode = this.selection.mode;
		const edit = this.model.edit;

		if (e.which === MOUSE_LEFT_BUTTON) {
			const pathFinder = new PathService(this.bag.body);
			const cell = pathFinder.cell(e.path);

			if (mode === 'range') {
				this.rangeStartCell = null;
			}

			if (edit().state === 'startBatch') {
				edit({ state: 'endBatch' });
				return;
			}

			if (cell) {
				this.select(cell);
				this.navigate(cell);
				if (cell.column.editorOptions.trigger === 'click' && this.view.edit.cell.enter.canExecute(cell)) {

					if (this.selection.items.length > 1) {
						return;
					}

					this.view.edit.cell.enter.execute(cell);
				}
			}
		}
	}

	select(cell) {
		const selectionState = this.selection;
		if (cell.column.type !== 'select' &&
			(selectionState.area !== 'body' || selectionState.mode === 'range')) {
			return;
		}

		const model = this.model;
		const editMode = model.edit().mode;
		switch (selectionState.unit) {
			case 'row': {
				if (cell.column.type === 'select' && cell.column.editorOptions.trigger === 'focus') {
					const focusState = model.focus();
					if (focusState.rowIndex !== cell.rowIndex || focusState.columnIndex !== cell.columnIndex) {
						this.view.selection.toggleRow.execute(cell.row, 'body');
					}
				} else if (!editMode && cell.column.class !== 'control') {
					this.view.selection.toggleRow.execute(cell.row, 'body');
				}
				break;
			}

			case 'column': {
				if (!editMode) {
					this.view.selection.toggleColumn.execute(cell.column, 'body');
				}
				break;
			}

			case 'mix': {
				if (cell.column.type === 'row-indicator') {
					this.view.selection.toggleCell.execute(cell, 'body');
				}
			}
		}
	}

	navigate(cell) {
		const focus = this.view.nav.focus;
		if (focus.canExecute(cell)) {
			focus.execute(cell);
		}
	}

	get selection() {
		return this.model.selection();
	}
}

@@ -136,16 +136,64 @@
 nition';
+%0Aimport %7B jobLine %7D from '../services/job.line';
 %0A%0Aconst 
@@ -531,27 +531,25 @@
 %0A%09%09this.
-isS
+s
 crolling
  = null;
@@ -540,23 +540,34 @@
 crolling
- = null
+Job = jobLine(100)
 ;%0A%09%7D%0A%0A%09o
@@ -583,110 +583,8 @@
 ) %7B%0A
-%09%09clearTimeout(this.isScrolling);%0A%09%09this.isScrolling = setTimeout(this.onScrollEnd.bind(this), 100);%0A%0A
 %09%09co
@@ -1086,32 +1086,83 @@
 core'%0A%09%09%09%7D);%0A%09%09%7D
+%0A%0A%09%09this.scrollingJob(this.onScrollEnd.bind(this));
 %0A%09%7D%0A%0A%09onScrollEn

use job line to detect scroll end