
// state object//
import { setVertexAttribArrays } from './setVertexAttribArrays';

/**
 * Helper class to work with WebGL VertexArrayObjects (vaos)
 * Only works if WebGL extensions are enabled (they usually are)
 * 核心类VAOs
 * @class
 * @memberof glCore
 * @param gl {WebGLRenderingContext} The current WebGL rendering context
 */
export class VertexArrayObject {
    nativeVaoExtension: any;
    nativeState: any;
    nativeVao: any;
    /**
     * 当前上下文
     */
    gl: WebGLRenderingContext;
    /**
     * An array of attributes
     * attributes数组
     */
    attributes: any[];

    /**
     * 索引buffer
     *  @member {GLBuffer}
     */
    indexBuffer: any;
    /**
     * A boolean flag
     */
    dirty: boolean;
    constructor(gl: WebGLRenderingContext, state) {
        this.nativeVaoExtension = null;

        //不要求必须使用原生时，基本都支持扩展
        if (!VertexArrayObject.FORCE_NATIVE) {
            this.nativeVaoExtension = gl.getExtension('OES_vertex_array_object') ||
                gl.getExtension('MOZ_OES_vertex_array_object') ||
                gl.getExtension('WEBKIT_OES_vertex_array_object');
        }

        this.nativeState = state;

        if (this.nativeVaoExtension) {
            this.nativeVao = this.nativeVaoExtension.createVertexArrayOES();

            var maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);

            // VAO - overwrite the state..
            this.nativeState = {
                tempAttribState: new Array(maxAttribs),
                attribState: new Array(maxAttribs)
            };
        }

        this.gl = gl;

        this.attributes = [];

        this.indexBuffer = null;

        this.dirty = false;
    };

    /**
     * Binds the buffer
     * 绑定数据
     */
    public bind() {
        if (this.nativeVao) {
            this.nativeVaoExtension.bindVertexArrayOES(this.nativeVao);

            if (this.dirty) {
                this.dirty = false;
                this.activate();
                return this;
            }
            if (this.indexBuffer) {
                this.indexBuffer.bind();
            }
        }
        else {
            this.activate();
        }

        return this;
    };

    /**
     * Unbinds the buffer
     * 解绑数据
     */
    public unbind() {
        if (this.nativeVao) {
            this.nativeVaoExtension.bindVertexArrayOES(null);
        }

        return this;
    };

    /**
     * Uses this vao
     * 激活vao
     */
    public activate() {

        var gl = this.gl;
        var lastBuffer = null;

        for (var i = 0; i < this.attributes.length; i++) {
            var attrib = this.attributes[i];

            if (lastBuffer !== attrib.buffer) {
                attrib.buffer.bind();
                lastBuffer = attrib.buffer;
            }

            // gl.vertexAttribPointer(attrib.attribute.location,
            //     attrib.attribute.size,
            //     attrib.type || gl.FLOAT,
            //     attrib.normalized || false,
            //     attrib.stride || 0,
            //     attrib.start || 0);
            attrib.attribute.pointer(
                attrib.type || gl.FLOAT,
                attrib.normalized || false,
                attrib.stride || 0,
                attrib.start || 0
            )
        }

        setVertexAttribArrays(gl, this.attributes, this.nativeState);

        if (this.indexBuffer) {
            this.indexBuffer.bind();
        }

        return this;
    };

    /**
     * 添加attribute
     * @param buffer     {gl.GLBuffer}
     * @param attribute  {*}
     * @param type       {String}
     * @param normalized {Boolean}
     * @param stride     {Number}
     * @param start      {Number}
     */
    public addAttribute(buffer, attribute, type?, normalized?, stride?, start?) {
        this.attributes.push({
            buffer: buffer,
            attribute: attribute,

            location: attribute.location,
            type: type || this.gl.FLOAT,
            normalized: normalized || false,
            stride: stride || 0,
            start: start || 0
        });

        this.dirty = true;

        return this;
    };

    /**
     * 添加索引数据
     * @param buffer   {gl.GLBuffer}
     */
    public addIndex(buffer/*, options*/) {
        this.indexBuffer = buffer;

        this.dirty = true;

        return this;
    };

    /**
     * Unbinds this vao and disables it
     * 解绑废弃vao
     */
    public clear() {
        // var gl = this.gl;

        // TODO - should this function unbind after clear?
        // for now, no but lets see what happens in the real world!
        if (this.nativeVao) {
            this.nativeVaoExtension.bindVertexArrayOES(this.nativeVao);
        }

        this.attributes.length = 0;
        this.indexBuffer = null;

        return this;
    };

    /**
     * 执行绘制
     * @param type  {Number} gl.TRIANGLES\gl.TRIANGLE_STRIP等
     * @param size  {Number} 个数
     * @param start {Number} 偏移
     */
    public draw(type, size?, start?) {
        var gl = this.gl;

        if (this.indexBuffer) {
            //有索引 https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/drawElements
            gl.drawElements(type, size || this.indexBuffer.data.length, gl.UNSIGNED_SHORT, (start || 0) * 2);
        }
        else {
            //无索引 https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/drawArrays
            // TODO need a better way to calculate size..
            gl.drawArrays(type, start || 0, size || this.getSize());
        }

        return this;
    };

    /**
     * Destroy this vao
     */
    public destroy() {
        // lose references
        this.gl = null;
        this.indexBuffer = null;
        this.attributes = null;
        this.nativeState = null;

        if (this.nativeVao) {
            this.nativeVaoExtension.deleteVertexArrayOES(this.nativeVao);
        }

        this.nativeVaoExtension = null;
        this.nativeVao = null;
    };

    public getSize() {
        var attrib = this.attributes[0];
        // return attrib.buffer.data.length / ((attrib.stride / 4) || attrib.attribute.size);
        //ArrayBuffer没有length，要不要考虑0？
        return (attrib.buffer.data.length || attrib.buffer.data.byteLength / 4) / ((attrib.stride / 4) || attrib.attribute.size);
    };

    /**
    * Some devices behave a bit funny when using the newer extensions (im looking at you ipad 2!)
    * If you find on older devices that things have gone a bit weird then set this to true.
    */
    /**
     * Lets the VAO know if you should use the WebGL extension or the native methods.
     * Some devices behave a bit funny when using the newer extensions (im looking at you ipad 2!)
     * If you find on older devices that things have gone a bit weird then set this to true.
     * @static
     * @property {Boolean} FORCE_NATIVE
     */
    public static FORCE_NATIVE = false;

}




