

// simple model class

function baseModel(context, texID, model, shaderProgram)
{
	// fields //////////////////////////////////
	this.ctx			= context;
	this.world			= new CanvasMatrix4();
	this.normalMatrix	= new CanvasMatrix4();
	this.texID			= texID;
	this.model			= model;
	this.program		= shaderProgram;
	
	// uniform handles
	this.u_world    				= null;
	this.u_normalMatrix				= null;
	this.u_modelViewProjMatrixLoc 	= null;
}

// methods /////////////////////////////////

baseModel.prototype.init = function()
{
	this.ctx.useProgram(this.program);
	this.ctx.clearColor(0.25, 0.25, 0.25, 1);
    this.ctx.clearDepth(10000);

    this.ctx.enable(this.ctx.DEPTH_TEST);
    this.ctx.enable(this.ctx.BLEND);
    this.ctx.blendFunc(this.ctx.SRC_ALPHA, this.ctx.ONE_MINUS_SRC_ALPHA);
	
	// Multi-samp
	this.ctx.enable(this.ctx.SAMPLE_COVERAGE);
	this.ctx.sampleCoverage(0.1, this.ctx.TRUE);
	this.u_world = this.ctx.getUniformLocation(this.ctx.program, "u_worldMatrix");
	this.u_normalMatrix = this.ctx.getUniformLocation(this.ctx.program, "u_normalMatrix");
	this.u_modelViewProjMatrixLoc = this.ctx.getUniformLocation(this.program, "u_modelViewProjMatrix");
	
	// Set some uniform variables for the shaders
    this.ctx.uniform1i(this.ctx.getUniformLocation(this.program, "basemap"), 0);
	
	this.ctx.enableVertexAttribArray(0);
    this.ctx.enableVertexAttribArray(1);
    this.ctx.enableVertexAttribArray(2);
}

baseModel.prototype.update = function(dt)
{
	
}

baseModel.prototype.draw = function()
{
	this.ctx.useProgram(this.program);
	
	if(this.model == null || this.model.loaded == false)
		this.drawUnloaded();
	else
	{
		// push the world matrix
		this.ctx.uniformMatrix4fv(this.u_world, false, this.world.getAsWebGLFloatArray());
		
		// create the model-view matrix
		this.ctx.mvpMatrix.load(this.world);																			// load the world matrix
		this.ctx.mvpMatrix.multRight(g_cam.view);

		// Construct the normal matrix from the model-view matrix and pass it in
        this.normalMatrix.load(this.ctx.mvpMatrix);
        this.normalMatrix.invert();
        this.normalMatrix.transpose();
        this.ctx.uniformMatrix4fv(this.u_normalMatrix, false, this.normalMatrix.getAsWebGLFloatArray());
		
		// construct the mvp		
		this.ctx.mvpMatrix.multRight(this.ctx.perspectiveMatrix);														// mult with the projection (view is left as identity - no need to mult it)
		this.ctx.uniformMatrix4fv(this.u_modelViewProjMatrixLoc, false, this.ctx.mvpMatrix.getAsWebGLFloatArray());		// push the mvp matrix to vertex shader
		
		this.ctx.bindTexture(this.ctx.TEXTURE_2D, this.texID);															// Bind the texture
		this.ctx.drawElements(this.ctx.TRIANGLES, this.model.numIndices, this.ctx.UNSIGNED_SHORT, 0);					// Draw the vortex
		this.ctx.bindTexture(this.ctx.TEXTURE_2D, null);																// unbind
	}
	
	this.ctx.useProgram(null);
}

baseModel.prototype.drawUnloaded = function()
{
	// do nothing
}

// Vortex model class
function model(context, rotSpeed, zOffset, speed, texID, model, rotOffset, shaderProgram)
{
	// fields //////////////////////////////////
	this.ctx			= context;
	this.world			= new CanvasMatrix4();
	this.zOffset		= zOffset;
	this.speed			= speed;
	this.curZ			= 0;
	this.rotSpeed		= rotSpeed;
	this.texID			= texID;
	this.model			= model;
	this.u_world    	= null;
	this.u_time 		= null;
	
	this.baseRot		= 0.0;
	this.rotOffset  	= rotOffset;
	this.vortexLength   = 80.265;
	this.scale 			= 1;
	this.zAxis 			= 0;
	this.dist 			= 0;
	this.program		= shaderProgram;
	
	// shader params
	this.lineColor			= new vec4(1, 1, 1, 1);
	this.swapColor			= new vec4(1, 1, 1, 1);
	this.swapTime 			= 1.0;
	this.corkscrewRotation	= 5.280;
	this.corkscrewRange 	= 80.265;
	this.taperEnd 			= 0.496;
	this.taperStart			= 1.0;
	this.elapsedTime 		= 0.0;
	
	// modifies the shader paramaters
	this.atSwapTime 		= 1.0;
	this.baseRotSpeed		= 0.157;
	this.doSwap 			= true;
}
// methods /////////////////////////////////

model.prototype.init = function()
{
	this.ctx.useProgram(this.program);
	this.u_world 	= this.ctx.getUniformLocation(this.ctx.program, "u_worldMatrix");
	this.u_time		= this.ctx.getUniformLocation(this.ctx.program, "u_time");

	this.world.m43 = this.zAxis;
	this.baseRot = this.rotOffset;
	if(this.zOffset != 0)
	{
		this.curZ = this.vortexLength*0.5;
		this.dist = this.vortexLength*1.5;
	}else
	{
		this.curZ = -this.vortexLength*0.5;
		this.dist = this.vortexLength*0.5;
	}
}

model.prototype.swapColors = function()
{
	this.doSwap = true;
	this.swapTime = 0;
}

model.prototype.update = function(dt)
{
	this.elapsedTime += dt;
	if(this.doSwap)
	{
		this.swapTime 	 = (this.swapTime + this.rotSpeed*dt) % 1.0; 	// swap time
		if(this.swapTime > 0.9)
		{
			this.doSwap = false;
			this.lineColor = new vec4(this.swapColor.x, this.swapColor.y, this.swapColor.z, 1.0);
		}
	}
	this.baseRot 	 -= (this.baseRotSpeed*dt*0.2);						// base rotation
	
	if(this.baseRot < -1.59)
		this.baseRot += 1.59;

	// save pos and move to origin
	this.world.m41 = 0;
	this.world.m42 = 0; 
	this.world.m43 = 0;
	
	// apply rotation
	this.world.rotate(-controller.xRot, 1,0,0);
	this.world.rotate(-controller.yRot, 0,1,0);
	
	// translate forward from default position
	var dist = this.speed*dt;
	this.curZ += dist;
	this.dist += dist;
	
	var x = this.world.m31;
	var y = this.world.m32;
	var z = this.world.m33;
	
	// normalize
	var length = Math.sqrt(x * x + y * y + z * z);
	if (length == 0) {
		// bad vector, just use something reasonable
		x = 0;
		y = 0;
		z = 1;
	} else if (length != 1) {
		x /= length;
		y /= length;
		z /= length;
	}
	
	this.scale = this.curZ;
	this.world.m41 +=  x*(this.curZ - dist		);					// move on the x relative to x axis
	this.world.m42 +=  y*(this.curZ - dist		);                  // move on the y relative to y axis
	this.world.m43 +=  z*(this.curZ - dist		) + this.zAxis;     // push the object way out so we can debug
	
	if (this.dist  > this.vortexLength*2) {
		this.world.m41 	=  0;
		this.world.m42 	=  0;
		this.world.m43 	=  this.zAxis;
		this.curZ 		= -this.vortexLength;
		this.curZ		+= (this.dist - this.vortexLength*2);
		this.dist 		= 0;
	}
}

model.prototype.draw = function()
{
	this.ctx.useProgram(this.program);
	
	if(this.model == null || this.model.loaded == false)
		this.drawUnloaded();
	else
	{
		// set params
		this.ctx.uniform1f(u_corkscrewRotation,		this.corkscrewRotation	);
		this.ctx.uniform1f(u_corkscrewRange, 		this.corkscrewRange 	);
		this.ctx.uniform1f(u_taperEnd, 				this.taperEnd 			);
		this.ctx.uniform1f(u_taperStart, 			this.taperStart			);
		this.ctx.uniform4f(u_lineColor, 			this.lineColor.x, this.lineColor.y,this.lineColor.z,this.lineColor.w);
		this.ctx.uniform4f(u_swapColor, 			this.swapColor.x, this.swapColor.y,this.swapColor.z,this.swapColor.w);
		this.ctx.uniform1f(u_swapTime, 				this.swapTime);
		this.ctx.uniform1f(this.u_time, 			this.elapsedTime);
	
		this.ctx.uniform1f(u_swapTime, this.swapTime);
		this.ctx.uniform1f(u_baseRotation, this.baseRot);
		
		// Construct the model-view * projection matrix and pass it in
			
		// hack to make shader work
		var z = this.world.m43;
		this.world.m43 = this.scale;
		this.ctx.uniformMatrix4fv(this.u_world, false, this.world.getAsWebGLFloatArray());
		this.world.m43 = z;
		
		
		this.ctx.mvpMatrix.load(this.world);																			// load the world matrix
		this.ctx.mvpMatrix.multRight(g_cam.view);			
		this.ctx.mvpMatrix.multRight(this.ctx.perspectiveMatrix);														// mult with the projection (view is left as identity - no need to mult it)
		this.ctx.uniformMatrix4fv(this.ctx.u_modelViewProjMatrixLoc, false, this.ctx.mvpMatrix.getAsWebGLFloatArray());	// push the mvp matrix to vertex shader
		
		this.ctx.bindTexture(this.ctx.TEXTURE_2D, this.texID);															// Bind the texture
		this.ctx.drawElements(this.ctx.TRIANGLES, this.model.numIndices, this.ctx.UNSIGNED_SHORT, 0);					// Draw the vortex
		this.ctx.bindTexture(this.ctx.TEXTURE_2D, null);																// unbind
	}
}

model.prototype.drawUnloaded = function()
{
	// do nothing
}

