import extractUniformsFromSrc from './extractUniformsFromSrc';
import { BLEND_MODES } from '../const';
import { uid } from '../utils';
import RenderTarget from '../renderers/renderTarget/RenderTarget';


const SOURCE_KEY_MAP = {};

/**
 * 基本就是一个着色器，暂时不考虑集成GLShader，还不晓得咋搞
 */
export default class Filter {
    //顶点着色器
    vertexSrc: string;
    //片元着色器
    fragmentSrc: string;

    // state，新的用stat，以后处理
    private _blendMode: BLEND_MODES;
    get blendMode(): BLEND_MODES {
        return this._blendMode;
    }
    /**
     * 别用先
     */
    set blendMode(value: BLEND_MODES) {
        this._blendMode = value;
    }

    uniformData: any;
    /**
     * An object containing the current values of custom uniforms.
     * @example <caption>Updating the value of a custom uniform</caption>
     * filter.uniforms.time = performance.now();
     *
     * @member {object}
     */
    uniforms: any;

    glShaders: any;

    glShaderKey: string;
    /**
     * The padding of the filter. Some filters require extra space to breath such as a blur.
     * Increasing this will add extra width and height to the bounds of the object that the
     * filter is applied to.
     *
     * @member {number}
     */
    padding: number;
    /**
     * The resolution of the filter. Setting this to be lower will lower the quality but
     * increase the performance of the filter.
     *
     * @member {number}
     */
    resolution: number;
    /**
     * If enabled is true the filter is applied, if false it will not.
     *
     * @member {boolean}
     */
    enabled: boolean;
    /**
     * If enabled, PixiJS will fit the filter area into boundaries for better performance.
     * Switch it off if it does not work for specific shader.
     *
     * @member {boolean}
     */
    autoFit: boolean;
    /**
     * @param {string} [vertexSrc] - The source of the vertex shader.
     * @param {string} [fragmentSrc] - The source of the fragment shader.
     * @param {object} [uniforms] - Custom uniforms to use to augment the built-in ones.
     */
    constructor(vertexSrc?: string, fragmentSrc?: string, uniforms?: any) {

        this.vertexSrc = vertexSrc || Filter.defaultVertexSrc;

        this.fragmentSrc = fragmentSrc || Filter.defaultFragmentSrc;

        this._blendMode = BLEND_MODES.NORMAL;

        this.uniformData = uniforms || extractUniformsFromSrc(this.vertexSrc, this.fragmentSrc);

        this.uniforms = {};

        for (const i in this.uniformData) {
            this.uniforms[i] = this.uniformData[i].value;
            if (this.uniformData[i].type) {
                this.uniformData[i].type = this.uniformData[i].type.toLowerCase();
            }
        }

        // this is where we store shader references..
        // TODO we could cache this!
        this.glShaders = {};

        // used for cacheing.. sure there is a better way!
        if (!SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) {
            SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid();
        }

        this.glShaderKey = SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc];

        this.padding = 4;

        this.resolution = 1;

        this.enabled = true;

        this.autoFit = true;
    }

    /**
     * Applies the filter
     *
     * @param {FilterManager} filterManager - The renderer to retrieve the filter from
     * @param {RenderTarget} input - The input render target.
     * @param {RenderTarget} output - The target to output to.
     * @param {boolean} clear - Should the output be cleared before rendering to it
     * @param {object} [currentState] - It's current state of filter.
     *        There are some useful properties in the currentState :
     *        target, filters, sourceFrame, destinationFrame, renderTarget, resolution
     */
    apply(filterManager, input: RenderTarget, output: RenderTarget, clear: boolean) {
        filterManager.applyFilter(this, input, output, clear);
    }



    /**
     * The default vertex shader source
     *
     * @static
     * @constant
     */
    private static get defaultVertexSrc() {
        return [
            'attribute vec2 aVertexPosition;',
            'attribute vec2 aTextureCoord;',

            'uniform mat3 projectionMatrix;',
            'uniform mat3 filterMatrix;',

            'varying vec2 vTextureCoord;',
            'varying vec2 vFilterCoord;',

            'void main(void){',
            '   gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);',
            '   vFilterCoord = ( filterMatrix * vec3( aTextureCoord, 1.0)  ).xy;',
            '   vTextureCoord = aTextureCoord ;',
            '}',
        ].join('\n');
    }

    /**
     * The default fragment shader source
     *
     * @static
     * @constant
     */
    private static get defaultFragmentSrc() {
        return [
            'varying vec2 vTextureCoord;',
            'varying vec2 vFilterCoord;',

            'uniform sampler2D uSampler;',
            'uniform sampler2D filterSampler;',

            'void main(void){',
            '   vec4 masky = texture2D(filterSampler, vFilterCoord);',
            '   vec4 sample = texture2D(uSampler, vTextureCoord);',
            '   vec4 color;',
            '   if(mod(vFilterCoord.x, 1.0) > 0.5)',
            '   {',
            '     color = vec4(1.0, 0.0, 0.0, 1.0);',
            '   }',
            '   else',
            '   {',
            '     color = vec4(0.0, 1.0, 0.0, 1.0);',
            '   }',
            // '   gl_FragColor = vec4(mod(vFilterCoord.x, 1.5), vFilterCoord.y,0.0,1.0);',
            '   gl_FragColor = mix(sample, masky, 0.5);',
            '   gl_FragColor *= sample.a;',
            '}',
        ].join('\n');
    }
}
