/**
 * Created by rockyl on 2018/11/6.
 *
 */

import {get, recycle, register} from "./ObjectPool";

const name = 'Vector2D';
register(name, function(){
	return new Vector2D();
}, function(instance: Vector2D, x, y){
	instance.setXY(x, y);
});

/**
 * 创建2D矢量
 * @param x
 * @param y
 */
export function createVector2D(x = 0, y = 0){
	return get(name, x, y);
}

/**
 * 回收2D矢量
 * @param target
 */
export function releaseVector2D(target){
	recycle(name, target);
}

/**
 * 2D矢量
 */
export default class Vector2D {
	_x;
	_y;
	onChange;

	public static get zero(): Vector2D{
		return zero;
	}
	
	constructor(x = 0, y = 0, onChange?) {
		this.onChange = onChange;

		this._x = 0;
		this._y = 0;
		this.setXY(x, y);
	}
	
	get x(){
		return this._x;
	}
	set x(v){
		if(this._x !== v){
			const old = this._x;
			this._x = v;

			this.onChange && this.onChange(v, 'x', old);
		}
	}
	
	get y(){
		return this._y;
	}
	set y(v){
		if(this._y !== v){
			const old = this._y;
			this._y = v;

			this.onChange && this.onChange(v, 'y', old);
		}
	}

	setXY(x = 0, y = 0) {
		this.x = x;
		this.y = y;

		return this;
	}

	copyFrom(v2) {
		this.x = v2.x;
		this.y = v2.y;
		return this;
	}

	clone() {
		return new Vector2D(this.x, this.y);
	}

	zero() {
		this.x = 0;
		this.y = 0;

		return this;
	}

	get isZero() {
		return this.x == 0 && this.y == 0;
	}

	normalize() {
		let len = this.length;
		if (len == 0) {
			this.x = 1;
			return this;
		}
		this.x /= len;
		this.y /= len;
		return this;
	}

	get isNormalized() {
		return this.length == 1.0;
	}

	truncate(max) {
		this.length = Math.min(max, this.length);
		return this;
	}

	reverse() {
		this.x = -this.x;
		this.y = -this.y;
		return this;
	}

	dotProd(v2) {
		return this.x * v2.x + this.y * v2.y;
	}

	crossProd(v2) {
		return this.x * v2.y - this.y * v2.x;
	}

	distSQ(v2) {
		let dx = v2.x - this.x;
		let dy = v2.y - this.y;
		return dx * dx + dy * dy;
	}

	distance(v2) {
		return Math.sqrt(this.distSQ(v2));
	}

	add(v2) {
		this.x += v2.x;
		this.y += v2.y;
		return this;
	}

	subtract(v2) {
		this.x -= v2.x;
		this.y -= v2.y;
		return this;
	}

	multiply(value) {
		this.x *= value;
		this.y *= value;
		return this;
	}

	divide(value) {
		this.x /= value;
		this.y /= value;
		return this;
	}

	set angle(value) {
		this.radian = value * Math.PI / 180;
	}

	get angle() {
		return this.radian * 180 / Math.PI;
	}

	set radian(value) {
		let len = this.length;
		this.setXY(Math.cos(value) * len, Math.sin(value) * len);
	}

	get radian() {
		return Math.atan2(this.y, this.x);
	}

	equals(v2) {
		return this.x == v2.x && this.y == v2.y;
	}

	set length(value) {
		let a = this.radian;
		this.setXY(Math.cos(a) * value, Math.sin(a) * value);
	}

	get length() {
		return Math.sqrt(this.lengthSQ);
	}

	get lengthSQ() {
		return this.x * this.x + this.y * this.y;
	}

	get slope() {
		return this.y / this.x;
	}

	toString() {
		return "[Vector2D (x:" + this.x + ", y:" + this.y + ")]";
	}

	static corner(v1, v2) {
		return Math.acos(v1.dotProd(v2) / (v1.length * v2.length));
	}
}

const zero = new Vector2D();
