let gfx = cc.gfx;
// 颜色去除 + 物件图集合批
var vfmtPosUvIndex = new gfx.VertexFormat([
    { name: gfx.ATTR_POSITION, type: gfx.ATTR_TYPE_FLOAT32, num: 2 },
    { name: gfx.ATTR_UV0, type: gfx.ATTR_TYPE_FLOAT32, num: 2 },
    { name: "a_texture_idx", type: gfx.ATTR_TYPE_FLOAT32, num: 1 },
]);

// 仅物件图集合批
var vfmtPosUvColorIndex = new gfx.VertexFormat([
    { name: gfx.ATTR_POSITION, type: gfx.ATTR_TYPE_FLOAT32, num: 2 },
    { name: gfx.ATTR_UV0, type: gfx.ATTR_TYPE_FLOAT32, num: 2 },
    { name: "a_texture_idx", type: gfx.ATTR_TYPE_FLOAT32, num: 1 },
    { name: gfx.ATTR_COLOR, type: gfx.ATTR_TYPE_UINT8, num: 4, normalize: true },
]);

export class TmoAssembler extends cc.Assembler {
    init(comp) {
        super.init(comp);

        this._noColor = comp.isNoColor();
        this.updateColor = this._noColor ? undefined : this.updateColorFunc;

        this.uvOffset = 2;
        this.colorOffset = 5;
        this.textureIndexOffset = 4;
        this.floatsPerVert = this._noColor ? 5 : 6;

        this.indicesCount = 6;
        this.verticesCount = 4;

        this._renderData = new cc.RenderData();
        this._renderData.init(this);

        this.initLocal();
        this.initData();
    }

    initLocal() {
        this._local = [];
        this._local.length = 4;
    }

    initData() {
        let data = this._renderData;
        data.createFlexData(0, this.verticesCount, this.indicesCount, this.getVfmt());

        // 填充顶点索引信息
        let indices = data.iDatas[0];
        let count = indices.length / 6;
        for (let i = 0, idx = 0; i < count; i++) {
            let vertextID = i * 4;
            indices[idx++] = vertextID;
            indices[idx++] = vertextID + 1;
            indices[idx++] = vertextID + 2;
            indices[idx++] = vertextID + 1;
            indices[idx++] = vertextID + 3;
            indices[idx++] = vertextID + 2;
        }
    }

    getBuffer() {
        return cc.renderer._handle.getBuffer("mesh", this.getVfmt());
    }

    getVfmt() {
        return this._noColor ? vfmtPosUvIndex : vfmtPosUvColorIndex;
    }

    updateRenderData(sprite) {
        this.packToDynamicAtlas(sprite, sprite._spriteFrame);

        if (sprite._vertsDirty) {
            this.updateUVs(sprite);
            this.updateVerts(sprite);
            this.updateTextureIdx(sprite);
            sprite._vertsDirty = false;
        }
    }

    /**
     * 填充textureIndex数据
     * @param {cc.Sprite} sprite 
     */
    updateTextureIdx(sprite) {
        let textureIdx = sprite._textureIdx;
        let verts = this._renderData.vDatas[0];

        let verticesCount = this.verticesCount;
        let floatsPerVert = this.floatsPerVert;
        let textureIndexOffset = this.textureIndexOffset;

        for (let i = 0; i < verticesCount; i++) {
            let index = i * floatsPerVert + textureIndexOffset;
            verts[index] = textureIdx;
        }
    }

    updateUVs(sprite) {
        let uv = sprite._spriteFrame.uv;
        let uvOffset = this.uvOffset;
        let floatsPerVert = this.floatsPerVert;
        let verts = this._renderData.vDatas[0];
        for (let i = 0; i < 4; i++) {
            let srcOffset = i * 2;
            let dstOffset = floatsPerVert * i + uvOffset;
            verts[dstOffset] = uv[srcOffset];
            verts[dstOffset + 1] = uv[srcOffset + 1];
        }
    }

    updateVerts(sprite) {
        let node = sprite.node,
            cw = node.width, ch = node.height,
            appx = node.anchorX * cw, appy = node.anchorY * ch,
            l, b, r, t;
        if (sprite.trim) {
            l = -appx;
            b = -appy;
            r = cw - appx;
            t = ch - appy;
        }
        else {
            let frame = sprite.spriteFrame,
                ow = frame._originalSize.width, oh = frame._originalSize.height,
                rw = frame._rect.width, rh = frame._rect.height,
                offset = frame._offset,
                scaleX = cw / ow, scaleY = ch / oh;
            let trimLeft = offset.x + (ow - rw) / 2;
            let trimRight = offset.x - (ow - rw) / 2;
            let trimBottom = offset.y + (oh - rh) / 2;
            let trimTop = offset.y - (oh - rh) / 2;
            l = trimLeft * scaleX - appx;
            b = trimBottom * scaleY - appy;
            r = cw + trimRight * scaleX - appx;
            t = ch + trimTop * scaleY - appy;
        }

        let local = this._local;
        local[0] = l;
        local[1] = b;
        local[2] = r;
        local[3] = t;
        this.updateWorldVerts(sprite);
    }

    get verticesFloats() {
        return this.verticesCount * this.floatsPerVert;
    }

    updateColorFunc(comp, color) {
        let uintVerts = this._renderData.uintVDatas[0];
        if (!uintVerts) return;
        color = color != null ? color : comp.node.color._val;
        let floatsPerVert = this.floatsPerVert;
        let colorOffset = this.colorOffset;
        for (let i = colorOffset, l = uintVerts.length; i < l; i += floatsPerVert) {
            uintVerts[i] = color;
        }
    }

    updateWorldVerts(comp) {
        if (CC_NATIVERENDERER) {
            // 原生平台兼容代码 复制于jsb-engine.js中的cc.Assembler2D.prototype.updateWorldVerts
            var local = this._local;
            var verts = this._renderData.vDatas[0];
            var vl = local[0],
                vr = local[2],
                vb = local[1],
                vt = local[3];
            var floatsPerVert = this.floatsPerVert;
            var vertexOffset = 0; // left bottom

            verts[vertexOffset] = vl;
            verts[vertexOffset + 1] = vb;
            vertexOffset += floatsPerVert; // right bottom

            verts[vertexOffset] = vr;
            verts[vertexOffset + 1] = vb;
            vertexOffset += floatsPerVert; // left top

            verts[vertexOffset] = vl;
            verts[vertexOffset + 1] = vt;
            vertexOffset += floatsPerVert; // right top

            verts[vertexOffset] = vr;
            verts[vertexOffset + 1] = vt;
        } else {
            let local = this._local;
            let verts = this._renderData.vDatas[0];

            let matrix = comp.node._worldMatrix;
            let matrixm = matrix.m,
                a = matrixm[0], b = matrixm[1], c = matrixm[4], d = matrixm[5],
                tx = matrixm[12], ty = matrixm[13];

            let vl = local[0], vr = local[2],
                vb = local[1], vt = local[3];

            let floatsPerVert = this.floatsPerVert;
            let vertexOffset = 0;
            let justTranslate = a === 1 && b === 0 && c === 0 && d === 1;

            if (justTranslate) {
                // left bottom
                verts[vertexOffset] = vl + tx;
                verts[vertexOffset + 1] = vb + ty;
                vertexOffset += floatsPerVert;
                // right bottom
                verts[vertexOffset] = vr + tx;
                verts[vertexOffset + 1] = vb + ty;
                vertexOffset += floatsPerVert;
                // left top
                verts[vertexOffset] = vl + tx;
                verts[vertexOffset + 1] = vt + ty;
                vertexOffset += floatsPerVert;
                // right top
                verts[vertexOffset] = vr + tx;
                verts[vertexOffset + 1] = vt + ty;
            } else {
                let al = a * vl, ar = a * vr,
                    bl = b * vl, br = b * vr,
                    cb = c * vb, ct = c * vt,
                    db = d * vb, dt = d * vt;

                // left bottom
                verts[vertexOffset] = al + cb + tx;
                verts[vertexOffset + 1] = bl + db + ty;
                vertexOffset += floatsPerVert;
                // right bottom
                verts[vertexOffset] = ar + cb + tx;
                verts[vertexOffset + 1] = br + db + ty;
                vertexOffset += floatsPerVert;
                // left top
                verts[vertexOffset] = al + ct + tx;
                verts[vertexOffset + 1] = bl + dt + ty;
                vertexOffset += floatsPerVert;
                // right top
                verts[vertexOffset] = ar + ct + tx;
                verts[vertexOffset + 1] = br + dt + ty;
            }
        }
    }

    fillBuffers(comp, renderer) {
        if (renderer.worldMatDirty) {
            this.updateWorldVerts(comp);
        }

        let renderData = this._renderData;
        let vData = renderData.vDatas[0];
        let iData = renderData.iDatas[0];

        let buffer = this.getBuffer(renderer);
        let offsetInfo = buffer.request(this.verticesCount, this.indicesCount);

        // buffer data may be realloc, need get reference after request.

        // fill vertices
        let vertexOffset = offsetInfo.byteOffset >> 2,
            vbuf = buffer._vData;

        if (vData.length + vertexOffset > vbuf.length) {
            vbuf.set(vData.subarray(0, vbuf.length - vertexOffset), vertexOffset);
        } else {
            vbuf.set(vData, vertexOffset);
        }

        // fill indices
        let ibuf = buffer._iData,
            indiceOffset = offsetInfo.indiceOffset,
            vertexId = offsetInfo.vertexOffset;
        for (let i = 0, l = iData.length; i < l; i++) {
            ibuf[indiceOffset++] = vertexId + iData[i];
        }
    }

    packToDynamicAtlas(comp, frame) {
        if (CC_TEST) return;

        if (!frame._original && cc.dynamicAtlasManager && frame._texture.packable) {
            let packedFrame = cc.dynamicAtlasManager.insertSpriteFrame(frame);
            if (packedFrame) {
                frame._setDynamicAtlasFrame(packedFrame);
            }
        }
        let material = comp._materials[0];
        if (!material) return;

        if (material.getProperty('texture') !== frame._texture) {
            // texture was packed to dynamic atlas, should update uvs
            comp._vertsDirty = true;
            comp._updateMaterial();
        }
    }
}