import { EventDispatcher } from '../events/EventDispatcher';

import { Rectangle, Transform, Point, ObservablePoint, Matrix } from '../math';
import { Event } from "../events/Event";
import Graphics from '../graphics/Graphics';
import { RAD_TO_DEG, DEG_TO_RAD } from '../const';
import { Filter } from '../filter';
import { Stage } from './Stage';
import Container from './Container';
/**
 * 基础显示对象抽象类
 * @class
 * @extends EventDispatcher
 */
export class DisplayObject extends EventDispatcher {
    /**
     * 基础信息
     */
    transform: Transform;
    /**
     * 是否可见
     */
    visible: boolean;
    /**
     * 是否可绘制
     * 自身不绘制，但是参与updateTransform
     * 在有些时候的遮罩处理
     */
    renderable: boolean;
    /**
     * 父级
     * @member {Container}
     * @readonly
     */
    parent: Container;

    /**
     * 舞台
     */
    stage: Stage

    /**
     * 名字,预留
     */
    name: string

    /**
     * 全局透明度
     */
    _worldAlpha: number;
    /**
     * 用于只管局部自己,不包括子级，用于各继承修改自身，hitTest检测自己用
     */
    protected _localBoundsSelf: Rectangle;

    /**
     * 用于计算世界bounds，包括自身和子级，一般只在获取宽高时使用，变形后的
     */
    protected _bounds: Rectangle;
    /**
     * 包围盒有修改用_boundsID++
     */
    protected _boundsID: number;
    protected _lastBoundsID: number;
    /**
     * 遮罩
     * @member {Graphics}
     * @private
     */
    private _mask//: Graphics;

    /**
     * 滤镜区域，暂时别用，用了也没效
     */
    filterArea: Rectangle;
    /**
     * 滤镜数组
     */
    private _filters: Filter[];
    get filters(): Filter[] { return this._filters && this._filters.slice(); }
    set filters(value: Filter[]) { this._filters = value && value.slice(); }
    /**
     * 可用的滤镜数据，_filters筛选过的
     */
    protected _enabledFilters: Filter[];

    /**
     * 透明度
     * 0到1
     */
    private _alpha: number;
    /**
     * 是否已销毁
     * @member {boolean}
     * @readonly
     */
    destroyed: boolean;

    /**
     * 是否可响应鼠标事件
     */
    mouseEnable: boolean = true;

    /**
     * 临时父级,特殊用处
     */
    private tempDisplayObjectParent: DisplayObject;

    /**
     * 特殊用处
     * 缓存的DisplayObject的updateTransform
     */
    displayObjectUpdateTransform;

    /**
     * 特殊用处
     * 缓存的DisplayObject的hitTestPoint
     */
    displayObjectHitTestPoint;


    protected _width: number;
    protected _height: number;

    constructor() {
        super();
        this._instanceType = "DisplayObject";

        this.tempDisplayObjectParent = null;

        this.transform = new Transform();

        this._alpha = 1;

        this.visible = true;

        this.renderable = true;

        this.parent = null;

        this._worldAlpha = 1;

        //滤镜相关，都置null
        this.filterArea = null;
        this._filters = null;
        this._enabledFilters = null;

        this._localBoundsSelf = new Rectangle();
        this._bounds = new Rectangle();
        this._boundsID = 0;
        this._lastBoundsID = -1;

        this._mask = null;

        this.destroyed = false;
    }
    /**
     * @private
     * @member {DisplayObject}
     */
    private get _tempDisplayObjectParent() {
        if (this.tempDisplayObjectParent === null) {
            this.tempDisplayObjectParent = new DisplayObject();
        }
        return this.tempDisplayObjectParent;
    }

    /**
     * 更新矩阵
     */
    updateTransform() {
        this.transform.updateWorldMatrix(this.parent.transform);
        //更新透明度
        this._worldAlpha = this.alpha * this.parent._worldAlpha;
    }

    //为了hitTestPoint，localToGlobal，globalToLocal等方法不复新不重复生成新的点对象而节约内存
    public static _bp: Point = new Point();
    public static _p1: Point = new Point();
    public static _p2: Point = new Point();
    public static _p3: Point = new Point();
    public static _p4: Point = new Point();
    //bounds缓存
    public static temBounds = new Rectangle()
    /**
     * 点击碰撞测试,就是舞台上的一个point是否在显示对象内,在则返回该对象，不在则返回null
     * 对于那些不是继承container，而直接继承displayObject的不用重写，如Bitmap
     * @method hitTestPoint
     * @public
     * @since 1.0.0
     * @param {Point} point 需要碰到的坐标点
     * @param {boolean} isMouseEvent 是否是鼠标事件调用此方法,一般都为true
     * @return {DisplayObject}
     */
    public hitTestPoint(point: Point, isMouseEvent: boolean = false): DisplayObject {
        let s = this;
        if (!s.visible) return null;
        if (isMouseEvent && !s.mouseEnable) return null;
        if (!isMouseEvent) {
            //如果不是系统调用则不考虑这个点是从全局来的，只认为这个点就是当前要碰撞测试同级别下的坐标点
            if (s._localBoundsSelf.isPointIn(point)) {
                return s;
            }
        } else {
            if (s._localBoundsSelf.isPointIn(s.globalToLocal(point, DisplayObject._bp))) {
                return s;
            }
        }
        return null;
    }

    /**
     * 递归更新矩阵方法
     */
    private _recursivePostUpdateTransform() {
        if (this.parent) {
            this.parent._recursivePostUpdateTransform();
            this.transform.updateWorldMatrix(this.parent.transform);
        }
        else {
            this.transform.updateWorldMatrix(this._tempDisplayObjectParent.transform);
        }
    }

    /**
     * 获取全局的包围盒，变形后的
     * @param {boolean} skipUpdate - 是否递归至父级的更新，默认false
     * @param {Rectangle} rect - 可选，返回的包围盒数据，不传会用固定临时对象
     * @return {Rectangle} 包围盒
     */
    getBounds(skipUpdate: boolean = false, rect: Rectangle = DisplayObject.temBounds): Rectangle {
        //先把父级的都变换
        if (!skipUpdate) {
            if (!this.parent) {
                this.parent = this._tempDisplayObjectParent as Container;
                this.updateTransform();
                this.parent = null;
            }
            else {
                this._recursivePostUpdateTransform();
                this.updateTransform();
            }
        }

        //里面计算了_bounds
        this.calculateBounds();

        return rect.copy(this._bounds);
    }

    /**
     * 以自身为世界坐标系的本地包围盒
     * @param {Rectangle} rect - 可选，返回的包围盒数据，不传会用固定临时对象
     * @return {Rectangle} 包围盒
     */
    getLocalBounds(rect: Rectangle = DisplayObject.temBounds): Rectangle {
        const transformRef = this.transform;
        const parentRef = this.parent;

        this.parent = null;
        this.transform = this._tempDisplayObjectParent.transform;

        const bounds = this.getBounds(false, rect);

        this.parent = parentRef;
        this.transform = transformRef;
        //是否考虑算一次回去，因为getBounds里parent为空时会赋值临时父级并执行updateTransform修改了所有子级原来的矩阵
        if (this.parent) this.updateTransform();

        return bounds;
    }

    calculateBounds() {
        //重写
        if (this._lastBoundsID == this._boundsID) return
        this._lastBoundsID = this._boundsID
    }
    /**
     * 手动调用calculateBounds或getBounds或getLocalBounds时如果发现包围盒计算有误，可以尝试前置调用needUpdateBounds
     */
    needUpdateBounds() {
        this._lastBoundsID = -1;
    }

    /**
     *将全局坐标转换到本地坐标值
     * @method globalToLocal
     * @since 1.0.0
     * @public
     * @param {Point} point
     * @return {Point}
     */
    public globalToLocal(point: Point, bp: Point = null): Point {
        return this.worldMatrix.transformPointInverse(point.x, point.y, bp || DisplayObject._bp);
    }

    /**
     *将本地坐标转换到全局坐标值
     * @method localToGlobal
     * @public
     * @since 1.0.0
     * @param {Point} point
     * @return {Point}
     */
    public localToGlobal(point: Point, bp: Point = null): Point {
        var wp = this.worldMatrix.transformPoint(point.x, point.y, bp || DisplayObject._bp)
        if (this.stage) {
            //舞台存在时返回舞台上的坐标
            return this.stage.globalToLocal(wp, bp || DisplayObject._bp)
        } else {
            //否则返回画布上的坐标
            return wp
        }
        // return this.worldMatrix.transformPoint(point.x, point.y, bp || DisplayObject._bp);
    }

    /**
     * 调用些方法会冒泡的将事件向显示列表下方传递，主要用于移除舞台，和添加进舞台事件，修改stage
     * @method _onDispatchBubbledEvent
     * @public
     * @since 1.0.0
     * @param {string} type
     * @return {void}
     */
    public _onDispatchBubbledEvent(type: string): void {
        let s: any = this;
        if (type == Event.REMOVED_FROM_STAGE && !s.stage) return;
        if (type == Event.REMOVED_FROM_STAGE) {
            s.dispatchEvent(type);
            //以防REMOVED_FROM_STAGE事件里用到了stage
            s.stage = null;
        } else if (type == Event.ADDED_TO_STAGE) {
            s.stage = s.parent.stage;
            s.dispatchEvent(type);
        }
    }

    /**
     * webgl渲染
     * @param {WebGLRenderer} renderer - 渲染器
     */
    renderWebGL(renderer: any) {
        //子类重写
    }

    /**
     * canvas渲染
     * @param {CanvasRenderer} renderer - 渲染器
     */
    renderCanvas(renderer: any) {
        //子类重写
    }

    /**
     * 根据常规属性，设置矩阵属性，挺少用，自己赋值也完全一样，可用于初始化所有数据，考虑废弃
     * @param {number} [x=0] - 位置x
     * @param {number} [y=0] - 位置y
     * @param {number} [scaleX=1] - 缩放x
     * @param {number} [scaleY=1] - 缩放y
     * @param {number} [rotation=0] - 旋转(角度制)
     * @param {number} [skewX=0] - 斜切x（弧度制）
     * @param {number} [skewY=0] - 斜切y（弧度制）
     * @param {number} [anchorX=0] - 锚点x
     * @param {number} [anchorY=0] - 锚点y
     * @return {DisplayObject} 返回自身
     */
    setTransform(
        x: number = 0,
        y: number = 0,
        scaleX: number = 1,
        scaleY: number = 1,
        rotation: number = 0,
        skewX: number = 0,
        skewY: number = 0,
        anchorX: number = 0,
        anchorY: number = 0
    ): DisplayObject {
        this.position.x = x;
        this.position.y = y;
        this.scale.x = !scaleX ? 1 : scaleX;
        this.scale.y = !scaleY ? 1 : scaleY;
        this.rotation = rotation;
        this.skew.x = skewX;
        this.skew.y = skewY;
        this.anchor.x = anchorX;
        this.anchor.y = anchorY;
        return this;
    }

    /**
     * 基本销毁方法
     */
    destroy() {
        //移除所有监听
        //如果有父级，从中移除自己
        this.removeAllEventListener();
        // super.destroy();//不适用继承的，里面的eventTypes置空很麻烦
        if (this.parent) {
            this.parent.removeChild(this);
        }
        //矩阵销毁
        this.transform.destroy();
        //对应属性都置null
        this.transform = null;
        this._localBoundsSelf = null;
        this._bounds = null;
        this.tempDisplayObjectParent = null;
        this._mask = null;

        this.filterArea = null;
        this._filters = null;
        this._enabledFilters = null;

        this.mouseEnable = false;
        //标记为已销毁
        this.destroyed = true;


    }
    /**
     * 获取透明度，0到1
     */
    get alpha(): number {
        return this._alpha
    }
    /**
     * 设置透明度，0到1
     */
    set alpha(value: number) {
        if (this._alpha != value) {
            this._alpha = value
            //是否有必要设置_worldAlpha是否需要更新，一个乘法基本不耗性能
        }
    }
    /**
     * 获取位置x
     */
    get x(): number {
        return this.position.x;
    }
    /**
     * 设置位置x
     */
    set x(value: number) {
        this.transform.position.x = value;
    }
    /**
     * 获取位置y
     */
    get y(): number {
        return this.position.y;
    }
    /**
     * 设置位置y
     */
    set y(value: number) {
        this.transform.position.y = value;
    }

    /**
     * 获取世界矩阵
     * 手动修改时，this.transform._worldID++,保证子级的worldMatrix会修改，尽量别那么做
     * @member {Matrix}
     * @readonly
     */
    get worldMatrix(): Matrix {
        return this.transform.worldMatrix;
    }

    /**
     * 获取本地矩阵
     * 手动修改时this.transform._parentID=-1;保证其worldMatrix会更新
     * @member {Matrix}
     * @readonly
     */
    get localMatrix(): Matrix {
        return this.transform.localMatrix;
    }

    /**
     * 获取位置对象
     * @member {Point|ObservablePoint}
     */
    get position(): ObservablePoint {
        return this.transform.position;
    }

    /**
     * 设置位置对象
     * 传值用于copy，并非赋值，要求传值存在x和y字段
     */
    set position(value) {
        this.transform.position.copy(value);
    }

    /**
     * 获取缩放对象
     * @member {Point|ObservablePoint}
     */
    get scale(): ObservablePoint {
        return this.transform.scale;
    }

    /**
     * 设置缩放对象
     * 传值用于copy，并非赋值，要求传值存在x和y字段
     */
    set scale(value) {
        this.transform.scale.copy(value);
    }
    /**
     * 获取缩放x
     */
    get scaleX(): number {
        return this.transform.scale.x;
    }
    /**
     * 设置缩放x
     */
    set scaleX(value: number) {
        this.transform.scale.x = value;
    }
    /**
     * 获取缩放y
     */
    get scaleY(): number {
        return this.transform.scale.y;
    }
    /**
     * 设置缩放y
     */
    set scaleY(value: number) {
        this.transform.scale.y = value;
    }

    /**
     * 获取锚点对象
     * @member {Point|ObservablePoint}
     */
    get anchor(): ObservablePoint {
        return this.transform.anchor;
    }
    /**
     * 设置锚点对象
     * 传值用于copy，并非赋值，要求传值存在x和y字段
     */
    set anchor(value) {
        this.transform.anchor.copy(value);
    }
    /**
     * 获取锚点x
     */
    get anchorX(): number {
        return this.transform.anchor.x;
    }
    /**
     * 设置锚点x
     */
    set anchorX(value: number) {
        this.transform.anchor.x = value;
    }
    /**
     * 获取锚点y
     */
    get anchorY(): number {
        return this.transform.anchor.y;
    }
    /**
     * 设置锚点y
     */
    set anchorY(value: number) {
        this.transform.anchor.y = value;
    }

    /**
     * 获取斜切对象
     * @member {ObservablePoint}
     */
    get skew(): ObservablePoint {
        return this.transform.skew;
    }
    /**
     * 设置斜切对象
     * 传值用于copy，并非赋值，要求传值存在x和y字段
     */
    set skew(value) {
        this.transform.skew.copy(value);
    }

    /**
     * 获取斜切x，角度制
     */
    get skewX(): number {
        return this.transform.skew.x * RAD_TO_DEG;
    }
    /**
     * 设置斜切x，角度制
     */
    set skewX(value: number) {
        this.transform.skew.x = value * DEG_TO_RAD;
    }
    /**
     * 获取斜切y，角度制
     */
    get skewY(): number {
        return this.transform.skew.y * RAD_TO_DEG;
    }
    /**
     * 设置斜切y，角度制
     */
    set skewY(value: number) {
        this.transform.skew.y = value * DEG_TO_RAD;
    }
    /**
     * 获取旋转值,顺时针,角度制
     * @member {number}
     */
    get rotation(): number {
        return this.transform.rotation * RAD_TO_DEG;
    }

    /**
     * 设置旋转，角度制
     */
    set rotation(value: number) {
        this.transform.rotation = value * DEG_TO_RAD;
    }

    /**
     * 自身是否可见,检测所有父级的visible
     * @member {boolean}
     * @readonly
     */
    get worldVisible(): boolean {
        let item = this;
        do {
            if (!item.visible) {
                return false;
            }
            //@ts-ignore
            item = item.parent;
        } while (item);

        return true;
    }

    /**
     * 获取遮罩
     * @member {Graphics}
     */
    get mask()/*: Graphics*/ {
        return this._mask;
    }

    set mask(value) {
        //同一个return
        if (this._mask == value) return;
        if (this._mask) {
            //原先有的遮罩,重置属性
            this._mask.renderable = true;
            this._mask._isUsedToMask = false;
            // if (this._mask.parent) {
            //     this._mask.parent.removeChild(this._mask)
            //     //是否销毁
            // }
        }

        this._mask = value;

        if (this._mask) {
            this._mask.renderable = false;
            this._mask._isUsedToMask = true;

            //保证更新包围盒
            this._boundsID++;
            //手动添加吧
            // this.parent.addChild(this._mask)
        }
    }

    /**
     * 子类必重写，
     */
    get width() {
        return this._width
    }
    /**
     * 子类必重写，如果设置过宽高_width有值且不为0，子类在更新texture时需设置scale
     */
    set width(value) {
        this._width = value;
    }
    get height() {
        return this._height;
    }
    set height(value) {
        this._height = value;
    }


    /**
     * 更新方法，帧循环的监听事件是放在这派发的
     */
    public update() {
        //监听的
        if (this.hasEventListener(Event.ENTER_FRAME)) {
            this.dispatchEvent(Event.ENTER_FRAME);
        }
    }
}
/**
 * 比用call性能好
 * 不会被子类覆盖，部分地方使用
 */
DisplayObject.prototype.displayObjectUpdateTransform = DisplayObject.prototype.updateTransform;

DisplayObject.prototype.displayObjectHitTestPoint = DisplayObject.prototype.hitTestPoint;
