import Filter from "../Filter";
import { Matrix, Point } from "../../math";
import { defaultFilterMatrix } from "../defaultVerts";
import Sprite from "../../display/Sprite";

const displacementFrag =
    'precision mediump float;' +
    'varying vec2 vFilterCoord;' +
    'varying vec2 vTextureCoord;' +

    'uniform vec2 scale;' +

    'uniform sampler2D uSampler;' +
    'uniform sampler2D mapSampler;' +

    'uniform vec4 filterArea;' +
    'uniform vec4 filterClamp;' +

    'void main(void){' +
    'vec4 map = texture2D(mapSampler, vFilterCoord);' +

    'map -= 0.5;' +
    'map.xy *= scale / filterArea.xy;' +

    'gl_FragColor = texture2D(uSampler, clamp(vec2(vTextureCoord.x + map.x, vTextureCoord.y + map.y), filterClamp.xy, filterClamp.zw));' +
    '}'

/**
 * The DisplacementFilter class uses the pixel values from the specified texture
 * (called the displacement map) to perform a displacement of an object. You can
 * use this filter to apply all manor of crazy warping effects. Currently the r
 * property of the texture is used to offset the x and the g property of the texture
 * is used to offset the y.
 *
 * @class
 * @extends Filter
 * @memberof filters
 */
export default class DisplacementFilter extends Filter {
    maskSprite: Sprite;
    maskMatrix: Matrix;
    scale: Point;
    /**
     * @param {Sprite} sprite - The sprite used for the displacement map. (make sure its added to the scene!)
     * @param {number} scale - The scale of the displacement
     */
    constructor(sprite: Sprite, scale?: number) {
        const maskMatrix = new Matrix();

        sprite.renderable = false;

        super(defaultFilterMatrix, displacementFrag);

        this.maskSprite = sprite;
        this.maskMatrix = maskMatrix;

        this.uniforms.mapSampler = sprite._texture;
        this.uniforms.filterMatrix = maskMatrix;
        this.uniforms.scale = { x: 1, y: 1 };

        if (scale === null || scale === undefined) scale = 20;

        this.scale = new Point(scale, scale);
    }

    /**
     * Applies the filter.
     *
     * @param {FilterManager} filterManager - The manager.
     * @param {RenderTarget} input - The input target.
     * @param {RenderTarget} output - The output target.
     */
    apply(filterManager, input, output) {
        this.uniforms.filterMatrix = filterManager.calculateSpriteMatrix(this.maskMatrix, this.maskSprite);
        this.uniforms.scale.x = this.scale.x;
        this.uniforms.scale.y = this.scale.y;

        // draw the filter...
        filterManager.applyFilter(this, input, output);
    }

    /**
     * The texture used for the displacement map. Must be power of 2 sized texture.
     *
     * @member {Texture}
     */
    get map() {
        return this.uniforms.mapSampler;
    }

    set map(value) {
        this.uniforms.mapSampler = value;
    }
}
