/*
 This is a WebGL help library created by Mikael Degerfält
 Copyright © 2011 by Mikael Degerfält 
 
 This version is a work in progress, lots of bygs and debugging
 can be found in it, and I garantee nothing in terms of functionality
 and safety to use it.
*/

Demi3d = function(){
	this.shaders={}
	this.progs={}
	this.uniforms={}

	// Add a console
	this.console = ("console" in window) ? window.console : { log: function() { } };
}

Demi3d.prototype.init = function( canvasName )
{
	var glcanvasnames = ['webgl','webgl-canvas','webkit-3d','moz-webgl','experimental-webgl'];

	var canvas = document.getElementById(canvasName);
	var gl = null;
	
	for( i in glcanvasnames) {
		try {gl = canvas.getContext(glcanvasnames[i]) } catch(e) { }
		if (gl) break;
	}
	
	if (!gl) { if(console) console.log('no 3d canvas found'); return null; }

	this.gl = gl
	if (!this.gl) { return null; }

	if( !window.WebGLMatrix4 ){
		window.WebGLMatrix4 = window.CanvasMatrix4;
//		this.log('Could not WebGLMatrix4 class not found');
	}


	gl.clearColor(0.0,0.0,0.0,0.0);
	gl.clearDepth(10000);

	gl.enable(gl.DEPTH_TEST);
	gl.enable(gl.BLEND);
	gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

	return gl;
}


Demi3d.prototype.loadTexture = function(name,url)
{
	var gl = this.gl
	var tex = this.gl.createTexture()
	tex.image = new Image();
	tex.image.onload = function() {
		gl.bindTexture( gl.TEXTURE_2D,tex)
		gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, true)
		gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, tex.image)
		gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
		gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
	}
	tex.image.src = url
}


Demi3d.prototype.loadShaders = function( )
{
	var demi=this
	jQuery('script').each( function(i) { 
		var id=jQuery(this).attr('id')
		
		if( jQuery(this).attr('type')=='x-shader/x-vertex') {
			var type = demi.gl.VERTEX_SHADER;
		} else if(jQuery(this).attr('type')=='x-shader/x-fragment') {
			var type = demi.gl.FRAGMENT_SHADER;
		} else {
			return;
		}

		var s=demi.shader(demi.gl,jQuery(this).text(),type)
		if(s) demi.shaders[id] = s;
	})
	return this;
}


Demi3d.prototype.loadShaderFiles = function( files, callback )
{
	var demi=this  
	var count = 0
	var success = true;

	count = files.length

	jQuery.each(files, function(i){
		var shader = this
		$.get(shader['url'], function(data,status) {
			if( status == 'success') {
				var s = demi.shader(demi.gl, data, shader['type'])
				if(s) demi.shaders[shader['name']] = s;
				success &= true;
			} else {
				success &= (status == 'success');
			}
			if(--count==0) {
				callback( success?'ok':'failed');
			}
		})
	})
	return this;
}



Demi3d.prototype.shader = function(gl, shader_code, type) {

	if(typeof shader_code != 'string') {
		// todo: check that it really is a jqueryable object
		shader_code = jQuery(shader_code).text()
	}

	var shader_obj = gl.createShader(type)
	gl.shaderSource( shader_obj, shader_code )
	gl.compileShader( shader_obj )

	var ok = gl.getShaderParameter( shader_obj, gl.COMPILE_STATUS )
	if(!ok) {
		this.console.log('failed to compile shader ' + shader_code  )
		var errlog = gl.getShaderInfoLog(shader_obj)
		this.console.log( errlog )
		gl.deleteShader(shader_obj)
		return null
	}

	var s = {}
	s.name = shader_obj
	s.type = type
//	this.console.log('created ' + type + ' shader from '+element.id)
	return s;
}


Demi3d.prototype.makeProgram = function(name) {
	var p={}
	p.name =this.gl.createProgram()
	p.attrs = {}
	this.progs[name] = p
	return this
}

Demi3d.prototype.attachShader = function(program,shadername) {
	if( !(shadername in this.shaders) ) {
		this.console.log(shadername + ' not loaded')
	} else if( !(shadername in this.shaders) ) {
		this.console.log(program + ' dont exist')
	} else {
		this.gl.attachShader(this.progs[program].name, this.shaders[shadername].name )
	}
	return this
}


Demi3d.prototype.linkProgram = function(program){
	var id=this.progs[program].name

	this.gl.linkProgram(id )
	var ok = this.gl.getProgramParameter( id, this.gl.LINK_STATUS )
	if(!ok){
		this.console.log('failed to link program')
		var error = this.gl.getProgramInfoLog (id);
        this.console.log("Error in program linking: "+error);
	}
	return this
}


Demi3d.prototype.useProgram = function(program){
	this.gl.useProgram(this.progs[program].name )
	return this
}

Demi3d.prototype.bindAttributes = function(prog,attrs)
{
	var gl=this.gl
	prog=this.progs[prog]
	jQuery.each( attrs, function(i){
		gl.bindAttribLocation (prog.name, i, this);
		prog.attrs[this] = i;
	});
	return this;
}


Demi3d.prototype.setUniform1f = function( prog, varname, value )
{
//	this.console.log( 'setting ' + varname + ' in program ' +prog +' to ' + value );
	var gl = this.gl

	if( !(varname in this.uniforms) ){
		var loc = gl.getUniformLocation(this.progs[prog].name, varname)
		this.uniforms[varname] = loc;
	}
	gl.uniform1f( this.uniforms[varname], value);
}

Demi3d.prototype.setUniform4fv = function( prog, varname, value )
{
	//this.console.log( 'setting ' + varname + ' in program ' +prog +' to ' + value );
	var gl = this.gl
	
	if( !(varname in this.uniforms) ){
		var loc = gl.getUniformLocation(this.progs[prog].name, varname)
		this.uniforms[varname] = loc;
	}


	gl.uniform4fv(gl.getUniformLocation(this.progs[prog].name, varname), value);
}


Demi3d.prototype.setUniformMatrix4fv = function( prog, varname, value )
{
	//this.console.log( 'setting ' + varname + ' in program ' +prog +' to ' + value );
	var gl = this.gl
	
	if( !(varname in this.uniforms) ){
		var loc = gl.getUniformLocation(this.progs[prog].name, varname)
		this.uniforms[varname] = loc;
	}

	gl.uniformMatrix4fv( gl.getUniformLocation(this.progs[prog].name, varname), false, value.getAsWebGLFloatArray() );
}

//
// 3D camera 
//

demiCamera = function() {
	this.position = [0,0,0]
	this.view = new WebGLMatrix4()
	this.perspective = new WebGLMatrix4()
}


demiCamera.prototype.setPosition = function(x,y,z) {
	this.position[0] = x
	this.position[1] = y
	this.position[2] = z
}

demiCamera.prototype.updateMatrix = function() {

	this.view.loadIdentity()
//	this.view.;

}


demiCamera.prototype.use = function(demi) {
//	demi.
	
}


//
// 3D object, 
//

Object3d = function(){
	this.position = [0,0,0];
	this.rotation = [0,0,0];
	this.matrix = new WebGLMatrix4();
}

Object3d.prototype.render = function(){

}


function createCube(){

}

//-------------------------------------------
//
// Delta time class. Used to get dt between calls to update.
//

DeltaTime = function(){
	this.s = new Date().getTime();
	this.dt = 0;
}

DeltaTime.prototype.update = function(){
	var t = new Date().getTime();
	this.dt = (t-this.s)/1000.0;
	this.s = t;
	return this.dt;
}

DeltaTime.prototype.getDelta = function(){ return this.dt;}

