
import { DisplayObject } from './DisplayObject';
import { Rectangle } from "../math/Rectangle";
import { Point } from '../math';
import CanvasRenderer from '../renderers/CanvasRenderer';
import { Event } from "../events/Event"
import { WebglRenderer } from '../renderers/WebglRenderer';
/**
 * 容器类
 * @class
 * @extends DisplayObject
 */
export default class Container extends DisplayObject {

    /**
     * 为false鼠标事件不再向下传递
     */
    mouseChildren: boolean = true;
    /**
     * 儿子们
     * @member {DisplayObject[]}
     * @readonly
     */
    children: any[];

    /**
     * 特殊用处
     * 缓存的Container的updateTransform
     */
    containerUpdateTransform;

    constructor() {
        super();
        this._instanceType = "Container";
        this.children = [];
    }

    /**
     * children改变时
     * @private
     */
    onChildrenChange(index) {
        /* empty */
        //子类需要时重写
    }

    /**
     * 添加child，返回的就是child
     * @param {DisplayObject} child
     * @return {DisplayObject}
     */
    addChild<T extends DisplayObject>(child: T): T {
        //默认添加在最顶层
        this.addChildAt(child, this.children.length);
        return child;
    }

    /**
     * 在相应index处添加child
     * @param {DisplayObject} child - The child to add
     * @param {number} index - The index to place the child in
     * @return {DisplayObject} The child that was added.
     */
    addChildAt<T extends DisplayObject>(child: T, index: number): T {
        if (!child) return;

        let s = this;
        let sameParent = (s == child.parent);
        let len: number;
        if (child.parent) {
            if (!sameParent) {
                child.parent.removeChild(child);
            } else {
                len = s.children.length;
                for (let i = 0; i < len; i++) {
                    if (s.children[i] == child) {
                        s.children.splice(i, 1);
                        break;
                    }
                }
            }
        }
        child.parent = s;

        //保证child的transform会被更新
        child.transform._parentID = -1;

        //确保包围盒重新计算
        this._boundsID++;

        len = s.children.length;
        if (index >= len) {
            s.children[s.children.length] = child;
            index = len;
        } else if (index == 0 || index < 0) {
            s.children.unshift(child);
            index = 0;
        } else {
            s.children.splice(index, 0, child);
        }
        if (s.stage && !sameParent) {
            // child["_cp"] = true;
            child._onDispatchBubbledEvent(Event.ADDED_TO_STAGE);
        }

        this.onChildrenChange(index);

        return child;
    }

    /**
     * 只用于交换索引
     * @param {DisplayObject} child - First display object to swap
     * @param {DisplayObject} child2 - Second display object to swap
     */
    swapChildren(child1: DisplayObject, child2: DisplayObject) {
        if (child1 === child2) {
            return;
        }
        let s = this;
        let id1 = -1;
        let id2 = -1;
        let childCount = s.children.length;
        if (typeof (child1) == "number") {
            id1 = child1;
        } else {
            id1 = s.getChildIndex(child1);
        }
        if (typeof (child2) == "number") {
            id2 = child2;
        } else {
            id2 = s.getChildIndex(child2);
        }
        if (id1 == id2 || id1 < 0 || id1 >= childCount || id2 < 0 || id2 >= childCount) {
            return false;
        } else {
            let temp: any = s.children[id1];
            s.children[id1] = s.children[id2];
            s.children[id2] = temp;

            this.onChildrenChange(id1 < id2 ? id1 : id2);
            return true;
        }

    }

    /**
     * 获取child的层级索引index
     * @param {DisplayObject} child - The DisplayObject instance to identify
     * @return {number} The index position of the child display object to identify
     */
    getChildIndex(child: DisplayObject): number {
        const index = this.children.indexOf(child);

        if (index === -1) {
            return null
        }
        return index;
    }

    /**
     * 设置child的层级
     * @param {DisplayObject} child 
     * @param {number} index
     */
    setChildIndex(child: DisplayObject, index: number) {
        this.addChildAt(child, index);
    }

    /**
     * Returns the child at the specified index
     * @param {number} index - The index to get the child at
     * @return {DisplayObject} The child at the given index, if any.
     */
    getChildAt(index: number): DisplayObject {
        if (index < 0 || index >= this.children.length) {
            return null
        }
        return this.children[index];
    }

    /**
     * 通过名字获取子级
     * @param name 
     * @param isOnlyOne 
     * @param isRecursive 
     */
    public getChildByName(name: string | RegExp, isOnlyOne: boolean = true, isRecursive: boolean = false): any {
        if (!name) return null;
        let s = this;
        let rex: any;
        if (typeof (name) == "string") {
            rex = new RegExp("^" + name + "$");
        } else {
            rex = name;
        }
        let elements: Array<DisplayObject> = [];
        Container._getElementsByName(rex, s, isOnlyOne, isRecursive, elements);
        let len = elements.length;
        if (len == 0) {
            return null;
        } else if (len == 1) {
            return elements[0];
        } else {
            return elements;
        }
    }

    /**
     * 移除child
     * @param {DisplayObject} child
     * @return {DisplayObject} 
     */
    removeChild(child: DisplayObject): DisplayObject {

        const index = this.children.indexOf(child);

        if (index === -1) return null;

        this.removeChildAt(index);

        return child;
    }

    /**
     * 在index处移除child
     * @param {number} index - The index to get the child from
     * @return {DisplayObject} The child that was removed.
     */
    removeChildAt(index: number): DisplayObject {
        let s = this;
        let child: any;
        let len = s.children.length - 1;
        if (len < 0) return;
        if (index == len) {
            child = s.children.pop();
        } else if (index == 0) {
            child = s.children.shift();
        } else {
            child = s.children.splice(index, 1)[0];
        }
        child._onDispatchBubbledEvent(Event.REMOVED_FROM_STAGE);

        //保证子级会被更新
        child.parent = null;
        child.transform._parentID = -1;

        //保证包围盒重新计算
        this._boundsID++;

        this.onChildrenChange(index);

        return child;
    }

    /**
     * 通过索引批量移除child
     * @param {number} [beginIndex=0]
     * @param {number} [endIndex=this.children.length]
     * @returns {DisplayObject[]} List of removed children
     */
    removeChildren(beginIndex: number = 0, endIndex: number = this.children.length): DisplayObject[] {
        const begin = beginIndex;
        const end = typeof endIndex === 'number' ? endIndex : this.children.length;
        const range = end - begin;
        let removed;

        if (range > 0 && range <= end) {
            removed = this.children.splice(begin, range);

            for (let i = 0; i < removed.length; ++i) {
                removed[i].parent = null;
                if (removed[i].transform) {
                    removed[i].transform._parentID = -1;
                }
            }

            this._boundsID++;

            this.onChildrenChange(beginIndex);

            for (let i = 0; i < removed.length; ++i) {
                removed[i]._onDispatchBubbledEvent(Event.REMOVED_FROM_STAGE);
            }

            return removed;
        }
        else if (range === 0 && this.children.length === 0) {
            return [];
        }

        throw new RangeError('removeChildren: numeric values are outside the acceptable range.');
    }

    /**
     * 更新矩阵
     */
    updateTransform() {
        //自己先算
        super.updateTransform();
        //考虑是否要加，
        //this._boundsID++;
        //children遍历计算
        for (let i = 0, j = this.children.length; i < j; ++i) {
            const child = this.children[i];
            if (child.visible) {
                child.updateTransform();
            }
        }
    }

    /**
     * 父类重写
     * 都是全局的
     */
    calculateBounds() {
        if (this._lastBoundsID == this._boundsID) return
        this._lastBoundsID = this._boundsID
        this._bounds.clear();
        //算自己的
        this._calculateBounds();
        for (let i = 0; i < this.children.length; i++) {
            const child = this.children[i];
            if (!child.visible || !child.renderable) {
                continue;
            }
            child.calculateBounds();
            if (child._mask) {
                child._mask.calculateBounds();
                //取交集矩形
                if (child._bounds.x < child._mask._bounds.x) {
                    child._bounds.x = child._mask._bounds.x;
                }
                if (child._bounds.y < child._mask._bounds.y) {
                    child._bounds.y = child._mask._bounds.y;
                }
                if (child._bounds.width > child._mask._bounds.width) {
                    child._bounds.width = child._mask._bounds.width;
                }
                if (child._bounds.height > child._mask._bounds.height) {
                    child._bounds.height = child._mask._bounds.height;
                }
                Rectangle.createFromRects(this._bounds, child._bounds);
            }
            //到时处理mask和filterArea
            else if (child.filterArea) {

            }
            else {
                Rectangle.createFromRects(this._bounds, child._bounds);
            }
        }

    }

    /**
     * 加"_"的方法基本是为了自己特殊处理
     */
    protected _calculateBounds() {
        //子类自己重写
    }

    /**
     * 检测点是否在任何child上
     * 重写父类方法
     */
    hitTestPoint(globalPoint: Point, isMouseEvent: boolean = false) {
        if (!this.visible) return null
        //如果禁止子级的鼠标事件
        if (isMouseEvent && !this.mouseChildren) return null;
        var children = this.children;
        var length = children.length
        let child, hitDisplayObject;
        //后序遍历，后添加的在上层
        for (var i = length - 1; i >= 0; i--) {
            child = children[i];
            //当作遮罩的不作为检测，跳过
            if (child.isUsedToMask) continue;
            //有遮罩，但是不在遮罩内，跳过
            if (child.mask && !child.mask.hitTestPoint(globalPoint, isMouseEvent)) continue;
            //检测
            hitDisplayObject = child.hitTestPoint(globalPoint, isMouseEvent);
            //存在直接返回
            if (hitDisplayObject) return hitDisplayObject;
        }
        return null;
    }

    /**
     * webgl渲染
     * @param {WebglRenderer} renderer - The renderer
     */
    renderWebGL(renderer: WebglRenderer) {
        //不可见，全局透明度为0，或者 不渲染，直接return
        if (!this.visible || this._worldAlpha <= 0 || !this.renderable) {
            return;
        }
        //是否有遮罩。到时如果有滤镜，
        if (this.mask || (this.filters && this.filters.length)) {
            this.renderAdvancedWebGL(renderer);
        }
        else {
            //自身先渲染
            this._renderWebGL(renderer);
            //遍历children
            for (let i = 0, j = this.children.length; i < j; ++i) {
                this.children[i].renderWebGL(renderer);
            }
        }
    }

    /**
     * 高级渲染方法
     *
     * @private
     * @param {WebGLRenderer} renderer - The renderer
     */
    private renderAdvancedWebGL(renderer: WebglRenderer) {
        //之前的批处理刷掉先
        renderer.batchManager.flush();

        //有滤镜再说
        const filters = this.filters;
        // push filter first as we need to ensure the stencil buffer is correct for any masking
        if (filters) {
            if (!this._enabledFilters) this._enabledFilters = [];
            //重置
            this._enabledFilters.length = 0;
            //筛选可用的
            for (let i = 0; i < filters.length; i++) {
                if (filters[i].enabled) this._enabledFilters.push(filters[i]);
            }
            if (this._enabledFilters.length) renderer.filterManager.pushFilter(this, this._enabledFilters);
        }

        const mask = this.mask;
        if (mask) {
            //先画遮罩
            renderer.maskManager.pushMask(this, this.mask);
        }

        //渲染自身
        this._renderWebGL(renderer);

        //遍历children
        for (let i = 0, j = this.children.length; i < j; i++) {
            this.children[i].renderWebGL(renderer);
        }

        //刷掉批处理
        renderer.batchManager.flush();

        if (mask) {
            //移除遮罩，支持多重遮罩
            renderer.maskManager.popMask(this, this.mask);
        }

        //移除滤镜
        if (filters && this._enabledFilters && this._enabledFilters.length) renderer.filterManager.popFilter();

    }

    /**
     * 自身渲染方式
     * @private
     * @param {WebglRenderer} renderer - The renderer
     */
    protected _renderWebGL(renderer: WebglRenderer) {
        //自身绘制方法
    }

    /**
     * canvas渲染方式
     * @param {CanvasRenderer} renderer - The renderer
     */
    renderCanvas(renderer: CanvasRenderer) {
        if (!this.visible || this._worldAlpha <= 0 || !this.renderable) {
            return;
        }

        if (this.mask) {
            renderer.maskManager.pushMask(this.mask);
        }

        this._renderCanvas(renderer);
        for (let i = 0, j = this.children.length; i < j; ++i) {
            this.children[i].renderCanvas(renderer);
        }

        if (this.mask) {
            renderer.maskManager.popMask(renderer);
        }
    }

    /**
     * 自身渲染方法
     *
     * @private
     * @param {CanvasRenderer} renderer - The renderer
     */
    protected _renderCanvas(renderer: CanvasRenderer) {
        //自身绘制方法
    }

    /**
     * 更新方法
     */
    update() {
        if (!this.visible) return;
        //更新自己的
        super.update()
        //更新儿子们的
        let len = this.children.length;
        for (let i = len - 1; i >= 0; i--) {
            const child = this.children[i];
            if (child.visible) child.update();
        }
    }


    /**
     * 调用此方法对自己及其child触发一次指定事件
     * @method _onDispatchBubbledEvent
     * @public
     * @param {string} type
     * @since 1.0.0
     */
    public _onDispatchBubbledEvent(type: string): void {
        let s = this;
        let len = s.children.length;
        if (type == Event.REMOVED_FROM_STAGE && !s.stage) return;
        super._onDispatchBubbledEvent(type);
        for (let i = 0; i < len; i++) {
            s.children[i]._onDispatchBubbledEvent(type);
        }

    }

    /**
     * 
     */
    destroy() {
        let s = this;
        //让子级也destroy
        for (let i = s.children.length - 1; i >= 0; i--) {
            s.children[i].destroy();
        }
        super.destroy();
        this.mouseChildren = false;
    }

    /**
     * 一般用于获取宽高并设置
     * 包括子级的,容器的尽量少用，子类可重写
     * @member {number}
     */
    get width() {
        return Math.abs(this.scale.x) * this.getLocalBounds().width;
    }

    set width(value) {
        const width = this.getLocalBounds().width;

        if (width !== 0) {
            this.scale.x = value / width;
        }
        else {
            this.scale.x = 1;
        }
        //子类有用，有_width,才需设置scaleX
        this._width = value
    }

    /**
     * 高度同width
     * @member {number}
     */
    get height() {
        return Math.abs(this.scale.y) * this.getLocalBounds().height;
    }

    set height(value) {
        const height = this.getLocalBounds().height;

        if (height !== 0) {
            this.scale.y = value / height;
        }
        else {
            this.scale.y = 1;
        }

        this._height = value
    }

    //全局遍历
    /**
     * @method _getElementsByName
     * @param {RegExp} rex
     * @param {Container} root
     * @param {boolean} isOnlyOne
     * @param {boolean} isRecursive
     * @param {Array<DisplayObject>} resultList
     * @private
     * @static
     */
    private static _getElementsByName(rex: RegExp, root: Container, isOnlyOne: boolean, isRecursive: boolean, resultList: Array<DisplayObject>): void {
        let len = root.children.length;
        if (len > 0) {
            let name: string;
            let child: any;
            for (let i = 0; i < len; i++) {
                child = root.children[i];
                name = child.name;
                if (name && name != "") {
                    if (rex.test(name)) {
                        resultList[resultList.length] = child;
                        if (isOnlyOne) {
                            return;
                        }
                    }
                }
                if (isRecursive) {
                    if (child["children"] != null) {
                        Container._getElementsByName(rex, child, isOnlyOne, isRecursive, resultList);
                    }
                }
            }
        }
    }
}

Container.prototype.containerUpdateTransform = Container.prototype.updateTransform;
