//+-----------------------------------------------------------------------------
//| Utils
//+-----------------------------------------------------------------------------
function Clamp(min, val, max) {
  return Math.min(max, Math.max(min, val));
}

function lerp(from, to, t) {
	return from + t * (to - from);
}

function randomRange(l, h){
	return l + Math.random() * (h-l);
}

function randomBool(){
    return Math.random() >= 0.5;
}


//+-----------------------------------------------------------------------------
//|Circle
//+-----------------------------------------------------------------------------
/**
 * Simple circle object
 *
 * @param {Vec2} cent   Point of centre
 * @param {number} rad  Radius of circle
 * @constructor
 */
Nbo.Circle = Nbo.Class.extend({
	init: function(cent, rad) {
	    this.c = cent;
	    this.r = rad;
	}
});


//+-----------------------------------------------------------------------------
//| Nbo.Point2
//+-----------------------------------------------------------------------------
Nbo.Point2 = Nbo.Class.extend({
	init: function(x, y) {
		this.x = x;
		this.y = y;
	},
	
	magnitudeSquared: function() {
		 return this.x * this.x + this.y * this.y;
	},
	
	magnitude: function() {
		return Math.sqrt(this.magnitudeSquared());
	},
	
	add: function(v) {
		this.x += v.x;
		this.y += v.y;
		return this;
	},
	
	addNew: function(v) {
		return new Nbo.Point2(this.x + v.x, this.y + v.y);
	},
	
	subNew: function(v) {
		return new Nbo.Point2(this.x - v.x, this.y - v.y);
	},
	
	mul: function(f) {
		this.x *= f;
		this.y *= f;
		return this;
	},
	
	mulNew: function(f) {
		new Nbo.Point2(this.x * f, this.y * f);
	},

	div: function (f) {
	    this.x /= f;
	    this.y /= f;
	    return this;
	},

	eq: function (v) {
	    return this.x === v.x && this.y === v.y;
	},
	
	neq: function (v) {
	    return this.x !== v.x || this.y !== v.y;
	},
	
	copy: function () {
	    return new Nbo.Point2(this.x, this.y);
	}
});


//+-----------------------------------------------------------------------------
//| Nbo.Vec2
//+-----------------------------------------------------------------------------
/**
 * Two dimensional vector class with accompanying functions for manipulating
 * points. Extends the Nbo.Point2 class
 */
Nbo.Vec2 = Nbo.Point2.extend({
	dot: function (arg) {
	    return this.x * arg.x + this.y * arg.y;
	},
	
	cross: function (arg) {
	    return this.x * arg.y - this.y * arg.x;
	},
	
	normalize: function () {
	    var inv = 1 / Math.sqrt(this.x * this.x + this.y * this.y);
	    this.x *= inv;
	    this.y *= inv;
	
	    return this;
	},
	
	copy: function () {
	    return new Nbo.Vec2(this.x, this.y);
	},
});


//+-----------------------------------------------------------------------------
//| Nbo.Pixel32
//+-----------------------------------------------------------------------------
Nbo.Pixel32 = Nbo.Class.extend({
	init: function(r, g, b, a) {
	    assert(0 <= r && r <= 255);
	    assert(0 <= g && g <= 255);
	    assert(0 <= b && b <= 255);
	    assert(a !== null);
	    
	    this.r = r;
	    this.g = g;
	    this.b = b;
	    if (a === undefined) {
	        this.a = 255;
	    } else {
	        assert(0 <= a && a <= 255);
	        this.a = a;
	    }
	    this.normalized = [r / 255.0, g / 255.0, b / 255.0, this.a / 255.0];
	},
	
	equals: function(other) {
	    if (other instanceof Nbo.Pixel32) {
	        return this.r == other.r && this.g == other.g && this.b == other.b && this.a == other.a;
	    }
	    else {
	        return false;
	    }
	},
	
	toString: function() {
	    return "rgba(" + this.r + "," + this.g + "," + this.b + "," + this.normalized[3] + ")";
	},
	
	rgbString: function() {
	    return "rgb(" + this.r + "," + this.g + "," + this.b + ")";
	},
	
	equal: function(other) {
	    assert(other !== undefined);
	    if (other == null) {
	        return false;
	    }
	    else {
	        return this.r == other.r && this.g == other.g && this.b == other.b && this.a == other.a;
	    }
	},
});


//+-----------------------------------------------------------------------------
//| Nbo.Rectf
//+-----------------------------------------------------------------------------
Nbo.Rectf = Nbo.Class.extend({
	init: function(x0, y0, x1, y1) {
	    this.x0 = x0;
	    this.y0 = y0;
	    this.x1 = x1;
	    this.y1 = y1;
	    this.width = x1 - x0 + 1;
	    this.height = y1 - y0 + 1;
	},
	
	center: function() {
	    return new Nbo.Vec2((this.x1 + this.x0 + 1) / 2.0, (this.y1 + this.y0 + 1) / 2.0);
	},
	
	/**
	 * inverts the rectangle along one of the axes
	 */
	invert: function(axe) {
	    assert(axe != null);
	    if (axe) {
	        return new Nbo.Rectf(-this.x1, this.y0, -this.x0, this.y1);
	    }
	    else {
	        return new Nbo.Rectf(this.x0, -this.y1, this.x1, -this.y0);
	    }
	},
	
	/**
	 * moves rectangle by a vector
	 */
	offset: function(ox, oy) {
	    if (ox instanceof Nbo.Vec2 || ox instanceof Nbo.Point2) {
	        assert(oy === undefined);
	        oy = ox.y;
	        ox = ox.x;
	    }
	    assert(ox != null && oy != null);
	    return new Nbo.Rectf(this.x0 + ox, this.y0 + oy, this.x1 + ox, this.y1 + oy);
	},
	
	/**
	 * expands rectangle
	 */
	expand: function(w, h) {
	    if (w instanceof Nbo.Vec2 || w instanceof Nbo.Point2) {
	        assert(h === undefined);
	        h = w.y;
	        w = w.x;
	    }
	    assert(w != null && h != null);
	    return new Nbo.Rectf(this.x0 - w, this.y0 - h, this.x1 + w, this.y1 + h);
	},
	
	/**
	 * checks for containment (including border)
	 */
	contains: function(x, y) {
	    var o = x;
	    if (x instanceof Nbo.Vec2 || x instanceof Nbo.Point2) {
	        assert(y === undefined);
	        return this.x0 <= o.x && this.y0 <= o.y && o.x <= this.x1 && o.y <= this.y1;
	    }
	    else if (x instanceof Nbo.Rectf) {
	        assert(y === undefined);
	        return this.x0 <= o.x0 && this.y0 <= o.y0 && o.x1 <= this.x1 && o.y1 <= this.y1;
	    }
	    else {
	        assert(x != null && y != null);
	        return this.x0 <= x && this.y0 <= y && x <= this.x1 && y <= this.y1;
	    }
	},
	
	/**
	 * checks containment of circle or rectangle inside this rectangle
	 */
	overlaps: function(shape, ox, oy) {
	    var r, x, y;
	    if (ox === undefined) {
	        assert(oy === undefined);
	        ox = 0;
	        oy = 0;
	    }
	    else if (ox instanceof Nbo.Vec2 || ox instanceof Nbo.Point2) {
	        assert(oy === undefined);
	        oy = ox.y;
	        ox = ox.x;
	    }
	    assert(ox != null && oy != null);
	    if (shape instanceof Nbo.Circle) {
	        r = shape.r;
	        x = shape.c.x + ox;
	        y = shape.c.y + oy;
	        return this.x0 - r <= x && this.y0 - r <= y && x <= this.x1 + r && y <= this.y1 + r;
	    }
	    else if (shape instanceof Nbo.Rectf) {
	        return !(this.x0 > shape.x1 + ox || this.x1 < shape.x0 + ox ||
	                 this.y0 > shape.y1 + oy || this.y1 < shape.y0 + oy);
	    }
	    else {
	        assert(false);
	    }
	},
	
	/**
	 * Clips a vector to the rect
	 */
	clip: function (v) {
		return new Nbo.Vec2(Math.max(this.x0, Math.min(this.x1, v.x)), Math.max(this.y0, Math.min(this.y1, v.y)));
	},
	
	/**
	 * Copy the rect, instantiating a new one
	 */
	copy: function () {
		return new Nbo.Rectf(this.x0, this.y0, this.x1, this.y1);
	},
	
	include: function (x, y) {
	    this.x0 = Math.min(this.x0, x);
	    this.y0 = Math.min(this.y0, y);
	    this.x1 = Math.max(this.x1, x);
	    this.y1 = Math.max(this.y1, y);
		this.width = this.x1 - this.x0 + 1;
		this.height = this.y1 - this.y0 + 1;
	},
});


//+-----------------------------------------------------------------------------
//|
//+-----------------------------------------------------------------------------
var perlin = {start:true, g1 : [], p : []};

perlin.noise1 = function (arg){
	var bx0, bx1;
	var rx0, rx1, sx, t, u, v;

	if (this.start) {
		this.start = false;
		this.init();
	}

	var s = this.setup(arg,bx0,bx1,rx0,rx1);

	sx = this.s_curve(s.rx0);
	u = s.rx0 * this.g1[ this.p[ s.bx0 ] ];
	v = s.rx1 * this.g1[ this.p[ s.bx1 ] ];

	return lerp(u, v, sx);
};

perlin.s_curve = function (t){
	return t * t * (3 - 2 * t);
};

perlin.setup = function (i,bx0,bx1,rx0,rx1){
	var B = 0x100;
	var BM = 0xff;
	var N = 0x1000;
	var NP = 12;
	var NM = 0xfff;

	var s = {};
	s.t = i + N;
	s.bx0 = Math.floor(s.t) & BM;
	s.bx1 = (s.bx0+1) & BM;
	s.rx0 = s.t - Math.floor(s.t);
	s.rx1 = s.rx0 - 1;
	return s;
};

perlin.init = function(){
	var B = 0x100;
	var i, j, k;

	for (i = 0 ; i < B ; i++) {
		this.p[i] = i;
		this.g1[i] = (Math.random() * (B + B) - B) / B;

		/*for (j = 0 ; j < 2 ; j++)
			g2[i][j] = (double)((rand() % (B + B)) - B) / B;
		normalize2(g2[i]);*/

		/*for (j = 0 ; j < 3 ; j++)
			g3[i][j] = (double)((rand() % (B + B)) - B) / B;
		normalize3(g3[i]);*/
	}

	while (--i) {
		k = this.p[i];
		j = Math.floor(Math.random() * B);
		this.p[i] = this.p[j];
		this.p[j] = k;
	}

	for (i = 0 ; i < B + 2 ; i++) {
		this.p[B + i] = this.p[i];
		this.g1[B + i] = this.g1[i];
		/*for (j = 0 ; j < 2 ; j++)
			g2[B + i][j] = g2[i][j];
		for (j = 0 ; j < 3 ; j++)
			g3[B + i][j] = g3[i][j];*/
	}
};