//////////////////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2014-present, Egret Technology.
//  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 the Egret 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 EGRET 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 EGRET AND 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.
//
//////////////////////////////////////////////////////////////////////////////////////
namespace egret.native {
    let blendModesForGL = {
        "source-over": [1, 771],
        "lighter": [770, 1],
        "destination-out": [0, 771],
        "destination-in": [0, 770]
    };
    /**
     * @version Egret 2.4
     * @platform Web,Native
     * @private
     */
    export class NativeCanvasRenderContext extends HashObject {

        private $matrix:Matrix = new Matrix();

        public $nativeContext:any = null;

        /**
         * @private
         * 与绘图上线文关联的画布实例
         * @version Egret 2.4
         * @platform Web,Native
         */
        canvas:NativeCanvas;
        private $globalCompositeOperation:string = "source-over";

        /**
         * @private
         * 设置新图像如何绘制到已有的图像上的规制
         * @version Egret 2.4
         * @platform Web,Native
         */
        public get globalCompositeOperation():string {
            return this.$globalCompositeOperation;
        }

        public set globalCompositeOperation(value:string) {
            this.$globalCompositeOperation = value;
            let arr = blendModesForGL[value];
            if (arr) {
                $cmdManager.setContext(this.$nativeContext);
                $cmdManager.setBlendArg(arr[0], arr[1]);
            }
        }

        private $globalAlpha:number = 1;

        /**
         * @private
         * 设置接下来绘图填充的整体透明度
         * @version Egret 2.4
         * @platform Web,Native
         */
        public get globalAlpha():number {
            return this.$globalAlpha;
        }

        public set globalAlpha(value:number) {
            this.$globalAlpha = value;
            $cmdManager.setContext(this.$nativeContext);
            $cmdManager.setGlobalAlpha(value);
        }

        /**
         * @private
         * 用于表示剪切斜接的极限值的数字。
         * @default 10
         * @version Egret 2.4
         * @platform Web,Native
         */
        public miterLimit:number;
        /**
         * @private
         * 指定如何绘制每一条线段末端的属性。有3个可能的值，分别是：<br/>
         * <ul>
         * <li>"butt": 线段末端以方形结束。</li>
         * <li>"round": 线段末端以圆形结束。</li>
         * <li>"square": 线段末端以方形结束，但是增加了一个宽度和线段相同，高度是线段厚度一半的矩形区域。</li>
         * </ul>
         * @default "butt"
         * @version Egret 2.4
         * @platform Web,Native
         */
        public lineCap:string;
        /**
         * @private
         * 指定用于拐角的连接外观的类型,有3个可能的值，分别是：<br/>
         * <ul>
         * <li>"round": 圆角连接</li>
         * <li>"bevel": 斜角连接。</li>
         * <li>"miter": 尖角连接。当使用尖角模式时，还可以同时使用 miterLimit 参数限制尖角的长度。</li>
         * </ul>
         * @default "miter"
         * @version Egret 2.4
         * @platform Web,Native
         */
        public lineJoin:string;

        private $lineWidth:number = 0;

        /**
         * @private
         * 设置线条粗细，以像素为单位。设置为0，负数，Infinity 或 NaN 将会被忽略。
         * @default 1
         * @version Egret 2.4
         * @platform Web,Native
         */
        public get lineWidth():number {
            return this.$lineWidth;
        }

        public set lineWidth(value:number) {
            //console.log("set lineWidth" + value);
            this.$lineWidth = value;
            $cmdManager.setContext(this.$nativeContext);
            $cmdManager.setLineWidth(value);
        }

        private $strokeStyle:any = "#000000";

        /**
         * @private
         * 设置要在图形边线填充的颜色或样式
         * @default "#000000"
         * @version Egret 2.4
         * @platform Web,Native
         */
        public get strokeStyle():any {
            return this.$strokeStyle;
        }

        public set strokeStyle(value:any) {
            this.$strokeStyle = value;
            if (value != null) {
                if (value.indexOf("rgba") != -1) {
                    value = this.$parseRGBA(value);
                }
                else if (value.indexOf("rgb") != -1) {
                    value = this.$parseRGB(value);
                }
                $cmdManager.setContext(egret_native.Label);
                $cmdManager.setStrokeColor(parseInt(value.replace("#", "0x")));
            }
            $cmdManager.setContext(this.$nativeContext);
            let s1 = $cmdManager.pushString(value);
            $cmdManager.setStrokeStyle(s1);
        }

        private $fillStyle:any = "#000000";

        /**
         * @private
         * 设置要在图形内部填充的颜色或样式
         * @default "#000000"
         * @version Egret 2.4
         * @platform Web,Native
         */
        public get fillStyle():any {
            return this.$fillStyle;
        }

        public set fillStyle(value:any) {
            this.$fillStyle = value;
            if (value != null) {
                if (value.indexOf("rgba") != -1) {
                    value = this.$parseRGBA(value);
                }
                else if (value.indexOf("rgb") != -1) {
                    value = this.$parseRGB(value);
                }
                $cmdManager.setContext(egret_native.Label);
                $cmdManager.setTextColor(parseInt(value.replace("#", "0x")));
            }
            $cmdManager.setContext(this.$nativeContext);
            let s1 = $cmdManager.pushString(value);
            $cmdManager.setFillStyle(s1);
        }

        private $fillColorStr(s:string):string {
            if (s.length < 2) {
                s = "0" + s;
            }
            return s;
        }

        private $parseRGBA(str:string):string {
            let index:number = str.indexOf("(");
            str = str.slice(index + 1, str.length - 1);
            let arr:string[] = str.split(",");
            let a:string = parseInt(<any>(parseFloat(arr[3]) * 255)).toString(16);
            let r:string = parseInt(arr[0]).toString(16);
            let g:string = parseInt(arr[1]).toString(16);
            let b:string = parseInt(arr[2]).toString(16);
            str = "#" + this.$fillColorStr(a) + this.$fillColorStr(r) + this.$fillColorStr(g) + this.$fillColorStr(b);
            return str;
        }

        private $parseRGB(str:string):string {
            let index:number = str.indexOf("(");
            str = str.slice(index + 1, str.length - 1);
            let arr:string[] = str.split(",");
            let r:string = parseInt(arr[0]).toString(16);
            let g:string = parseInt(arr[1]).toString(16);
            let b:string = parseInt(arr[2]).toString(16);
            str = "#" + this.$fillColorStr(r) + this.$fillColorStr(g) + this.$fillColorStr(b);
            return str;
        }

        /**
         * @private
         * 控制在缩放时是否对位图进行平滑处理。
         * @default true
         * @version Egret 2.4
         * @platform Web,Native
         */
        public imageSmoothingEnabled:boolean;
        /**
         * @private
         * 文本的对齐方式的属性,有5个可能的值，分别是：<br/>
         * <ul>
         * <li>"left" 文本左对齐。</li>
         * <li>"right" 文本右对齐。</li>
         * <li>"center" 文本居中对齐。</li>
         * <li>"start" 文本对齐界线开始的地方 （对于从左向右阅读的语言使用左对齐，对从右向左的阅读的语言使用右对齐）。</li>
         * <li>"end" 文本对齐界线结束的地方 （对于从左向右阅读的语言使用右对齐，对从右向左的阅读的语言使用左对齐）。</li>
         * </ul>
         * @default "start"
         * @version Egret 2.4
         * @platform Web,Native
         */
        public textAlign:string;
        /**
         * @private
         * 决定文字垂直方向的对齐方式。有6个可能的值，分别是：<br/>
         * <ul>
         * <li>"top" 文本基线在文本块的顶部。</li>
         * <li>"hanging" 文本基线是悬挂基线。</li>
         * <li>"middle" 文本基线在文本块的中间。</li>
         * <li>"alphabetic" 文本基线是标准的字母基线。</li>
         * <li>"ideographic" 文字基线是表意字基线；如果字符本身超出了alphabetic 基线，那么ideograhpic基线位置在字符本身的底部。</li>
         * <li>"bottom" 文本基线在文本块的底部。 与 ideographic 基线的区别在于 ideographic 基线不需要考虑下行字母。</li>
         * </ul>
         * @default "alphabetic"
         * @version Egret 2.4
         * @platform Web,Native
         */
        public textBaseline:string;

        private $font:string = "normal normal 10px sans-serif";
        private $fontSize:number = 10;
        private $fontFamily:string = "";

        /**
         * @private
         * 当前的字体样式
         * @version Egret 2.4
         * @platform Web,Native
         */
        public get font():string {
            return this.$font;
        }

        public set font(value:string) {
            this.$font = value;
            let arr:string[] = value.split(" ");
            let sizeTxt:string = arr[2];
            if (sizeTxt.indexOf("px") != -1) {
                this.$fontSize = parseInt(sizeTxt.replace("px", ""));
                //console.log("set font" + this.$lineWidth);
            }
            if(useFontMapping) {
                let fontFamilyText:string;
                if(arr.length == 4) {
                    fontFamilyText = arr[3];
                }
                else {
                    fontFamilyText = arr.slice(3).join(" ");
                }
                let arr2;
                if(fontFamilyText.indexOf(", ") != -1) {
                    arr2 = fontFamilyText.split(", ");
                }
                else if(fontFamilyText.indexOf(",") != -1) {
                    arr2 = fontFamilyText.split(",");
                }
                if(arr2) {
                    let length:number = arr2.length;
                    for(let i = 0 ; i < length ; i++) {
                        let fontFamily = arr2[i];
                        //暂时先不考虑带有引号的情况
                        if(fontMapping[fontFamily]) {
                            this.$fontFamily = fontMapping[fontFamily];
                            return;
                        }
                    }
                }
                else {
                    this.$fontFamily = fontMapping[fontFamilyText];
                }
                if(!this.$fontFamily) {
                    this.$fontFamily = "/system/fonts/DroidSansFallback.ttf";
                }
            }
            else {
                //兼容旧版本直接将 default_fontFamily 设置为字体路径的情况
                this.$fontFamily = TextField.default_fontFamily;
            }
        }

        /**
         * @private
         * 绘制一段圆弧路径。圆弧路径的圆心在 (x, y) 位置，半径为 r ，根据anticlockwise （默认为顺时针）指定的方向从 startAngle 开始绘制，到 endAngle 结束。
         * @param x 圆弧中心（圆心）的 x 轴坐标。
         * @param y 圆弧中心（圆心）的 y 轴坐标。
         * @param radius 圆弧的半径。
         * @param startAngle 圆弧的起始点， x轴方向开始计算，单位以弧度表示。
         * @param endAngle 圆弧的重点， 单位以弧度表示。
         * @param anticlockwise 如果为 true，逆时针绘制圆弧，反之，顺时针绘制。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public arc(x:number, y:number, radius:number, startAngle:number, endAngle:number, anticlockwise?:boolean):void {
            $cmdManager.setContext(this.$nativeContext);
            $cmdManager.arc(x, y, radius, startAngle, endAngle, anticlockwise ? 1 : 0);
            // this.$nativeContext.arc(x, y, radius, startAngle, endAngle, anticlockwise);
        }

        /**
         * @private
         * 绘制一段二次贝塞尔曲线路径。它需要2个点。 第一个点是控制点，第二个点是终点。 起始点是当前路径最新的点，当创建二次贝赛尔曲线之前，可以使用 moveTo() 方法进行改变。
         * @param cpx 控制点的 x 轴坐标。
         * @param cpy 控制点的 y 轴坐标。
         * @param x 终点的 x 轴坐标。
         * @param y 终点的 y 轴坐标。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public quadraticCurveTo(cpx:number, cpy:number, x:number, y:number):void {
            //console.log("quadraticCurveTo " + cpx + " " + cpy + " " + x + " " + y);
            $cmdManager.setContext(this.$nativeContext);
            $cmdManager.quadraticCurveTo(cpx, cpy, x, y);
            // this.$nativeContext.quadraticCurveTo(cpx, cpy, x, y);
        }

        /**
         * @private
         * 使用直线连接子路径的终点到x，y坐标。
         * @param x 直线终点的 x 轴坐标。
         * @param y 直线终点的 y 轴坐标。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public lineTo(x:number, y:number):void {
            //console.log("lineTo " + x + " " + y);
            $cmdManager.setContext(this.$nativeContext);
            $cmdManager.lineTo(x, y);
        }

        /**
         * @private
         * 根据当前的填充样式，填充当前或已存在的路径的方法。采取非零环绕或者奇偶环绕规则。
         * @param fillRule 一种算法，决定点是在路径内还是在路径外。允许的值：
         * "nonzero": 非零环绕规则， 默认的规则。
         * "evenodd": 奇偶环绕规则。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public fill(fillRule?:string):void {
            $cmdManager.setContext(this.$nativeContext);
            let s1 = $cmdManager.pushString(fillRule);
            $cmdManager.fill(s1);
        }

        /**
         * @private
         * 使笔点返回到当前子路径的起始点。它尝试从当前点到起始点绘制一条直线。如果图形已经是封闭的或者只有一个点，那么此方法不会做任何操作。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public closePath():void {
            $cmdManager.setContext(this.$nativeContext);
            $cmdManager.closePath();
            if (this.clipRectArray) {
                this.$clipRectArray = this.clipRectArray;
                this.clipRectArray = null;
            }
        }

        /**
         * @private
         * 创建一段矩形路径，矩形的起点位置是 (x, y) ，尺寸为 width 和 height。矩形的4个点通过直线连接，子路径做为闭合的标记，所以你可以填充或者描边矩形。
         * @param x 矩形起点的 x 轴坐标。
         * @param y 矩形起点的 y 轴坐标。
         * @param width 矩形的宽度。
         * @param height 矩形的高度。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public rect(x:number, y:number, w:number, h:number):void {
            $cmdManager.setContext(this.$nativeContext);
            $cmdManager.rect(x, y, w, h);
            this.$clipRectArray.push({x: x, y: y, w: w, h: h});
        }

        /**
         * @private
         * 将一个新的子路径的起始点移动到(x，y)坐标
         * @param x 点的 x 轴
         * @param y 点的 y 轴
         * @version Egret 2.4
         * @platform Web,Native
         */
        public moveTo(x:number, y:number):void {
            $cmdManager.setContext(this.$nativeContext);
            $cmdManager.moveTo(x, y);
        }

        public setLineDash(segments: number[]):void {
            // $cmdManager.setLineDash(segments);
        }

        /**
         * @private
         * 绘制一个填充矩形。矩形的起点在 (x, y) 位置，矩形的尺寸是 width 和 height ，fillStyle 属性决定矩形的样式。
         * @param x 矩形起始点的 x 轴坐标。
         * @param y 矩形起始点的 y 轴坐标。
         * @param width 矩形的宽度。
         * @param height 矩形的高度。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public fillRect(x:number, y:number, w:number, h:number):void {
            $cmdManager.setContext(this.$nativeContext);
            $cmdManager.fillRect(x, y, w, h);
            // this.$nativeContext.fillRect(x, y, w, h);
        }

        /**
         * @private
         * 绘制一段三次贝赛尔曲线路径。该方法需要三个点。 第一、第二个点是控制点，第三个点是结束点。起始点是当前路径的最后一个点，
         * 绘制贝赛尔曲线前，可以通过调用 moveTo() 进行修改。
         * @param cp1x 第一个控制点的 x 轴坐标。
         * @param cp1y 第一个控制点的 y 轴坐标。
         * @param cp2x 第二个控制点的 x 轴坐标。
         * @param cp2y 第二个控制点的 y 轴坐标。
         * @param x 结束点的 x 轴坐标。
         * @param y 结束点的 y 轴坐标。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public bezierCurveTo(cp1x:number, cp1y:number, cp2x:number, cp2y:number, x:number, y:number):void {
            $cmdManager.setContext(this.$nativeContext);
            $cmdManager.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
            // this.$nativeContext.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
        }

        /**
         * @private
         * 根据当前的画线样式，绘制当前或已经存在的路径的方法。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public stroke():void {
            $cmdManager.setContext(this.$nativeContext);
            $cmdManager.stroke();
            // this.$nativeContext.stroke();
        }

        /**
         * @private
         * 使用当前的绘画样式，描绘一个起点在 (x, y) 、宽度为 w 、高度为 h 的矩形的方法。
         * @param x 矩形起点的 x 轴坐标。
         * @param y 矩形起点的 y 轴坐标。
         * @param w 矩形的宽度。
         * @param h 矩形的高度。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public strokeRect(x:number, y:number, w:number, h:number):void {
            //console.log("strokeRect");
            $cmdManager.setContext(this.$nativeContext);
            $cmdManager.strokeRect(x, y, w, h);
            // this.$nativeContext.strokeRect(x, y, w, h);
        }

        private clipRectArray = null;

        /**
         * @private
         * 清空子路径列表开始一个新路径。 当你想创建一个新的路径时，调用此方法。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public beginPath():void {
            $cmdManager.setContext(this.$nativeContext);
            $cmdManager.beginPath();
            this.clipRectArray = this.$clipRectArray.concat();
        }

        /**
         * @private
         * 根据控制点和半径绘制一段圆弧路径，使用直线连接前一个点。
         * @param x1 第一个控制点的 x 轴坐标。
         * @param y1 第一个控制点的 y 轴坐标。
         * @param x2 第二个控制点的 x 轴坐标。
         * @param y2 第二个控制点的 y 轴坐标。
         * @param radius 圆弧的半径。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public arcTo(x1:number, y1:number, x2:number, y2:number, radius:number):void {
            this.$nativeContext.arcTo(x1, y1, x2, y2, radius);
        }

        /**
         * @private
         * 使用方法参数描述的矩阵多次叠加当前的变换矩阵。
         * @param a 水平缩放。
         * @param b 水平倾斜。
         * @param c 垂直倾斜。
         * @param d 垂直缩放。
         * @param tx 水平移动。
         * @param ty 垂直移动。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public transform(a:number, b:number, c:number, d:number, tx:number, ty:number):void {
            this.$matrix.append(a, b, c, d, tx, ty);
            this.setTransformToNative();
        }

        /**
         * @private
         * 通过在网格中移动 surface 和 surface 原点 x 水平方向、原点 y 垂直方向，添加平移变换
         * @param x 水平移动。
         * @param y 垂直移动。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public translate(x:number, y:number):void {
            this.$matrix.translate(x, y);
            this.setTransformToNative();
        }

        /**
         * @private
         * 根据 x 水平方向和 y 垂直方向，为 surface 单位添加缩放变换。
         * @param x 水平方向的缩放因子。
         * @param y 垂直方向的缩放因子。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public scale(x:number, y:number):void {
            this.$matrix.scale(x, y);
            this.setTransformToNative();
        }

        /**
         * @private
         * 在变换矩阵中增加旋转，角度变量表示一个顺时针旋转角度并且用弧度表示。
         * @param angle 顺时针旋转的弧度。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public rotate(angle:number):void {
            this.$matrix.rotate(angle);
            this.setTransformToNative();
        }

        /**
         * @private
         * 恢复到最近的绘制样式状态，此状态是通过 save() 保存到”状态栈“中最新的元素。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public restore():void {
            //console.log("restore");
            if (this.$saveList.length) {
                let data = this.$saveList.pop();
                for (let key in data) {
                    this[key] = data[key];
                }
                this.setTransformToNative();
                $cmdManager.setContext(this.$nativeContext);
                $cmdManager.restore();
                this.clipRectArray = null;
            }

        }

        private $saveList:any[] = [];

        /**
         * @private
         * 使用栈保存当前的绘画样式状态，你可以使用 restore() 恢复任何改变。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public save():void {
            //console.log("save");
            let transformMatrix = new Matrix();
            transformMatrix.copyFrom(this.$matrix);
            this.$saveList.push({
                lineWidth: this.$lineWidth,
                globalCompositeOperation: this.$globalCompositeOperation,
                globalAlpha: this.$globalAlpha,
                strokeStyle: this.$strokeStyle,
                fillStyle: this.$fillStyle,
                font: this.$font,
                $matrix: transformMatrix,
                $clipRectArray: this.$clipRectArray.concat()
            });
            $cmdManager.setContext(this.$nativeContext);
            $cmdManager.save();
        }


        private $clipRectArray:any[] = [];

        private $clipRect:Rectangle = new Rectangle();
        private $saveCount:number = 0;
        private $clipList:number[] = [];


        /**
         * @private
         * 从当前路径创建一个剪切路径。在 clip() 调用之后，绘制的所有信息只会出现在剪切路径内部。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public clip(fillRule?:string):void {
            if (this.$clipRectArray.length > 0) {
                let arr = [];
                for (let i:number = 0; i < this.$clipRectArray.length; i++) {
                    let clipRect = this.$clipRectArray[i];
                    arr.push(clipRect.x);
                    arr.push(clipRect.y);
                    arr.push(clipRect.w);
                    arr.push(clipRect.h);
                }
                //console.log("pushRectStencils " + arr.toString());
                $cmdManager.setContext(this.$nativeContext);
                $cmdManager.pushRectStencils(arr);
                this.$clipRectArray.length = 0;
            }
        }

        /**
         * @private
         * 设置指定矩形区域内（以 点 (x, y) 为起点，范围是(width, height) ）所有像素变成透明，并擦除之前绘制的所有内容。
         * @param x 矩形起点的 x 轴坐标。
         * @param y 矩形起点的 y 轴坐标。
         * @param width 矩形的宽度。
         * @param height 矩形的高度。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public clearRect(x:number, y:number, width:number, height:number):void {
            //console.log("clearRect x:" + x + " y:" +  y + " width:" + width + " height:" + height);
            $cmdManager.setContext(this.$nativeContext);
            $cmdManager.clearRect(x, y, width, height);
        }

        /**
         * @private
         * 重新设置当前的变换为单位矩阵，并使用同样的变量调用 transform() 方法。
         * @param a 水平缩放。
         * @param b 水平倾斜。
         * @param c 垂直倾斜。
         * @param d 垂直缩放。
         * @param tx 水平移动。
         * @param ty 垂直移动。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public setTransform(a:number, b:number, c:number, d:number, tx:number, ty:number):void {
            this.$matrix.setTo(a, b, c, d, tx, ty);
            this.setTransformToNative();
        }

        private setTransformToNative():void {
            let m = this.$matrix;
            //console.log("setTransformToNative::a=" + m.a + " b=" + m.b + " c=" + m.c + " d=" + m.d + " tx=" + m.tx + " ty=" + m.ty);
            $cmdManager.setContext(this.$nativeContext);
            $cmdManager.setTransform(m.a, m.b, m.c, m.d, m.tx, m.ty);
        }
        
        private savedMatrix:Matrix = new Matrix();
        
        /**
         * @private
         * 保存矩阵，这里只能保存一次，嵌套无效
         */
        public saveTransform():void {
            this.savedMatrix.copyFrom(this.$matrix);
        }

        /**
         * @private
         * 保存矩阵，这里只能保存一次，嵌套无效
         */
        public restoreTransform():void {
            this.$matrix.copyFrom(this.savedMatrix);
        }

        /**
         * @private
         * 创建一个沿参数坐标指定的直线的渐变。该方法返回一个线性的 GraphicsGradient 对象。
         * @param x0 起点的 x 轴坐标。
         * @param y0 起点的 y 轴坐标。
         * @param x1 终点的 x 轴坐标。
         * @param y1 终点的 y 轴坐标。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public createLinearGradient(x0:number, y0:number, x1:number, y1:number):CanvasGradient {
            return this.$nativeContext.createLinearGradient(x0, y0, x1, y1);
        }

        /**
         * @private
         * 根据参数确定的两个圆的坐标，创建一个放射性渐变。该方法返回一个放射性的 GraphicsGradient。
         * @param x0 开始圆形的 x 轴坐标。
         * @param y0 开始圆形的 y 轴坐标。
         * @param r0 开始圆形的半径。
         * @param x1 结束圆形的 x 轴坐标。
         * @param y1 结束圆形的 y 轴坐标。
         * @param r1 结束圆形的半径。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public createRadialGradient(x0:number, y0:number, r0:number, x1:number, y1:number, r1:number):CanvasGradient {
            return this.$nativeContext.createRadialGradient(x0, y0, r0, x1, y1, r1);
        }

        /**
         * @private
         * 在(x,y)位置绘制（填充）文本。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public fillText(text:string, x:number, y:number, maxWidth?:number):void {
            //console.log("drawText" + text);
            $cmdManager.setContext(this.$nativeContext);
            let s1 = $cmdManager.pushString(this.$fontFamily);
            let s2 = $cmdManager.pushString("");
            $cmdManager.createLabel(s1, this.$fontSize, s2, this.$hasStrokeText ? this.$lineWidth : 0);
            this.$hasStrokeText = false;
            let s3 = $cmdManager.pushString(text);
            $cmdManager.drawText(s3, x, y);
        }

        private $hasStrokeText:boolean = false;

        public strokeText(text:string, x:number, y:number, maxWidth?:number):void {
            this.$hasStrokeText = true;
        }

        /**
         * @private
         * 测量指定文本宽度，返回 TextMetrics 对象。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public measureText(text:string):TextMetrics {
            $cmdManager.setContext(egret_native.Label);
            let s1 = $cmdManager.pushString(this.$fontFamily);
            let s2 = $cmdManager.pushString("");
            $cmdManager.createLabel(s1, this.$fontSize, s2, this.$hasStrokeText ? this.$lineWidth : 0);
            //同步更新
            $cmdManager.flush();
            return {width: egret_native.Label.getTextSize(text)[0]};
        }

        /**
         * @private
         * 注意：如果要对绘制的图片进行缩放，出于性能优化考虑，系统不会主动去每次重置imageSmoothingEnabled属性，因此您在调用drawImage()方法前请务必
         * 确保 imageSmoothingEnabled 已被重置为正常的值，否则有可能沿用上个显示对象绘制过程留下的值。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public drawImage(image:BitmapData | NativeCanvas, offsetX:number, offsetY:number, width?:number, height?:number,
                         surfaceOffsetX?:number, surfaceOffsetY?:number, surfaceImageWidth?:number, surfaceImageHeight?:number):void {
            let bitmapData;
            let isNative:boolean;
            if ((<NativeCanvas><any>image).$nativeCanvas) {
                bitmapData = (<NativeCanvas><any>image).$nativeCanvas;
                isNative = true;
            }
            else {
                bitmapData = image;
                isNative = false;
            }
            if (!bitmapData) {
                return;
            }
            if (arguments.length == 3) {
                surfaceOffsetX = offsetX;
                surfaceOffsetY = offsetY;
                offsetX = 0;
                offsetY = 0;
                width = surfaceImageWidth = image.width;
                height = surfaceImageHeight = image.height;
            }
            else if (arguments.length == 5) {
                surfaceOffsetX = offsetX;
                surfaceOffsetY = offsetY;
                surfaceImageWidth = width;
                surfaceImageHeight = height;
                offsetX = 0;
                offsetY = 0;
                width = image.width;
                height = image.height;
            }
            else {
                if (width == void 0) {
                    width = image.width;
                }
                if (height == void 0) {
                    height = image.height;
                }
                if (surfaceOffsetX == void 0) {
                    surfaceOffsetX = 0;
                }
                if (surfaceOffsetY == void 0) {
                    surfaceOffsetY = 0;
                }
                if (surfaceImageWidth == void 0) {
                    surfaceImageWidth = width;
                }
                if (surfaceImageHeight == void 0) {
                    surfaceImageHeight = height;
                }
            }
            //console.log("drawImage::" + offsetX + " " + offsetY + " " + width + " " + height + " " + surfaceOffsetX + " " + surfaceOffsetY + " " + surfaceImageWidth + " " + surfaceImageHeight);
            //console.log("drawImage::" + bitmapData);
            
            let imageAdress;
            if(!isNative) {
                if(!bitmapData._native_tex_loc) {
                    bitmapData._native_tex_loc = bitmapData.___native_texture__p;
                }
                imageAdress = bitmapData._native_tex_loc;
            } else {
                imageAdress = bitmapData.___native_texture__p;
            }
            
            native.$cmdManager.setContext(this.$nativeContext);
            $cmdManager.drawImage(imageAdress, offsetX, offsetY, width, height, surfaceOffsetX, surfaceOffsetY, surfaceImageWidth, surfaceImageHeight);
        }

        /**
         * @private
         * draw mesh
         */
        public drawMesh(image, offsetX, offsetY, width, height, surfaceOffsetX, surfaceOffsetY, surfaceImageWidth, surfaceImageHeight,
                    textureSourceWidth, textureSourceHeight, meshUVs, meshVertices, meshIndices):void {
            let bitmapData;
            if (image.$nativeCanvas) {
                bitmapData = image.$nativeCanvas;
            }
            else {
                bitmapData = image;
            }
            if (!bitmapData) {
                return;
            }
            if (arguments.length == 3) {
                surfaceOffsetX = offsetX;
                surfaceOffsetY = offsetY;
                offsetX = 0;
                offsetY = 0;
                width = surfaceImageWidth = image.width;
                height = surfaceImageHeight = image.height;
            }
            else if (arguments.length == 5) {
                surfaceOffsetX = offsetX;
                surfaceOffsetY = offsetY;
                surfaceImageWidth = width;
                surfaceImageHeight = height;
                offsetX = 0;
                offsetY = 0;
                width = image.width;
                height = image.height;
            }
            else {
                if (!width) {
                    width = image.width;
                }
                if (!height) {
                    height = image.height;
                }
                if (!surfaceOffsetX) {
                    surfaceOffsetX = 0;
                }
                if (!surfaceOffsetY) {
                    surfaceOffsetY = 0;
                }
                if (!surfaceImageWidth) {
                    surfaceImageWidth = width;
                }
                if (!surfaceImageHeight) {
                    surfaceImageHeight = height;
                }
            }

            this.vertices = new Float32Array(meshVertices.length / 2 * 5);
            this.indicesForMesh = new Uint32Array(meshIndices.length);
            this.cacheArrays(this.$matrix, 1, offsetX, offsetY, width, height, surfaceOffsetX, surfaceOffsetY,
                surfaceImageWidth, surfaceImageHeight, textureSourceWidth, textureSourceHeight, meshUVs, meshVertices, meshIndices);

            // 打断批渲染
            $cmdManager.flush();

            this.$nativeContext.drawMesh(bitmapData, this.vertices, this.indicesForMesh, this.vertices.length, this.indicesForMesh.length);
        }

        private vertices:Float32Array;
        private indicesForMesh:Float32Array;

        private cacheArrays(transform, alpha, sourceX, sourceY, sourceWidth, sourceHeight, destX, destY, destWidth, destHeight,
                    textureSourceWidth, textureSourceHeight, meshUVs, meshVertices, meshIndices) {
            //计算出绘制矩阵，之后把矩阵还原回之前的
            let locWorldTransform = transform;
            let originalA = locWorldTransform.a;
            let originalB = locWorldTransform.b;
            let originalC = locWorldTransform.c;
            let originalD = locWorldTransform.d;
            let originalTx = locWorldTransform.tx;
            let originalTy = locWorldTransform.ty;
            if (destX != 0 || destY != 0) {
                locWorldTransform.append(1, 0, 0, 1, destX, destY);
            }
            if (sourceWidth / destWidth != 1 || sourceHeight / destHeight != 1) {
                locWorldTransform.append(destWidth / sourceWidth, 0, 0, destHeight / sourceHeight, 0, 0);
            }
            let a = locWorldTransform.a;
            let b = locWorldTransform.b;
            let c = locWorldTransform.c;
            let d = locWorldTransform.d;
            let tx = locWorldTransform.tx;
            let ty = locWorldTransform.ty;
            locWorldTransform.a = originalA;
            locWorldTransform.b = originalB;
            locWorldTransform.c = originalC;
            locWorldTransform.d = originalD;
            locWorldTransform.tx = originalTx;
            locWorldTransform.ty = originalTy;
            if (meshVertices) {
                // 计算索引位置与赋值
                let vertices = this.vertices;
                // 缓存顶点数组
                let i = 0, iD = 0, l = 0;
                let u = 0, v = 0, x = 0, y = 0;
                for (i = 0, l = meshUVs.length; i < l; i += 2) {
                    iD = i * 5 / 2;
                    x = meshVertices[i];
                    y = meshVertices[i + 1];
                    u = meshUVs[i];
                    v = meshUVs[i + 1];
                    // xy
                    vertices[iD + 0] = a * x + c * y + tx;
                    vertices[iD + 1] = b * x + d * y + ty;
                    // uv
                    vertices[iD + 2] = (sourceX + u * sourceWidth) / textureSourceWidth;
                    vertices[iD + 3] = (sourceY + v * sourceHeight) / textureSourceHeight;
                    // alpha
                    vertices[iD + 4] = alpha;
                }
                for (i = 0; i < meshIndices.length; i++) {
                    this.indicesForMesh[i] = meshIndices[i];
                }
            }
            else {
                console.log("meshVertices not exist");
            }
        }

        /**
         * @private
         * 基于指定的源图象(BitmapData)创建一个模板，通过repetition参数指定源图像在什么方向上进行重复，返回一个GraphicsPattern对象。
         * @param bitmapData 做为重复图像源的 BitmapData 对象。
         * @param repetition 指定如何重复图像。
         * 可能的值有："repeat" (两个方向重复),"repeat-x" (仅水平方向重复),"repeat-y" (仅垂直方向重复),"no-repeat" (不重复).
         * @version Egret 2.4
         * @platform Web,Native
         */
        public createPattern(image:BitmapData, repetition:string):CanvasPattern {
            return null;
        }

        /**
         * @private
         * 返回一个 ImageData 对象，用来描述canvas区域隐含的像素数据，这个区域通过矩形表示，起始点为(sx, sy)、宽为sw、高为sh。
         * @version Egret 2.4
         * @platform Web,Native
         */
        public getImageData(sx:number, sy:number, sw:number, sh:number):ImageData {

            $cmdManager.flush();

            let res;
            if (sx != Math.floor(sx)) {
                sx = Math.floor(sx);
                sw++;
            }
            if (sy != Math.floor(sy)) {
                sy = Math.floor(sy);
                sh++;
            }
            res = this.$nativeContext.getPixels(sx, sy, sw, sh);
            if (res.pixelData) {
                res.data = res.pixelData;
            }
            return res;
        }

        /**
         * @private
         * 设置全局shader
         * @param filter filter属性生成的json
         */
        public setGlobalShader(filter:egret.Filter):void {
            $cmdManager.setContext(this.$nativeContext);

            let s1;
            if(filter) {
                s1 = $cmdManager.pushString(filter.$toJson());
            } else {
                s1 = $cmdManager.pushString("");
            }
            
            $cmdManager.setGlobalShader(s1);
        }
    }
}