import Container from "./Container";
import { RENDERER_TYPE, VERSION } from "../const"
// import SystemRenderer from "../renderers/SystemRenderer";
import { Rectangle, Point } from "../math";
import { EventDispatcher } from "../events/EventDispatcher";
import { Event } from "../events/Event";
// import { FloatDisplay } from "./FloatDisplay";
import { DisplayObject } from "./DisplayObject";
import { MouseEvent } from "../events/MouseEvent";

import { WebglRenderer } from "../renderers/WebglRenderer";

import { CanvasRenderer } from "../renderers/CanvasRenderer";
import { SystemRenderer } from "../renderers/SystemRenderer";
import { getEnv } from "../utils";

export class Stage extends Container {

    /**
     * 当前stage所使用的渲染器
     * 渲染器有两种,一种是canvas 一种是webGl
     * @property renderObj
     * @public
     * @since 1.0.0
     * @type {IRender}
     * @default null
     */
    public renderObj: SystemRenderer = null;


    /**
     * 直接获取stage的引用，避免总是从Event.ADD_TO_STAGE 事件中去获取stage引用
     * @property getStage
     * @param {string} stageName
     * @return {any}
     * @since 2.0.0
     */
    public static getStage(stageName: string = "cusEngine"): Stage {
        return Stage._stageList[stageName];
    }

    /**
     * @property _stageList
     * @static
     * @type {Object}
     * @private
     */
    private static _stageList: any = {};

    /**
     * 舞台在设备里截取后的可见区域,有些时候知道可见区域是非常重要的,因为这样你就可以根据舞台的可见区域做自适应了。
     * @property viewRect
     * @public
     * @readonly
     * @since 1.0.0
     * @type {Rectangle}
     * @default {x:0,y:0,width:0,height:0}
     * @readonly
     */
    public viewRect: Rectangle = new Rectangle();

    /**
     * 舞台的尺寸宽,也就是我们常说的设计尺寸
     * @property desWidth
     * @public
     * @since 1.0.0
     * @default 320
     * @type {number}
     * @readonly
     */
    public desWidth: number = 0;
    /**
     * 舞台的尺寸高,也就是我们常说的设计尺寸
     * @property desHeight
     * @public
     * @since 1.0.0
     * @default 240
     * @type {number}
     * @readonly
     */
    public desHeight: number = 0;
    /**
     * 舞台在当前设备中的真实高
     * @property divHeight
     * @public
     * @since 1.0.0
     * @default 320
     * @type {number}
     * @readonly
     */
    public divHeight: number = 0;
    /**
     * 舞台在当前设备中的真实宽
     * @property divWidth
     * @public
     * @since 1.0.0
     * @default 240
     * @readonly
     * @type {number}
     */
    public divWidth: number = 0;
    /**
     * 舞台的背景色
     * 默认就是透明背景
     * 可能设置一个颜色值改变舞台背景
     * @property bgColor
     * @public
     * @since 1.0.0
     * @type {number}
     * @default "";
     */
    private _bgColor: number = 0x000000;

    public get bgColor(): number {
        return this._bgColor;
    }
    /**
     * 设置颜色，即改变渲染器颜色
     */
    public set bgColor(value: number) {
        if (this._bgColor === value) return
        this._bgColor = value;
        this.renderObj.backgroundColor = value;
    }

    /**
     * 上一次鼠标或触碰经过的显示对象列表
     * @property _lastDpList
     * @type {Object}
     * @private
     */
    private _lastDpList: any = {};

    dpi: number

    private canvas: HTMLCanvasElement;
    private _canvasOffsetX: number = 0;
    private _canvasOffsetY: number = 0;
    /**
     * canvas模式下有问题，画布没按style缩小
     * @param {gl} context 
     * @param {*} data desWidth,desHeight,divWidth,divHeight,canWidth,canHeight
     */
    public constructor(
        canvas: HTMLCanvasElement,
        desWidth: number = 750,
        desHeight: number = 1624,
        divWidth: number,
        divHeight: number,
        renderType: RENDERER_TYPE = RENDERER_TYPE.CANVAS,
    ) {
        super();
        this.canvas = canvas;
        let s: Stage = this;
        this._instanceType = "Stage";
        Stage._stageList["canvas"] = s;
        s.stage = this;
        s.name = "stageInstance_" + s.instanceId;
        s.desWidth = desWidth || 750;
        s.desHeight = desHeight || 1206;
        s.divWidth = divWidth;
        s.divHeight = divHeight;

        //@ts-ignore淘宝小程序环境下提前记录canvas偏移，到时需要修改id传入，再说把
        if (getEnv() == "tb") my.createSelectorQuery().select('#canvas').boundingClientRect().exec((r) => {
            s._canvasOffsetX = r[0].left;
            s._canvasOffsetY = r[0].top;
        });

        // console.log("%c ", "background: url(http://5b0988e595225.cdn.sohucs.com/images/20180315/d41842ad9b5443d3854a480ea49f3b09.gif) no-repeat center;padding-left:80px;padding-bottom: 80px;border-radius:50%;")
        //打印个版本号
        console.log(
            "%c版本：" + VERSION,
            // "text-shadow: 0 1px 0 #ccc,0 2px 0 #c9c9c9,0 3px 0 #bbb,0 4px 0 #b9b9b9,0 5px 0 #aaa,0 6px 1px rgba(0,0,0,.1),0 0 5px rgba(0,0,0,.1),0 1px 3px rgba(0,0,0,.3),0 3px 5px rgba(0,0,0,.2),0 5px 10px rgba(0,0,0,.25),0 10px 10px rgba(0,0,0,.2),0 20px 20px rgba(0,0,0,.15);font-size:3em"
        )

        // my.alert({
        //   title: '55729:'+data.canWidth+","+data.canHeight
        // });
        // s.renderObj = new CanvasRenderer(context, data.canWidth, data.canHeight);
        if (renderType == RENDERER_TYPE.CANVAS) {
            let context = canvas.getContext("2d");
            s.renderObj = new CanvasRenderer(context, canvas.width, canvas.height);
        } else {
            let context = canvas.getContext("webgl", {
                alpha: true,
                antialias: false,//抗锯齿尽量别开，耗性能
                premultipliedAlpha: true,  //一般png图片都不会预乘alpha，所以必为true,除非有些图集工具选择了premultipliedAlpha
                stencil: true,
                // preserveDrawingBuffer: this.options.preserveDrawingBuffer,//需要toDataUrl时要开
                // powerPreference: this.options.powerPreference,
            });
            s.renderObj = new WebglRenderer(context, canvas.width, canvas.height);
        }

        s.dpi = canvas.width / divWidth

        //不加刚好置顶
        s.anchorX = desWidth >> 1;
        s.anchorY = desHeight >> 1;
        s.x = (canvas.width - desWidth) >> 1;
        s.y = (canvas.height - desHeight) >> 1;
        //定宽
        var scale = canvas.width / desWidth;
        s.scaleX = scale;
        s.scaleY = scale;

        s.viewRect = new Rectangle();
        s.viewRect.x = (desWidth - canvas.width / scale) >> 1;
        s.viewRect.y = (desHeight - canvas.height / scale) >> 1;
        s.viewRect.width = desWidth - s.viewRect.x * 2;
        s.viewRect.height = desHeight - s.viewRect.y * 2;
        // my.alert({
        //   title: StageManager.ins.width
        // });

        setTimeout(function () {
            //派发事件
            s.dispatchEvent(Event.INIT_STAGE);
        }, 100);
    }


    /**
     * 这个是鼠标事件的MouseEvent对象池,因为如果用户有监听鼠标事件,如果不建立对象池,那每一秒将会new Fps个数的事件对象,影响性能
     * @property _ml
     * @type {Array}
     * @private
     */
    private _ml: any = [];
    /**
     * 这个是事件中用到的Point对象池,以提高性能
     * @property _mp
     * @type {Array}
     * @private
     */
    private _mp: any = [];

    /**
     * 刷新mouse或者touch事件
     * @method _initMouseEvent
     * @private
     */
    private _initMouseEvent(event: MouseEvent, cp: Point, sp: Point, identifier: number): void {
        event["_pd"] = false;
        event.clientX = cp.x;
        event.clientY = cp.y;
        event.stageX = sp.x;
        event.stageY = sp.y;
        event.identifier = identifier;
    }

    //每一个手指事件的对象池
    /**
     * @property _mouseDownPoint
     * @type {Object}
     * @private
     */
    private _mouseDownPoint: any = {};

    /**
     * 循环刷新页面的函数
     * @method flush
     * @private
     * @return {void}
     */
    public flush(): void {
        this.renderObj.render(this);
    }

    /**
     * html的鼠标或单点触摸对应的引擎事件类型名
     * @property _mouseEventTypes
     * @type {{mousedown: string, mouseup: string, mousemove: string, touchstart: string, touchmove: string, touchend: string}}
     * @private
     */
    private _mouseEventTypes: any = {
        touchstart: "onMouseDown",
        touchmove: "onMouseMove",
        touchend: "onMouseUp",
        touchStart: "onMouseDown",//小程序返回的时间名是驼峰的
        touchMove: "onMouseMove",
        touchEnd: "onMouseUp"
    };

    /**
     * 无多指，无拖动
     * @method onMouseEvent
     * @param e
     * @private
     */
    onMouseEvent(e: any): void {
        //web环境才有
        if (getEnv() == "web") e.preventDefault();
        let s: Stage = this;
        //检查mouse或touch事件是否有，如果有的话，就触发事件函数
        if (EventDispatcher._totalMEC > 0) {
            let points: any;
            //事件类型

            let item = s._mouseEventTypes[e.type];
            let events: any;
            let event: any;
            //stageMousePoint
            let sp: Point;
            //localPoint;
            let lp: Point;
            //clientPoint
            let cp: Point;
            //事件个数
            let eLen: number;
            let identifier: any;

            points = [e.changedTouches[0]];
            //  points = [e.touches[0]];
            // my.alert({
            //   title: '55729:' + points.length
            // });
            let offSetX = s._canvasOffsetX, offSetY = s._canvasOffsetY;
            //web环境实时获取
            if (getEnv() == "web") {
                let doc = document.documentElement;
                let box = s.canvas.getBoundingClientRect();
                offSetX = box.left + window.pageXOffset - doc.clientLeft;
                offSetY = box.top + window.pageYOffset - doc.clientTop;
            }


            for (let o = 0; o < points.length; o++) {
                if (!points[o]) continue;
                eLen = 0;
                events = [];
                identifier = "m" + points[o].identifier;
                if (s._mp.length > 0) {
                    cp = s._mp.shift();
                } else {
                    cp = new Point();
                }
                cp.x = ((points[o].pageX || points[o].x || points[o].b) - offSetX) * s.dpi// devicePixelRatio;
                cp.y = ((points[o].pageY || points[o].y || points[o].c) - offSetY) * s.dpi// devicePixelRatio;
                // my.alert({
                //   title: JSON.stringify(points[o])
                // });
                //计算舞台中的点
                sp = s.globalToLocal(cp, DisplayObject._bp);
                //检查是否有鼠标事件
                if (EventDispatcher.getMouseEventCount() > 0) {
                    if (!s._ml[eLen]) {
                        event = new MouseEvent(item);
                        s._ml[eLen] = event;
                    } else {
                        event = s._ml[eLen];
                        event.type = item;
                    }
                    events[events.length] = event;
                    s._initMouseEvent(event, cp, sp, identifier);
                    eLen++;
                }
                if (item == "onMouseDown") {
                    s._mouseDownPoint[identifier] = cp;
                    //清空上次存在的显示列表
                } else if (item == "onMouseUp") {
                    if (s._mouseDownPoint[identifier]) {
                        if (Point.distance(s._mouseDownPoint[identifier], cp) < 20) {
                            //检查是否有添加对应的click事件
                            if (EventDispatcher.getMouseEventCount("onMouseClick") > 0) {
                                if (!s._ml[eLen]) {
                                    event = new MouseEvent("onMouseClick");
                                    s._ml[eLen] = event;
                                } else {
                                    event = s._ml[eLen];
                                    event.type = "onMouseClick";
                                }
                                events[events.length] = event;
                                s._initMouseEvent(event, cp, sp, identifier);
                                eLen++;
                            }
                        }
                    }
                }
                if (eLen > 0) {
                    //有事件开始遍历显示列表
                    //找出最底层的显示对象
                    let d: DisplayObject = s.hitTestPoint(cp, true);
                    // console.log(d)
                    let displayList: Array<DisplayObject> = [];
                    // my.alert({
                    //   title: '55729:' + d.instanceId
                    // });
                    if (d) {
                        //证明有点击到事件,然后从最底层追上来,看看一路是否有人添加过mouse或touch事件,还要考虑mousechildren和阻止事件方法
                        //找出真正的target,因为有些父级可能会mouseChildren=false;
                        while (d) {
                            if (d["mouseChildren"] === false) {
                                //丢掉之前的层级,因为根本没用了
                                displayList.length = 0;
                            }
                            displayList[displayList.length] = d;
                            d = d.parent;
                        }
                    } else {
                        displayList[displayList.length] = s;
                    }
                    let len: number = displayList.length;
                    for (let i = len - 1; i >= 0; i--) {
                        d = displayList[i];
                        for (let j = 0; j < eLen; j++) {
                            if (!events[j]["_pd"]) {
                                //有事件，且mouseEnable为true
                                if (d.hasEventListener(events[j].type) && d.mouseEnable) {
                                    events[j].target = d;
                                    events[j].currentTarget = displayList[0];
                                    lp = d.globalToLocal(cp, DisplayObject._bp);
                                    events[j].localX = lp.x;
                                    events[j].localY = lp.y;
                                    d.dispatchEvent(events[j]);
                                }
                            }
                        }
                    }
                    //这里一定要反转一下，因为会影响mouseOut mouseOver
                    displayList.reverse();
                    for (let i = len - 1; i >= 0; i--) {
                        d = displayList[i];
                        for (let j = 0; j < eLen; j++) {
                            if (!events[j]["_pd"]) {
                                //有事件，且mouseEnable为true
                                if (d.hasEventListener(events[j].type, false) && d.mouseEnable) {
                                    events[j].target = d;
                                    events[j].currentTarget = displayList[eLen - 1];
                                    lp = d.globalToLocal(cp, DisplayObject._bp);
                                    events[j].localX = lp.x;
                                    events[j].localY = lp.y;
                                    d.dispatchEvent(events[j], null, false);
                                }
                            }
                        }
                    }
                    //最后要和上一次的遍历者对比下，如果不相同则要触发onMouseOver和onMouseOut
                    if (item != "onMouseDown") {
                        if (EventDispatcher.getMouseEventCount("onMouseOver") > 0 || EventDispatcher.getMouseEventCount("onMouseOut") > 0) {
                            if (s._lastDpList[identifier]) {
                                //从第二个开始，因为第一个对象始终是stage顶级对象
                                let len1 = s._lastDpList[identifier].length;
                                let len2 = displayList.length;
                                len = len1 > len2 ? len1 : len2;
                                let isDiff = false;
                                let overEvent: MouseEvent;
                                let outEvent: MouseEvent;
                                for (let i = 1; i < len; i++) {
                                    if (!isDiff) {
                                        if (s._lastDpList[identifier][i] != displayList[i]) {
                                            //确定哪些有onMouseOver,哪些有onMouseOut
                                            isDiff = true;
                                            if (!s._ml[eLen]) {
                                                overEvent = new MouseEvent("onMouseOver");
                                                s._ml[eLen] = overEvent;
                                            } else {
                                                overEvent = s._ml[eLen];
                                                overEvent.type = "onMouseOver";
                                            }
                                            s._initMouseEvent(overEvent, cp, sp, identifier);
                                            eLen++;
                                            if (!s._ml[eLen]) {
                                                outEvent = new MouseEvent("onMouseOut");
                                                s._ml[eLen] = outEvent;
                                            } else {
                                                outEvent = s._ml[eLen];
                                                outEvent.type = "onMouseOut";
                                            }
                                            s._initMouseEvent(outEvent, cp, sp, identifier);
                                        }
                                    }
                                    if (isDiff) {
                                        if (s._lastDpList[identifier][i]) {
                                            //触发onMouseOut事件
                                            if (!outEvent["_pd"]) {
                                                d = s._lastDpList[identifier][i];
                                                if (d.hasEventListener("onMouseOut")) {
                                                    outEvent.currentTarget = d;
                                                    outEvent.target = s._lastDpList[identifier][len1 - 1];
                                                    lp = d.globalToLocal(cp, DisplayObject._bp);
                                                    outEvent.localX = lp.x;
                                                    outEvent.localY = lp.y;
                                                    d.dispatchEvent(outEvent);
                                                }
                                            }
                                        }
                                        if (displayList[i]) {
                                            //触发onMouseOver事件
                                            if (!overEvent["_pd"]) {
                                                d = displayList[i];
                                                if (d.hasEventListener("onMouseOver")) {
                                                    overEvent.currentTarget = d;
                                                    overEvent.target = displayList[len2 - 1];
                                                    lp = d.globalToLocal(cp, DisplayObject._bp);
                                                    overEvent.localX = lp.x;
                                                    overEvent.localY = lp.y;
                                                    d.dispatchEvent(overEvent);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        s._mp[s._mp.length] = cp;
                    }
                    if (item == "onMouseUp") {
                        delete s._mouseDownPoint[identifier];
                        delete s._lastDpList[identifier];
                    } else {
                        s._lastDpList[identifier] = displayList;
                    }
                }
            }
        }
    };

    public getBounds(): Rectangle {
        return this.viewRect;
    }

    public destroy(): void {
        let s = this;
        s.renderObj.destroy();
        s.renderObj = null;
        s.viewRect = null;
        s._lastDpList = null;
        s._ml = null;
        super.destroy();
    }
}