import { Vector3D, AbstractionBase, IAbstractionPool } from '@awayjs/core';

import { IPartitionTraverser } from '../partition/IPartitionTraverser';
import { INode } from '../partition/INode';

import { ITabContainer } from '../base/ITabContainer';
import { ContainerNode } from '../partition/ContainerNode';

/**
 * Picks a 3d object from a view or scene by 3D raycast calculations.
 * Performs an initial coarse boundary calculation to return a subset of entities whose bounding volumes intersect with the specified ray,
 * then triggers an optional picking collider on individual renderable objects to further determine the precise values of the picking ray collision.
 *
 * @class away.pick.RaycastPicker
 */
export class TabPicker extends AbstractionBase implements IPartitionTraverser {

	/**
     *
     * @returns {ContainerNode}
     */
	public get node(): INode {
		return <INode> this._asset;
	}

	private _tabNodes: ContainerNode[] = [];
	private _customTabNodes: ContainerNode[][] = [];
	private _customTabNodesSorted: ContainerNode[] = [];

	/**
	 * Creates a new <code>RaycastPicker</code> object.
	 *
	 * @param findClosestCollision Determines whether the picker searches for the closest bounds collision along the ray,
	 * or simply returns the first collision encountered. Defaults to false.
	 */
	public init(node: INode, pool: IAbstractionPool) {
		super.init(node, pool);
	}

	private sortTabEnabledEntities(): void {
		if (this._customTabNodes.length <= 0 && this._tabNodes.length <= 0)
			return;

		const snapGridY: number = 10;
		let len: number = 0;
		const orderedOnY: ContainerNode[][] = [];
		let i: number = 0;
		let e: number = 0;
		if (this._customTabNodes.length > 0) {

			while (i < this._customTabNodes.length) {
				if (this._customTabNodes[i]) {
					this._customTabNodes[i] = this._customTabNodes[i].reverse();
				}

				i++;
			}
			return;
		}

		//  first sort into rows based on global y-position, snapping y-positions to a grid
		//  than sort the rows by global x-position
		len = this._tabNodes.length;
		for (i = 0; i < len; i++) {
			const enabledNode = this._tabNodes[i];
			const scenePosition: Vector3D = enabledNode.parent.getMatrix3D().position;
			//console.log("enabledEntity.scenePosition.y", scenePosition.y);
			const ySnappedToGrid = Math.floor(scenePosition.y / snapGridY);
			if (orderedOnY.length <= ySnappedToGrid) {
				orderedOnY.length = ySnappedToGrid + 1;
			}
			if (!orderedOnY[ySnappedToGrid])
				orderedOnY[ySnappedToGrid] = [];
			orderedOnY[ySnappedToGrid].push(enabledNode);
		}

		this._tabNodes.length = 0;
		for (i = 0; i < orderedOnY.length; i++) {
			let entityRow = orderedOnY[i];
			if (entityRow) {
				//console.log("entityRow", entityRow.length);
				entityRow = entityRow.sort(function(a, b) {
					return a.parent.getMatrix3D().position.x > b.parent.getMatrix3D().position.x ? 1 : -1;
				});
				for (e = 0; e < entityRow.length; e++) {

					//console.log("2enabledEntity.scenePosition.y", entityRow[e].transform.concatenatedMatrix3D.position.y);
					//console.log("2enabledEntity.scenePosition.x", entityRow[e].transform.concatenatedMatrix3D.position.x);
					this._tabNodes[this._tabNodes.length] = entityRow[e];
				}
			}
		}

	}

	public traverse(): void {
		this._tabNodes.length = 0;
		this._customTabNodes.length = 0;
		this._customTabNodesSorted.length = 0;
		(<INode> this._asset).acceptTraverser(this);

		this.sortTabEnabledEntities();
		this._invalid = false;
	}

	public getTraverser(node: ContainerNode): IPartitionTraverser {
		return this;
	}

	public getNextTabEntity(currentFocus: ContainerNode): ContainerNode {
		if (this._invalid)
			this.traverse();

		if (this._customTabNodes.length <= 0 && this._tabNodes.length <= 0)
			return currentFocus;

		if (this._customTabNodes.length > 0) {
			var i: number = 0;
			let i2: number = 0;
			let t: number = 0;
			let t2: number = 0;
			if (currentFocus) {
				while (i < this._customTabNodes.length) {
					if (this._customTabNodes[i]) {
						for (t = 0; t < this._customTabNodes[i].length; t++) {
							if (this._customTabNodes[i][t] == currentFocus) {
								t2 = t + 1;
								while (t2 < this._customTabNodes[i].length) {
									if (this._customTabNodes[i][t2])
										return this._customTabNodes[i][t2];
									t2++;
								}
								i2 = i + 1;
								while (i2 < this._customTabNodes.length) {
									if (this._customTabNodes[i2]) {
										for (t2 = 0; t2 < this._customTabNodes[i2].length; t2++) {
											if (this._customTabNodes[i2][t2])
												return this._customTabNodes[i2][t2];
										}
									}
									i2++;
								}
								i2 = 0;
								while (i2 < this._customTabNodes.length) {
									if (this._customTabNodes[i2]) {
										for (t2 = 0; t2 < this._customTabNodes[i2].length; t2++) {
											if (this._customTabNodes[i2][t2])
												return this._customTabNodes[i2][t2];
										}
									}
									i2++;
								}
							}
						}
					}
					i++;
				}
			}
			i2 = 0;
			while (i2 < this._customTabNodes.length) {
				if (this._customTabNodes[i2]) {
					for (t2 = 0; t2 < this._customTabNodes[i2].length; t2++) {
						if (this._customTabNodes[i2][t2])
							return this._customTabNodes[i2][t2];
					}
				}
				i2++;
			}
			return currentFocus;
		}
		const len: number = this._tabNodes.length;
		for (var i: number = 0; i < len; i++) {
			if (this._tabNodes[i] == currentFocus) {
				if (i == len - 1) {
					return this._tabNodes[0];
				}
				return this._tabNodes[i + 1];
			}
		}
		// this point we would already have exit out if tabEntities.length was 0
		return this._tabNodes[0];

	}

	public getPrevTabEntity(currentFocus: ContainerNode): ContainerNode {
		if (this._invalid)
			this.traverse();

		if (this._customTabNodes.length <= 0 && this._tabNodes.length <= 0)
			return currentFocus;

		if (this._customTabNodes.length > 0) {
			var i: number = this._customTabNodes.length;
			let i2: number = 0;
			let t: number = 0;
			let t2: number = 0;
			if (currentFocus) {
				while (i > 0) {
					i--;
					if (this._customTabNodes[i]) {
						for (t = this._customTabNodes[i].length - 1; t >= 0; t--) {
							if (this._customTabNodes[i][t] == currentFocus) {
								t2 = t - 1;
								while (t2 > 0) {
									t2--;
									if (this._customTabNodes[i][t2])
										return this._customTabNodes[i][t2];
								}
								i2 = i - 1;
								while (i2 > 0) {
									i2--;
									if (this._customTabNodes[i2]) {
										for (t2 = this._customTabNodes[i2].length - 1;t2 >= 0; t2--) {
											if (this._customTabNodes[i2][t2])
												return this._customTabNodes[i2][t2];
										}
									}
								}
								i2 = this._customTabNodes.length;
								while (i2 > 0) {
									i2--;
									if (this._customTabNodes[i2]) {
										for (t2 = this._customTabNodes[i2].length - 1; t2 >= 0; t2--) {
											if (this._customTabNodes[i2][t2])
												return this._customTabNodes[i2][t2];
										}
									}
								}
							}
						}
					}
				}
			}
			i2 = this._customTabNodes.length - 1;
			while (i2 > 0) {
				i2--;
				if (this._customTabNodes[i2]) {
					for (t2 = this._customTabNodes[i2].length - 1;t2 >= 0; t2--) {
						if (this._customTabNodes[i2][t2])
							return this._customTabNodes[i2][t2];
					}
				}
			}
			return currentFocus;
		}
		if (currentFocus) {
			const len: number = this._tabNodes.length;
			for (var i: number = len - 1; i >= 0; i--) {
				if (this._tabNodes[i] == currentFocus) {
					if (i == 0) {
						return this._tabNodes[this._tabNodes.length - 1];
					}
					return this._tabNodes[i - 1];
				}
			}
		}
		// this point we would already have exit out if tabEntities.length was 0
		return this._tabNodes[0];

	}

	/**
	 * Returns true if the current node is at least partly in the frustum. If so, the partition node knows to pass on the traverser to its children.
	 *
	 * @param node The Partition3DNode object to frustum-test.
	 */
	public enterNode(node: INode): boolean {
		if (node.isInvisible())
			return false;
		return true;
	}

	public dispose(): void {
		//TODO
	}

	/**
	 *
	 * @param entity
	 */
	public applyEntity(node: ContainerNode): void {
		const tabContainer = <ITabContainer> node.container;
		if (tabContainer.tabEnabled) {
			if (tabContainer.assetType != '[asset TextField]' || (<any> tabContainer).type == 'input') {
				// add the entity to the correct tab list.
				if (tabContainer.tabIndex >= 0) {
					if (this._customTabNodes.length < tabContainer.tabIndex)
						this._customTabNodes.length = tabContainer.tabIndex;
					if (!this._customTabNodes[tabContainer.tabIndex]) {
						this._customTabNodes[tabContainer.tabIndex] = [];
					}
					this._customTabNodes[tabContainer.tabIndex].push(node);
				} else {
					this._tabNodes[this._tabNodes.length] = node;
				}

			}
		}
	}
}