import { Point, Rectangle } from "../math";
import { Polygon } from "../graphics/shapes";
import Container from "../display/Container";
import Texture from "../texture/Texture";
import { BLEND_MODES } from "../const";
import TextureMatrix from "../texture/TextureMatrix";
import {CanvasRenderer} from "../renderers/CanvasRenderer";
import { WebglRenderer } from "../renderers/WebglRenderer";
import { DisplayObject } from "../display";


const tempPoint = new Point();
const tempPolygon = new Polygon();

/**
 * Mesh网格类，其实最优的是仿pixiV5和threejs用geometry和material
 * 简便起见，顶点，索引，uv都干一起吧
 * 用batch渲染
 */
export class Mesh extends Container {
    /**
     * 混色模式
     * @default BLEND_MODES.NORMAL
     */
    private _blendMode: BLEND_MODES = BLEND_MODES.NORMAL;
    //以后修改
    get blendMode(): BLEND_MODES {
        return this._blendMode
    }
    /**
     * 很多效果暂时无效，再查原因，先不能设置吧
     */
    set blendMode(value: BLEND_MODES) {
        // if (value != this._blendMode) this._blendMode = value
    }
    /**
     * The texture of the Mesh
     *
     * @member {Texture}
     * @default Texture.EMPTY
     * @private
     */
    private _texture: Texture;

    /**
     * 原先自身的顶点数据,到时候封装几何全进
     */
    _vertices: Float32Array;
    /**
     * 是否要更新执行_refresh
     */
    protected _needRefresh: boolean;

    _indices: Uint16Array;
    _uvs: Float32Array;
    /**
     * 要传入批的顶点数据
     */
    _vertexData: Float32Array;
    /**
     * 传入批处理的永远是上面三个，上面三个的计算需要记录dirty
     * 但是每次都需要传Gpu,性能再说吧
     * Version of mesh verticies are dirty or not
     * 原来是dirty，
     * @member {number}
     */
    protected _vertexDirty: number;

    /**
     * 记录顶点原标记Id，确保与_vertexDirty保持一致
     * 默认-1，保证算一次
     */
    private _vertexId: number;
    /**
     * 记录LocalBoundsSelf的id，确保与_vertexDirty保持一致
     * 默认-1，保证算一次
     */
    private _localBoundsSelfId: number;
    /**
     * 需要与transform._worldID的保持一致
     * 用于标记计算顶点数据
     */
    private _transformID: number
    /**
     * Triangles in canvas mode are automatically antialiased, use this value to force triangles
     * to overlap a bit with each other.
     *
     * @member {number}
     */
    canvasPadding: number;
    /**
     * 色值调色
     * The tint applied to the mesh. This is a [r,g,b] value. A value of [1,1,1] will remove any
     * tint effect.
     */
    private _tint: number;
    /**
     * RGB形式色值，webgl用
     */
    private _tintRGB: number;
    /**
     * transform that is applied to UV to get the texture coords
     * its updated independently from texture uvTransform
     * updates of uvs are tied to that thing
     *
     * @member {TextureMatrix}
     * @private
     */
    private _uvTransform: TextureMatrix;
    /**
     * @param {Texture} texture - The texture to use
     * @param {Float32Array} [vertices] - if you want to specify the vertices
     * @param {Float32Array} [uvs] - if you want to specify the uvs
     * @param {Uint16Array} [indices] - if you want to specify the indices
     */
    constructor(texture?: Texture, vertices?: Float32Array, uvs?: Float32Array, indices?: Uint16Array) {
        super();

        /**
         * 赋值有问题，再处理
         */
        this._texture = texture || Texture.EMPTY;

        this._uvs = uvs || new Float32Array([
            0, 0,
            1, 0,
            1, 1,
            0, 1]);

        this._vertices = vertices || new Float32Array([
            0, 0,
            100, 0,
            100, 100,
            0, 100]);

        this._indices = indices || new Uint16Array([0, 1, 3, 2]);


        this._vertexDirty = 0;

        //置为-1，确保计算
        this._vertexId = -1;
        this._localBoundsSelfId = -1;

        this.blendMode = BLEND_MODES.NORMAL;

        this.canvasPadding = 0;

        this._tint = null;
        this._tintRGB = null;
        this.tint = 0xFFFFFF;

        this._uvTransform = new TextureMatrix(this._texture);

        //标记，至少更新一次
        this._needRefresh = true;
    }

    /**
     * Renders the object using the WebGL renderer
     *
     * @private
     * @param {WebGLRenderer} renderer - a reference to the WebGL renderer
     */
    _renderWebGL(renderer: WebglRenderer) {
        //刷一次
        this.refresh();
        //计算全局顶点
        this.calculateVertices();
        renderer.batchManager.setObjectRenderer(renderer.plugins["batch"]);
        renderer.plugins["batch"].render(this);
    }

    /**
     * Renders the object using the Canvas renderer
     *
     * @private
     * @param {CanvasRenderer} renderer - The canvas renderer.
     */
    _renderCanvas(renderer: CanvasRenderer) {
        this.refresh();
        renderer.plugins["mesh"].render(this);
    }

    /**
     * When the texture is updated, this event will fire to update the scale and frame
     *
     * @private
     */
    private _onTextureUpdate() {
        this._uvTransform.texture = this._texture;
        this._needRefresh = true;
    }

    /**
     * 重新计算uv，为了图集上的uv和自身uv
     */
    multiplyUvs() {
        this._uvTransform.multiplyUvs(this._uvs);
    }

    /**
     * Refreshes uvs for generated meshes (rope, plane)
     * sometimes refreshes vertices too
     *
     * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case
     */
    refresh(forceUpdate: boolean = false) {
        //贴图不可用，返回
        if (!this._texture || !this._texture.valid) return;
        if (this._uvTransform.update(forceUpdate) ||
            this._needRefresh ||
            forceUpdate
        ) {
            this._refresh();
        }
    }

    /**
     * 子类重写，用来计算 顶点，uv，索引
     * @protected
     */
    protected _refresh() {
        /* empty */
    }

    /**
     * 计算一边全局的顶点
     */
    calculateVertices() {
        //顶点没变，坐标没变，就返回
        if (this._vertexId === this._vertexDirty && this._transformID === this.transform._worldID) return;
        this._vertexId = this._vertexDirty
        this._transformID = this.transform._worldID;

        if (this._vertices.length <= 2) return
        if (!this._vertexData) this._vertexData = new Float32Array(this._vertices.length)



        const wt = this.transform.worldMatrix;
        const a = wt.a;
        const b = wt.b;
        const c = wt.c;
        const d = wt.d;
        const tx = wt.tx;
        const ty = wt.ty;

        const vertexData = this._vertexData;

        for (let i = 0; i < vertexData.length / 2; i++) {
            const x = this._vertices[(i * 2)];
            const y = this._vertices[(i * 2) + 1];
            vertexData[(i * 2)] = (a * x) + (c * y) + tx;
            vertexData[(i * 2) + 1] = (b * x) + (d * y) + ty;
        }
    }


    /**
     * Returns the bounds of the mesh as a rectangle. The bounds calculation takes the worldTransform into account.
     *
     */
    _calculateBounds() {
        // TODO - we can cache local bounds and use them if they are dirty (like graphics)
        // this._bounds.addVertices(this.transform, this.vertices, 0, this.vertices.length);
        this.updateLocalBoundsSelf();
        const rect = this._localBoundsSelf;
        var matrix = this.transform.worldMatrix;
        matrix.transformPoint(rect.x, rect.y, DisplayObject._p1);
        matrix.transformPoint(rect.x + rect.width, rect.y, DisplayObject._p2);
        matrix.transformPoint(rect.x + rect.width, rect.y + rect.height, DisplayObject._p3);
        matrix.transformPoint(rect.x, rect.y + rect.height, DisplayObject._p4);
        Rectangle.createFromPoints(this._bounds, DisplayObject._p1, DisplayObject._p2, DisplayObject._p3, DisplayObject._p4);
    }

    /**
     * 更新自身包围盒
     * 通过原先的顶点数据玩，不经过transform
     */
    updateLocalBoundsSelf() {
        if (this._localBoundsSelfId == this._vertexDirty) return;
        this._localBoundsSelfId = this._vertexDirty;
        //如果小于等于1个点清空
        if (this._vertices.length <= 2) this._localBoundsSelf.clear();
        Rectangle.createFromVertexData(this._localBoundsSelf, this._vertices);
    }
    /**
     * 
     * @param point 全局点
     * @param isMouseEvent 
     */
    hitTestPoint(point: Point, isMouseEvent: boolean = false): DisplayObject {
        //不可见，直接返回
        if (!this.visible) return null;
        let hitDisplayObject;
        //先检查子级，因为子级层级更高
        hitDisplayObject = super.hitTestPoint(point, isMouseEvent);
        //子级已有，返回
        if (hitDisplayObject) return hitDisplayObject;
        //子级没有的话，为了可以不updateLocalBoundsSelf，判断一下
        if (isMouseEvent && !this.mouseEnable) return null;
        //再粗略检查自己，先计算自身盒子
        this.updateLocalBoundsSelf();
        //简单检测
        hitDisplayObject = this.displayObjectHitTestPoint(point, isMouseEvent);
        //如果在LocalBoundsSelf内，再继续查点组成的
        if (hitDisplayObject) return this.hitTestPointAccuratly(point, isMouseEvent)
        return null
    }
    private hitTestPointAccuratly(point: Point, isMouseEvent): DisplayObject {
        if (isMouseEvent) {
            this.globalToLocal(point, tempPoint);
        } else {
            tempPoint.set(point.x, point.y);
        }
        const vertices = this._vertices;
        const points = tempPolygon.points;
        const indices = this._indices;
        const len = this._indices.length;
        for (let i = 0; i + 2 < len; i += 3) {
            const ind0 = indices[i] * 2;
            const ind1 = indices[i + 1] * 2;
            const ind2 = indices[i + 2] * 2;

            points[0] = vertices[ind0];
            points[1] = vertices[ind0 + 1];
            points[2] = vertices[ind1];
            points[3] = vertices[ind1 + 1];
            points[4] = vertices[ind2];
            points[5] = vertices[ind2 + 1];

            if (tempPolygon.isPointIn(tempPoint)) return this;

        }
        return null
    }

    /**
     * The texture that the mesh uses.
     *
     * @member {Texture}
     */
    get texture() {
        return this._texture;
    }

    set texture(value) {
        if (this._texture === value) return;
        this._texture = value;
        if (value) {
            // wait for the texture to load
            if (value.baseTexture.hasLoaded) {
                this._onTextureUpdate();
            }
            else {
                value.once('update', this._onTextureUpdate, this);
            }
        }
    }

    get tint() {
        return this._tint;
    }
    set tint(value: number) {
        if (value === this._tint) return;
        this._tint = value;
        this._tintRGB = (value >> 16) + (value & 0xff00) + ((value & 0xff) << 16);
    }

    /**
     * Destroys the Mesh object.
     *
     */
    destroy() {
        super.destroy();
        //相应texture移除监听
        this._texture.removeEventListener('update', this._onTextureUpdate, this);

        this._uvs = null;
        this._vertices = null;
        this._indices = null;
        this._vertexData = null
    }
}

