/* <copyright>
Copyright (c) 2012, Motorola Mobility LLC.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of Motorola Mobility LLC nor the names of its
  contributors may be used to endorse or promote products derived from this
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
</copyright> */

// RDGE namespaces
var RDGE = RDGE || {};

/*
this API should be familiar to anyone who has worked with HLSL effect files.
*/

/*
 *  A map of types to uniform 'binding' functions
 */
RDGE.bindMap={};
RDGE.bindMap['int']     = function(ctx, a,b) { ctx.uniform1iv(a,b); };
RDGE.bindMap['float']   = function(ctx, a,b) { ctx.uniform1fv(a,b); };
RDGE.bindMap['vec2']        = function(ctx, a,b) { ctx.uniform2fv(a,b); };
RDGE.bindMap['vec3']        = function(ctx, a,b) { ctx.uniform3fv(a,b); };
RDGE.bindMap['vec4']        = function(ctx, a,b) { ctx.uniform4fv(a,b); };
RDGE.bindMap['mat3']        = function(ctx, a,b) { ctx.uniformMatrix3fv(a,false,b); };
RDGE.bindMap['mat4']        = function(ctx, a,b)
{
    ctx.uniformMatrix4fv(a,false,b);
    RDGE.globals.engine.getContext().debug.mat4CallCount++;
};

RDGE.bindMap['tex2d']   = function(ctx, a,b)
{
    ctx.activeTexture(ctx.TEXTURE0+b[0]);
    ctx.bindTexture(ctx.TEXTURE_2D, b[1]);
    ctx.uniform1iv(a,[b[0]]);
};

RDGE.bindMap['texCube']=function(ctx, a,b)
{
    ctx.activeTexture(ctx.TEXTURE0+b[0]);
    ctx.bindTexture(ctx.TEXTURE_CUBE_MAP, b[1]);
    ctx.uniform1iv(a,[b[0]]);
};

RDGE.lightDataMap =
[
    function(ctx, loc, lightNode) { ctx.uniform3fv(loc, lightNode.position); },
    function(ctx, loc, lightNode) { ctx.uniform4fv(loc, lightNode.lightDiffuse); },
    function(ctx, loc, lightNode) { ctx.uniform4fv(loc, lightNode.lightAmbient); },
    function(ctx, loc, lightNode) { ctx.uniform4fv(loc, lightNode.lightSpecular); }
];

RDGE.paramTypeNameMapping = null;

RDGE.jshader = function (addr) {
    this.name = addr;
    this.def = null;
    this.technique = {};
    this.params = {};
    this.compiledShaders = {};
    this.resetRS = false;
    this.currentPass = 0;
    this.type_jshader = {};
    this.global = {};
    this.renderer = RDGE.globals.engine.getContext().renderer;
    this.ctx = this.renderer.ctx;

    // load jshader definition at addr (if provided)
    if (addr != undefined && addr != null) {
        // a synchronous ajax request
        request = new XMLHttpRequest();
        request.open("GET", addr, false);
        request.send(null);
        this.def = JSON.parse(request.responseText);
    }

    if (!RDGE.paramTypeNameMapping) {
        var gl = this.ctx;
        RDGE.paramTypeNameMapping = {};
        RDGE.paramTypeNameMapping[gl.BOOL] = "bool";
        RDGE.paramTypeNameMapping[gl.INT] = "int";
        RDGE.paramTypeNameMapping[gl.FLOAT] = "float";
        RDGE.paramTypeNameMapping[gl.FLOAT_VEC2] = "vec2";
        RDGE.paramTypeNameMapping[gl.FLOAT_VEC3] = "vec3";
        RDGE.paramTypeNameMapping[gl.FLOAT_VEC4] = "vec4";
        RDGE.paramTypeNameMapping[gl.INT_VEC2] = "vec2";
        RDGE.paramTypeNameMapping[gl.INT_VEC3] = "vec3";
        RDGE.paramTypeNameMapping[gl.INT_VEC4] = "vec4";
        RDGE.paramTypeNameMapping[gl.BOOL_VEC2] = "vec2";
        RDGE.paramTypeNameMapping[gl.BOOL_VEC3] = "vec3";
        RDGE.paramTypeNameMapping[gl.BOOL_VEC4] = "vec4";
        RDGE.paramTypeNameMapping[gl.FLOAT_MAT2] = "mat2";
        RDGE.paramTypeNameMapping[gl.FLOAT_MAT3] = "mat3";
        RDGE.paramTypeNameMapping[gl.FLOAT_MAT4] = "mat4";
        RDGE.paramTypeNameMapping[gl.SAMPLER_2D] = "tex2d";
        RDGE.paramTypeNameMapping[gl.SAMPLER_CUBE] = "texCube";
    }

    /*
    *   private helper functions
    */
    this.bindParameters = function (pass) {
        var params = pass.defParamsList; // global parameters to start with
        var lightParams = pass.lightParams;
        var lightContext = pass.lightContext;
        var length = params.length;
        var idx = 0;
        var texArg = new Array(2)

        // global parameters
        var texUnit = 0;
        for (idx = 0; idx < length; ++idx) {
            if (params[idx].type == 'tex2d' || params[idx].type == 'texCube') {
                texArg[0] = texUnit++;
                texArg[1] = params[idx].data[0];
                RDGE.bindMap[params[idx].type](this.ctx, params[idx].loc, texArg);
            }
            else {
                RDGE.bindMap[params[idx].type](this.ctx, params[idx].loc, RDGE.rdgeGlobalParameters[params[idx].name].data);
            }
        }

        // light settings defined by the material
        var len = RDGE.rdgeConstants.MAX_MATERIAL_LIGHTS;
        for (var i = 0; i < len; ++i) {
            // if there is a context for a light check to see if we have a binding to the light
            if (lightContext[i] != null) {
                // see if we have parameters to bind to this light
                if (lightParams[i]) {
                    // something is here lets bind it
                    var numParams = lightParams[i].length;
                    for (var lp = 0; lp < numParams; ++lp) {
                        // bind the parameters using the lightDataMap function lookup, dataIndex is the key
                        RDGE.lightDataMap[lightParams[i][lp].dataIndex](this.ctx, lightParams[i][lp].loc, lightContext[i]);
                    }
                }
            }
        }

        // let locally defined uniforms stomp globally defined uniforms
        texUnit = this.renderer.usedTextureUnits; // start adding texture after the default textures
        params = pass.paramsList;
        length = params.length;
        for (idx = 0; idx < length; ++idx) {
            if (params[idx].type == 'tex2d' || params[idx].type == 'texCube') {
                texArg[0] = texUnit++;
                texArg[1] = params[idx].data[0];
                RDGE.bindMap[params[idx].type](this.ctx, params[idx].loc, texArg);
            }
            else {
                RDGE.bindMap[params[idx].type](this.ctx, params[idx].loc, params[idx].data);
            }
        }
    };

    /*
    *   helper function for setting up a texture
    */
    createJShaderTexture = function (ctx, param) {
        var texHandle = null;
        if (typeof param.data == "string") {
            texHandle = ctx.canvas.renderer.getTextureByName(param.data, param.wrap, param.repeat, param.mips);
        }
        else {
            texHandle = ctx.canvas.renderer.getTextureByName(param.data.lookUpName, param.wrap, param.repeat, param.mips);
        }

        return [texHandle];
    };

    paramType = function (ctx, name, def, program, technique) {
        var texUnit = 0;

        // Get the uniform location and store it
        this.loc = ctx.getUniformLocation(program, name);

        // if the parameter does not exist in the shader cull it from the pass
        if (this.loc == null) {
            window.console.log("ctx:" + ctx.canvas.rdgeid + ", technique: " + technique + ", uniform: " + name + " was not found, jshader param will have no affect");
            //return;
        }

        var param = def[name];
        this.type = param.type;

        // if data was not provided then create default data
        if (param.data == undefined) {
            switch (param.type) {
                case "vec4": this.data = RDGE.vec4.zero(); break;
                case "vec3": this.data = RDGE.vec3.zero(); break;
                case "vec2": this.data = RDGE.vec2.zero(); break;
                case "mat4": this.data = RDGE.mat4.zero(); break;
                case "mat3": this.data = new Array(9); break;
                case "mat2": this.data = [0, 0, 0, 0]; break;
                case "float": this.data = [0]; break;
                case "int": this.data = [0]; break;
                case "tex2d": this.data = [ctx.canvas.renderer.getTextureByName(RDGE.globals.engine._assetPath+"images/white.png")]; break;
                case "texCube": this.data = [ctx.canvas.renderer.getTextureByName(RDGE.globals.engine._assetPath+"images/white.png")]; break;
            }
        }
        else {
            if (param.type == 'tex2d' || param.type == 'texCube') {
                this.data = createJShaderTexture(ctx, param);
            }
            else {
                this.data = param.data.slice();
            }
        }

        this.get = function () {
            return this.data.slice();
        };

        this.set = function (v) {
            if (this.type == 'tex2d' || this.type == 'texCube') {
                if (typeof v == "string") {
                    v = ctx.canvas.renderer.getTextureByName(v);
                }

                this.data[0] = v;
            }
            else {
                var len = this.data.length;
                for (var i = 0; i < len; ++i)
                    this.data[i] = v[i];
            }
        };
    };

    globalParam = function (ctx, name, param, program) {
        this.type = param.type;

        this.data = param.data;

        // Get the uniform location and store it
        this.loc = ctx.getUniformLocation(program, name);

        // if data was not provided then create default data
        if (!this.data) {
            switch (param.type) {
                case "vec4": this.data = RDGE.vec4.zero(); break;
                case "vec3": this.data = RDGE.vec3.zero(); break;
                case "vec2": this.data = RDGE.vec2.zero(); break;
                case "mat4": this.data = RDGE.mat4.zero(); break;
                case "mat3": this.data = new Array(9); break;
                case "mat2": this.data = [0, 0, 0, 0]; break;
                case "float": this.data = [0]; break;
                case "int": this.data = [0]; break;
                case "tex2d": this.data = [ctx.canvas.renderer.getTextureByName(RDGE.globals.engine._assetPath+"images/white.png")]; break;
                case "texCube": this.data = [ctx.canvas.renderer.getTextureByName(RDGE.globals.engine._assetPath+"images/white.png")]; break;
            }
        }
        else {
            if (param.type == 'tex2d' || param.type == 'texCube') {
                this.data = createJShaderTexture(ctx, param);
            }
            else {
                this.data = param.data.slice();
            }
        }

        this.get = function () {
            return this.data.slice();
        };

        this.set = function (v) {
            if (this.type == 'tex2d' || this.type == 'texCube') {
                if (typeof v == "string") {
                    v = ctx.canvas.renderer.getTextureByName(v);
                }

                this.data[0] = v;
            }
            else {
                var len = this.data.length;
                for (var i = 0; i < len; ++i)
                    this.data[i] = v[i];
            }
        };
    };


    this.init = function () {
        var techniques = this.def.techniques;
        var defaultTech = null;
        for (t in techniques) {
            defaultTech = t;
            var curTechnique = techniques[t];
            this[t] =
            {
                'passes': []
            };
            var numPasses = curTechnique.length;
            var i = 0;
            while (i < numPasses) {
                var program = this.buildProgram(curTechnique[i]);
                this.ctx.useProgram(program);

                // automatically create a parameter def for every active attribute in the shader.
                var numAttribs = this.ctx.getProgramParameter(program, this.ctx.ACTIVE_ATTRIBUTES);
                for (j = 0; j < numAttribs; ++j) {
                    var attribInfo = this.ctx.getActiveAttrib(program, j);
                    curTechnique[i].attributes[attribInfo.name] = { 'type': RDGE.paramTypeNameMapping[attribInfo.type] };
                }
                // automatically create a parameter def for every active uniform in the shader.
                var numUniforms = this.ctx.getProgramParameter(program, this.ctx.ACTIVE_UNIFORMS);
                for (j = 0; j < numUniforms; ++j) {
                    var uniformInfo = this.ctx.getActiveUniform(program, j);
                    if (!RDGE.rdgeGlobalParameters[uniformInfo.name]) {
                        curTechnique[i].params[uniformInfo.name] = { 'type': RDGE.paramTypeNameMapping[uniformInfo.type] };
                    }
                }

                program.ctxId = this.ctx.canvas.rdgeid;
                if (!program) {
                    this.renderer.console.log("Build errors found in technique: " + t);
                    this.def[t] = null; // remove bad technique
                    break;
                } else {
                    this[t].passes.push({ "program": program, "params": {}, "defParams": {}, "states": curTechnique[i].states, "attributes": curTechnique[i].attribPairs });
                }

                // init default parameters
                for (var p in RDGE.rdgeGlobalParameters) {
                    var gp = new globalParam(this.ctx, p, RDGE.rdgeGlobalParameters[p], program);

                    if (gp.loc != null) {
                        gp.loc.ctxID = this.ctx.canvas.rdgeid;
                        this[t].passes[i].defParams[p] = gp;
                        this.global[p] = gp;
                    }
                }

                // attach light parameters and container to light context
                this[t].passes[i].lightParams = [null, null, null, null];
                this[t].passes[i].lightContext = [null, null, null, null];

                // attach a parameter list that will be used to optimize binding attributes
                if (!this[t].passes[i].paramsList)
                    this[t].passes[i].paramsList = [];

                // locate individual light parameters to bind with local context
                var totalLights = RDGE.rdgeConstants.MAX_MATERIAL_LIGHTS;
                for (var lightIdx = 0; lightIdx < totalLights; ++lightIdx) {

                    // clear parameter
                    this[t].passes[i].lightParams[lightIdx] = null;

                    // 0 = pos, 1 = diff, 2 = amb, 3 = spec
                    // this is order assumed for light parameters

                    // the parameter index key - lets us know which piece of data we are getting/setting
                    var lightDataIndex = 0;

                    for (var lp in RDGE.globals.engine.lightManager.lightUniforms[lightIdx]) {
                        loc = this.ctx.getUniformLocation(program, lp);

                        // if item found enable this light param and set parameters to bind and lookup data
                        if (loc != null) {
                            if (!this[t].passes[i].lightParams[lightIdx])
                                this[t].passes[i].lightParams[lightIdx] = [];

                            this[t].passes[i].lightParams[lightIdx].push({ 'loc': loc, 'name': lp, 'dataIndex': lightDataIndex });
                        }

                        lightDataIndex++;
                    }
                }



                // init user defined parameters
                for (var p in curTechnique[i].params) {
                    if (typeof curTechnique[i].params[p] == 'string') {
                        continue;
                    }

                    var newParam = new paramType(this.ctx, p, curTechnique[i].params, program, t);

                    // if(newParam.loc != null)
                    // {
                    this[t].passes[i].params[p] = newParam;
                    this[t][p] = newParam;
                    // }
                }

                // link up aliases
                for (var p in curTechnique[i].params) {
                    if (typeof curTechnique[i].params[p] == 'string') {
                        // this just redirects to an already existing parameter.
                        this[t][p] = this[t].passes[i].params[p];
                    }
                }

                i++;
            }
        }

        // create linear lists of parameters - optimization
        for (t in techniques) {
            var numPasses = this[t].passes.length;

            for (var i = 0; i < numPasses; ++i) {

                this[t].passes[i].defParamsList = [];

                for (var p in this[t].passes[i].params) {
                    var param = this[t].passes[i].params[p];
                    param.name = p;
                    this[t].passes[i].paramsList.push(param);
                }

                for (var p in this[t].passes[i].defParams) {
                    var param = this[t].passes[i].defParams[p];
                    param.name = p;
                    this[t].passes[i].defParamsList.push(param);
                }
            }
        }

        this.setTechnique(defaultTech);
    };

    /*
    *   Init a local parameter at any time during the life of the jshader.
    *  This will add the parameter to the list of parameters to be bound
    *  before rendering
    */
    this.initLocalParameter = function (name, param) {
        var techniques = this.def.techniques;
        for (t in techniques) {
            var curTechnique = techniques[t];
            var numPasses = curTechnique.length;
            var i = 0;
            while (i < numPasses) {
                var newParam = new paramType(this.ctx, name, param, curTechnique[i].program, t);
                if (newParam) {
                    curTechnique[i][name] = newParam;

                    // this params list is created here because a parameter could be added before the jshader is initialized
                    if (!curTechnique[i].paramsList)
                        curTechnique[i].paramsList = [];

                    curTechnique[i].paramsList.push(newParam);
                }

                i++;
            }
        }
    };

    this.buildShader = function (shaderType, shaderStr) {
        // pre-pend preprocessor settings
        var preProcessor = "#define PC\n"
        preProcessor += shaderStr;
        shaderStr = preProcessor;

        // Create the shader object
        var shader = this.ctx.createShader(shaderType);
        if (shader == null) {
            this.renderer.console.log("*** Error: unable to create shader '" + shaderType + "'");
            return null;
        }

        // Load the shader source
        this.ctx.shaderSource(shader, shaderStr);

        // Compile the shader
        this.ctx.compileShader(shader);

        // Check the compile status
        var compiled = this.ctx.getShaderParameter(shader, this.ctx.COMPILE_STATUS);
        if (!compiled) {
            // compile failed, report error.
            var error = this.ctx.getShaderInfoLog(shader);
            window.console.error("*** Error compiling shader '" + shaderType + "':" + error);
            this.ctx.deleteShader(shader);
            return null;
        }

        return shader;
    };

    this.buildProgram = function (t) {
        window.console.log("building shader pair: <" + t.vshader + ", " + t.fshader + ">");
        var vShaderDef = RDGE.globals.engine.remapAssetFolder(this.def.shaders[t.vshader]);
        var fShaderDef = RDGE.globals.engine.remapAssetFolder(this.def.shaders[t.fshader]);

        this.ctx.useProgram(null);

        var vertexShader = null;
        var source = null;

        if (vShaderDef.indexOf('{') != -1) {
            source = vShaderDef;
        } else {
            var vshaderRequest = new XMLHttpRequest();
            var urlVertShader = vShaderDef;
            vshaderRequest.open("GET", urlVertShader, false);
            vshaderRequest.send(null);
            source = vshaderRequest.responseText;
        }

        vertexShader = this.buildShader(this.ctx.VERTEX_SHADER, source);

        var fragmentShader = null;
        var source = null;
        if (vShaderDef.indexOf('{') != -1) {
            source = fShaderDef;
        } else {
            var vshaderRequest = new XMLHttpRequest();
            var urlFragShader = fShaderDef;
            vshaderRequest.open("GET", urlFragShader, false);
            vshaderRequest.send(null);
            source = vshaderRequest.responseText;
        }

        fragmentShader = this.buildShader(this.ctx.FRAGMENT_SHADER, source);

        if (!vertexShader || !fragmentShader) {
            return null;
        }

        this.compiledShaders[t.vshader] = vertexShader;
        this.compiledShaders[t.fshader] = fragmentShader;

        // Create the program object
        var program = this.ctx.createProgram();
        if (!program) {
            return null;
        }

        // Attach our two shaders to the program
        this.ctx.attachShader(program, vertexShader);
        this.ctx.attachShader(program, fragmentShader);

        // Bind attributes
        var idx = 0;
        t.attribPairs = [];
        for (var i in t.attributes) {
            t.attribPairs.push({ 'loc': idx, 'name': i });
            this.ctx.bindAttribLocation(program, idx++, i);
        }

        // Link the program
        this.ctx.linkProgram(program);

        // Check the link status
        var linked = this.ctx.getProgramParameter(program, this.ctx.LINK_STATUS);
        if (!linked) {
            // failed to link
            var error = this.ctx.getProgramInfoLog(program);

            window.console.log("Error in program linking:" + error);

            this.ctx.deleteProgram(program);
            this.ctx.deleteProgram(fragmentShader);
            this.ctx.deleteProgram(vertexShader);

            return null;
        }

        return program;
    };

    /*
    *   Set the light nodes used by this jshader
    * array item 0 corresponds to light 0, item 1 tp light 1 and so on
    * place null for lights that are not there
    */
    this.setLightContext = function (lightRefArray) {
        for (t in this.technique) {
            var len = this.technique.passes.length;
            for (var i = 0; i < len; ++i) {
                this.technique.passes[i].lightContext = lightRefArray.slice();
            }
        }
    };

    /*
    *   Called by the system to add material textures settings to the jshader
    */
    this.setTextureContext = function (textureList) {
        var passCount = this.technique.passes.length;
        var param = null;

        for (var t = 0, texCount = textureList.length; t < texCount; ++t) {
            for (var i = 0; i < passCount; ++i) {
                var param = textureList[t];

                // set the rdge global parameters if the texture is in the list
                if (this.technique.passes[i].defParams[param.name])
                    this.technique.passes[i].defParams[param.name].set(param.handle);

                // and set the local parameters if the texture is in the list
                if (this.technique.passes[i].params[param.name])
                    this.technique.passes[i].params[param.name].set(param.data[0]);
            }
        }
    };

    this.setTechnique = function (name) {
        if (this[name] != undefined) {
            this.technique = this[name];
            return true;
        }

        this.ctx.console.log("Failed to set technique:" + name);
        return false;
    };

    this.beginRenderState = function (i) {
        var states = this.technique.passes[i].states;
        if (states == undefined) {
            return;
        }

        // depth enabled by default.
        var depthEnable = states.depthEnable != undefined ? states.depthEnable : true;
        if (!depthEnable) {
            this.ctx.disable(this.ctx.DEPTH_TEST);
            var depthFunc = states.depthFunc != undefined ? states.depthFunc : "LESS";
            this.ctx.depthFunc(this.ctx[states.depthFunc]);
            this.ctx.depthMask(true);
        }
        else {

            if (states.depthFunc) {
                this.ctx.depthFunc(this.ctx[states.depthFunc]);
            }

            if (states.offset) {
                this.ctx.enable(this.ctx.POLYGON_OFFSET_FILL);
                this.ctx.polygonOffset(states.offset[0], states.offset[1]);
            }

            // depth write
            if (states.depthWrite) {
                this.ctx.depthMask(states.depthWrite);
            }

            if (states.depthRangeMin) {
                this.ctx.depthRange(states.depthRangeMin);
            }

            if (states.depthRangeMax) {
                this.ctx.depthRange(states.depthRangeMax);
            }
        }

        // blend enabled by default.
        var blendEnabled = states.blendEnable != undefined ? states.blendEnable : false;
        if (blendEnabled) {
            var srcBlend = states.srcBlend != undefined ? states.srcBlend : "ONE"; // default src blend
            var dstBlend = states.dstBlend != undefined ? states.dstBlend : "ZERO"; // default dst blend
            this.ctx.enable(this.ctx.BLEND);
            this.ctx.blendFunc(this.ctx[srcBlend], this.ctx[dstBlend]);
        }

        if (states.culling) {
            if (states.culling)
                this.ctx.enable(this.ctx.CULL_FACE);
            else
                this.ctx.disable(this.ctx.CULL_FACE);

        }

        if (states.cullFace) {
            this.ctx.cullFace(this.ctx[states.cullFace]);
        }

        if (states.pointsprite) {
            if (states.pointsprite === true)
                this.renderer.enablePointSprites();
            else
                this.renderer.disablePointSprites();
        }

        this.resetRS = this.technique.passes[i].states.reset == undefined || this.technique.passes[i].states.reset == true;
    };

    this.endRenderState = function () {
        // restore render states to some default state.
        var ctx = this.ctx;
        if (this.resetRS) {
            ctx.enable(this.ctx.DEPTH_TEST);
            ctx.disable(this.ctx.BLEND);
            ctx.depthFunc(this.ctx.LESS);
            ctx.disable(this.ctx.POLYGON_OFFSET_FILL);
            ctx.disable(this.ctx.CULL_FACE);
            //            this.renderer.disablePointSprites();
            //ctx.enable(ctx.CULL_FACE);
            //ctx.cullFace(ctx.BACK);
        }
    };

    this.begin = function () {
        this.currentPass = null;
        if (this.def == null || this.technique == null) {
            return 0;
        }
        return this.technique.passes.length;
    };

    this.beginPass = function (i) {
        this.currentPass = this.technique.passes[i];
        this.ctx.useProgram(this.currentPass.program);
        this.bindParameters(this.currentPass);
        this.beginRenderState(i);
        return this.currentPass;
    };

    this.endPass = function () {
        this.endRenderState();
        this.ctx.useProgram(null);
    };

    this.end = function () {
    };

    this.exportShader = function () {

        for (t in this.def.techniques) {
            var numPasses = this[t].passes.length;

            for (var i = 0; i < numPasses; ++i) {
                this[t].passes[i].paramsList = [];
                this[t].passes[i].defParamsList = [];

                for (var p in this[t].passes[i].params) {
                    var tech = this.def.techniques[t][i];
                    if (tech && this[t].passes[i].params[p].type != "tex2d" && this[t].passes[i].params[p] != "texCube")
                        tech.params[p].data = this[t].passes[i].params[p].data;
                }
            }
        }

        return JSON.stringify(this.def);

    }
};