import { Rectangle, Matrix } from '../../math';
import { SCALE_MODES } from '../../const';
// import settings from '../../../settings';
import { GLFramebuffer, GLTexture, GLBuffer } from '../../../glCore';
import Graphics from '../../graphics/Graphics';


export default class RenderTarget {
    /**
     * The current WebGL drawing context.
     *
     */
    gl: WebGLRenderingContext;
    /**
     * A frame buffer
     */
    frameBuffer: GLFramebuffer;
    /**
     * The texture
     */
    texture: GLTexture;
    /**
     * The background colour of this render target, as an array of [r,g,b,a] values
     *
     */
    clearColor: number[];
    /**
     * The size of the object as a rectangle
     */
    size: Rectangle;
    /**
     * The projection matrix
     */
    projectionMatrix: Matrix;
    /**
     * The object's transform
     */
    transform: Matrix;
    /**
     * The frame.
     *
     */
    frame: Rectangle;

    defaultFrame: Rectangle;
    destinationFrame: any;
    sourceFrame: any;
    /**
     * The stencil buffer stores masking data for the render target
     *
     */
    stencilBuffer: GLBuffer;
    /**
     * The data structure for the stencil masks
     */
    stencilMaskStack: Graphics[];
    /**
     * The scale mode.
     *
     */
    scaleMode: number;
    /**
     * Whether this object is the root element or not
     */
    root: boolean;

    /**
     * Stores filter data for the render target
     *
     * @member {object[]}
     */
    filterData: any;
    /**
     * The key for pooled texture of FilterSystem
     * @private
     * @member {string}
     */
    filterPoolKey: string;
    /**
     * @param {WebGLRenderingContext} gl - The current WebGL drawing context
     * @param {number} [width=0] - the horizontal range of the filter
     * @param {number} [height=0] - the vertical range of the filter
     * @param {number} [scaleMode=SCALE_MODES.LINEAR] - See {@link SCALE_MODES} for possible values
     * @param {boolean} [root=false] - Whether this object is the root element or not
     */
    constructor(
        gl: WebGLRenderingContext,
        width: number = 0,
        height: number = 0,
        scaleMode: number = SCALE_MODES.LINEAR,
        root: boolean = false
    ) {
        this.gl = gl;

        // next time to create a frame buffer and texture
        this.frameBuffer = null;

        this.texture = null;

        this.clearColor = [0, 0, 0, 0];

        this.size = new Rectangle(0, 0, 1, 1);

        this.projectionMatrix = new Matrix();

        this.transform = null;

        this.frame = null;

        this.defaultFrame = new Rectangle();
        this.destinationFrame = null;
        this.sourceFrame = null;

        this.stencilBuffer = null;

        this.stencilMaskStack = [];

        this.filterData = null;

        this.filterPoolKey = '';


        this.scaleMode = scaleMode;

        this.root = root;

        if (!this.root) {
            this.frameBuffer = GLFramebuffer.createRGBA(gl, 100, 100);

            if (this.scaleMode === SCALE_MODES.NEAREST) {
                this.frameBuffer.texture.enableNearestScaling();
            }
            else {
                this.frameBuffer.texture.enableLinearScaling();
            }
            /*
                A frame buffer needs a target to render to..
                create a texture and bind it attach it to the framebuffer..
             */

            // this is used by the base texture
            this.texture = this.frameBuffer.texture;
        }
        else {
            // make it a null framebuffer..
            this.frameBuffer = new GLFramebuffer(gl, 100, 100);
            //帧缓存为空，gltexture也是空
            this.frameBuffer.framebuffer = null;
        }
        this.setFrame();
        this.resize(width, height);
    }

    /**
     * Clears the filter texture.
     *
     * @param {number[]} [clearColor=this.clearColor] - Array of [r,g,b,a] to clear the framebuffer
     */
    clear(clearColor?: number[]) {
        const cc = clearColor || this.clearColor;
        //帧缓存清空，this.frameBuffer.framebuffer为null时，直接清root
        this.frameBuffer.clear(cc[0], cc[1], cc[2], cc[3]);// r,g,b,a);
    }

    /**
     * Binds the stencil buffer.
     *
     */
    attachStencilBuffer() {
        // TODO check if stencil is done?
        /**
         * The stencil buffer is used for masking in
         * lets create one and then add attach it to the framebuffer..
         */
        if (!this.root) {
            this.frameBuffer.enableStencil();
        }
        //为什么不需要把源数据赋值到this.stencilMaskStack
    }

    /**
     * Sets the frame of the render target.
     *
     * @param {Rectangle} destinationFrame - The destination frame.
     * @param {Rectangle} sourceFrame - The source frame.
     */
    setFrame(destinationFrame?: Rectangle, sourceFrame?: Rectangle) {
        this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame;
        this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame;
    }

    /**
     * Binds the buffers and initialises the viewport.
     *
     */
    activate() {
        // TOOD refactor usage of frame..
        const gl = this.gl;

        // make sure the texture is unbound!
        this.frameBuffer.bind();

        this.calculateProjection(this.destinationFrame, this.sourceFrame);

        if (this.transform) {
            this.projectionMatrix.append(this.transform);
        }

        // TODO add a check as them may be the same!
        if (this.destinationFrame !== this.sourceFrame) {
            gl.enable(gl.SCISSOR_TEST);
            gl.scissor(
                this.destinationFrame.x | 0,
                this.destinationFrame.y | 0,
                this.destinationFrame.width | 0,
                this.destinationFrame.height | 0
            );
        }
        else {
            gl.disable(gl.SCISSOR_TEST);
        }

        // TODO - does not need to be updated all the time??
        gl.viewport(
            this.destinationFrame.x | 0,
            this.destinationFrame.y | 0,
            this.destinationFrame.width | 0,
            this.destinationFrame.height | 0
        );
    }

    /**
     * Updates the projection matrix based on a projection frame (which is a rectangle)
     *
     * @param {Rectangle} destinationFrame - The destination frame.
     * @param {Rectangle} sourceFrame - The source frame.
     */
    calculateProjection(destinationFrame: Rectangle, sourceFrame?: Rectangle) {
        const pm = this.projectionMatrix;
        // console.log(destinationFrame)
        sourceFrame = sourceFrame || destinationFrame;

        pm.identity();

        // TODO: make dest scale source
        if (!this.root) {
            pm.a = 1 / destinationFrame.width * 2;
            pm.d = 1 / destinationFrame.height * 2;

            pm.tx = -1 - (sourceFrame.x * pm.a);
            pm.ty = -1 - (sourceFrame.y * pm.d);
        }
        else {
            pm.a = 1 / destinationFrame.width * 2;
            pm.d = -1 / destinationFrame.height * 2;

            pm.tx = -1 - (sourceFrame.x * pm.a);
            pm.ty = 1 - (sourceFrame.y * pm.d);
        }

    }

    /**
     * Resizes the texture to the specified width and height
     *
     * @param {number} width - the new width of the texture
     * @param {number} height - the new height of the texture
     */
    resize(width: number, height: number) {
        width = width | 0;
        height = height | 0;

        if (this.size.width === width && this.size.height === height) {
            return;
        }

        this.size.width = width;
        this.size.height = height;

        this.defaultFrame.width = width;
        this.defaultFrame.height = height;

        this.frameBuffer.resize(width, height);

        const projectionFrame = this.frame || this.size;

        this.calculateProjection(projectionFrame);
    }

    /**
     * 销毁方法
     */
    destroy() {
        this.frameBuffer.destroy();
        this.frameBuffer = null;
        this.texture = null;
    }
}
