
(function(){
'use strict';

var VERT = 'attribute vec2 a_pos;varying vec2 v_uv;void main(){v_uv=a_pos*0.5+0.5;gl_Position=vec4(a_pos,0.,1.);}';

function wrapGLSL(transitionCode) {
    return [
        'precision highp float;',
        'uniform sampler2D u_from;',
        'uniform sampler2D u_to;',
        'uniform float progress;',
        'uniform float ratio;',
        'varying vec2 v_uv;',
        '',
        'vec4 getFromColor(vec2 uv){return texture2D(u_from,uv);}',
        'vec4 getToColor(vec2 uv){return texture2D(u_to,uv);}',
        '',
        transitionCode,
        '',
        'void main(){gl_FragColor=transition(v_uv);}'
    ].join('\n');
}

var TRANSITIONS = {};

TRANSITIONS.wipe = [
'uniform vec2 direction; // = vec2(1.0,-1.0)',
'uniform float smoothness; // = 0.5',
'const vec2 center=vec2(0.5);',
'vec4 transition(vec2 uv){',
'  vec2 v=normalize(direction);',
'  float d=dot(uv-center,v);',
'  float m=smoothstep(-smoothness,smoothness,d-progress*2.0+1.0);',
'  return mix(getToColor(uv),getFromColor(uv),m);',
'}'
].join('\n');

TRANSITIONS.cross_zoom = [
'uniform float strength; // = 0.4',
'const float PI=3.14159265;',
'float zp(float z,float p){return mix(z,pow(z,0.5),p);}',
'vec4 transition(vec2 uv){',
'  float p=smoothstep(0.0,1.0,progress);',
'  float iz=1.0+zp(p,1.0)*strength;',
'  float oz=1.0+(1.0-zp(p,1.0))*strength;',
'  vec2 iUV=(uv-0.5)/iz+0.5;',
'  vec2 oUV=(uv-0.5)/oz+0.5;',
'  return mix(getFromColor(oUV),getToColor(iUV),p);',
'}'
].join('\n');

TRANSITIONS.door = [
'uniform float reflection; // = 0.4',
'uniform float perspective; // = 0.4',
'uniform float depth; // = 3.0',
'const vec4 black=vec4(0.0,0.0,0.0,1.0);',
'const vec2 boundMin=vec2(0.0);',
'const vec2 boundMax=vec2(1.0);',
'bool inBounds(vec2 p){return all(lessThan(boundMin,p))&&all(lessThan(p,boundMax));}',
'vec4 transition(vec2 uv){',
'  float middleSplit=2.0*abs(uv.x-0.5)-progress;',
'  if(middleSplit>0.0){',
'    float d=1.0/(1.0+perspective*progress*(1.0-middleSplit));',
'    vec2 pfr=vec2(uv.x+(uv.x>0.5?-0.5:0.5)*progress,mix(0.5,uv.y,d));',
'    return inBounds(pfr)?getFromColor(pfr):black;',
'  }',
'  return getToColor(uv);',
'}'
].join('\n');

TRANSITIONS.cube = [
'uniform float persp; // = 0.7',
'uniform float unzoom; // = 0.3',
'uniform float reflection; // = 0.4',
'uniform float floating; // = 3.0',
'vec2 project(vec2 p){return p*vec2(1.0,-1.2)+vec2(0.0,-floating/100.0);}',
'bool inBounds(vec2 p){return all(lessThan(vec2(0.0),p))&&all(lessThan(p,vec2(1.0)));}',
'vec4 bgColor(vec2 p,vec2 pfr,vec2 pto){',
'  return vec4(0.0,0.0,0.0,1.0);',
'}',
'vec4 transition(vec2 op){',
'  float uz=unzoom*2.0*(0.5-distance(0.5,progress));',
'  vec2 p=(op-0.5)*(1.0-uz)+0.5;',
'  float s=step(progress,p.x);',
'  float hPersp=perspective*progress;',
'  vec2 pfr=vec2(p.x/(1.0-hPersp)-hPersp/(1.0-hPersp),(p.y-0.5*(1.0-1.0/(1.0+hPersp*0.5))*s)/max(0.001,1.0/(1.0+hPersp*0.5))+0.5);',
'  float hPersp2=perspective*(1.0-progress);',
'  vec2 pto=vec2((p.x-1.0+hPersp2)/(1.0-hPersp2),(p.y-0.5*(1.0-1.0/(1.0+hPersp2*0.5))*(1.0-s))/max(0.001,1.0/(1.0+hPersp2*0.5))+0.5);',
'  if(inBounds(pfr)&&progress<0.5)return getFromColor(pfr);',
'  if(inBounds(pto)&&progress>=0.5)return getToColor(pto);',
'  return bgColor(p,pfr,pto);',
'}'
].join('\n');

TRANSITIONS.swirl = [
'vec4 transition(vec2 uv){',
'  float a=(1.0-progress)*5.0;',
'  vec2 center=vec2(0.5);',
'  vec2 tc=uv-center;',
'  float dist=length(tc);',
'  float angle=a*dist*3.14159*2.0;',
'  tc=vec2(dot(tc,vec2(cos(angle),-sin(angle))),dot(tc,vec2(sin(angle),cos(angle))));',
'  tc+=center;',
'  return mix(getFromColor(tc),getToColor(uv),progress);',
'}'
].join('\n');

TRANSITIONS.glitch = [
'vec4 transition(vec2 uv){',
'  float x=progress;',
'  float g=0.05*x;',
'  vec2 red=vec2(g,0.0);',
'  vec2 green=vec2(0.0,g);',
'  vec2 blue=vec2(-g,0.0);',
'  float n=fract(sin(dot(uv+x,vec2(12.9898,78.233)))*43758.5);',
'  float o=smoothstep(0.0,0.5,x)*(1.0-smoothstep(0.5,1.0,x));',
'  float shift=o*n*0.15;',
'  vec2 uv2=vec2(uv.x+shift*(2.0*n-1.0),uv.y);',
'  vec4 a=getFromColor(uv2);',
'  vec4 b=getToColor(uv2);',
'  vec4 c=mix(a,b,x);',
'  c.r=mix(getFromColor(uv2+red).r,getToColor(uv2+red).r,x);',
'  c.b=mix(getFromColor(uv2+blue).b,getToColor(uv2+blue).b,x);',
'  return c;',
'}'
].join('\n');

TRANSITIONS.fade_color = [
'uniform vec3 color; // = vec3(0.0)',
'uniform float colorPhase; // = 0.4',
'vec4 transition(vec2 uv){',
'  return mix(',
'    mix(getFromColor(uv),vec4(color,1.0),smoothstep(0.0,colorPhase,progress)),',
'    mix(vec4(color,1.0),getToColor(uv),smoothstep(colorPhase,1.0,progress)),',
'    progress);',
'}'
].join('\n');

function createShader(gl, type, src) {
    var s = gl.createShader(type);
    gl.shaderSource(s, src);
    gl.compileShader(s);
    if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
        console.error('GLT shader error:', gl.getShaderInfoLog(s));
        gl.deleteShader(s);
        return null;
    }
    return s;
}

function createProgram(gl, vs, fs) {
    var v = createShader(gl, gl.VERTEX_SHADER, vs);
    var f = createShader(gl, gl.FRAGMENT_SHADER, fs);
    if (!v || !f) return null;
    var p = gl.createProgram();
    gl.attachShader(p, v);
    gl.attachShader(p, f);
    gl.linkProgram(p);
    if (!gl.getProgramParameter(p, gl.LINK_STATUS)) {
        console.error('GLT link error:', gl.getProgramInfoLog(p));
        return null;
    }
    return p;
}

function createTexture(gl, image) {
    var tex = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, tex);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
    return tex;
}

function run(oldEl, newEl, name, duration, callback) {
    var transCode = TRANSITIONS[name];
    if (!transCode || typeof html2canvas === 'undefined') {
        
        if (callback) callback();
        return;
    }

    var W = window.innerWidth;
    var H = window.innerHeight;
    var dpr = Math.min(window.devicePixelRatio || 1, 1.5); 

    var origNewVis = newEl.style.visibility;
    var origNewZ = newEl.style.zIndex;
    var origNewOp = newEl.style.opacity;
    newEl.style.visibility = 'visible';
    newEl.style.zIndex = '-1';
    newEl.style.opacity = '1';

    var captureOpts = {
        width: W, height: H, scale: dpr,
        useCORS: true, allowTaint: true,
        backgroundColor: null,
        logging: false
    };

    Promise.all([
        html2canvas(oldEl, captureOpts),
        html2canvas(newEl, captureOpts)
    ]).then(function(canvases) {
        var fromCanvas = canvases[0];
        var toCanvas = canvases[1];

        newEl.style.visibility = origNewVis;
        newEl.style.zIndex = origNewZ;
        newEl.style.opacity = origNewOp;

        var cv = document.createElement('canvas');
        cv.width = Math.floor(W * dpr);
        cv.height = Math.floor(H * dpr);
        cv.style.cssText = 'position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:99999;pointer-events:none;';
        document.body.appendChild(cv);

        var gl = cv.getContext('webgl', {
            alpha: false, premultipliedAlpha: false,
            antialias: false, preserveDrawingBuffer: false
        });
        if (!gl) {
            cv.remove();
            if (callback) callback();
            return;
        }

        var fragSrc = wrapGLSL(transCode);
        var program = createProgram(gl, VERT, fragSrc);
        if (!program) {
            cv.remove();
            if (callback) callback();
            return;
        }

        var buf = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, buf);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1, 1,-1, -1,1, 1,1]), gl.STATIC_DRAW);
        var aPos = gl.getAttribLocation(program, 'a_pos');
        gl.enableVertexAttribArray(aPos);
        gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0);

        var texFrom = createTexture(gl, fromCanvas);
        var texTo = createTexture(gl, toCanvas);

        gl.useProgram(program);

        var uFrom = gl.getUniformLocation(program, 'u_from');
        var uTo = gl.getUniformLocation(program, 'u_to');
        var uProgress = gl.getUniformLocation(program, 'progress');
        var uRatio = gl.getUniformLocation(program, 'ratio');

        gl.uniform1i(uFrom, 0);
        gl.uniform1i(uTo, 1);
        gl.uniform1f(uRatio, W / H);

        var optUniforms = {
            'strength': 0.1,
            'smoothness': 0.5,
            'size': 0.2,
            'amplitude': 30.0,
            'speed': 30.0,
            'persp': 0.7,
            'unzoom': 0.3,
            'reflection': 0.4,
            'floating': 3.0,
            'depth': 3.0,
            'perspective': 0.4,
            'colorSeparation': 0.04,
            'colorPhase': 0.4,
            'horizontalHexagons': 20.0
        };
        for (var uName in optUniforms) {
            var loc = gl.getUniformLocation(program, uName);
            if (loc) gl.uniform1f(loc, optUniforms[uName]);
        }
        
        var dirLoc = gl.getUniformLocation(program, 'direction');
        if (dirLoc) gl.uniform2f(dirLoc, 1.0, -1.0);
        var colorLoc = gl.getUniformLocation(program, 'color');
        if (colorLoc) gl.uniform3f(colorLoc, 0.0, 0.0, 0.0);
        
        var stepsLoc = gl.getUniformLocation(program, 'steps');
        if (stepsLoc) gl.uniform1i(stepsLoc, 50);
        var sqMinLoc = gl.getUniformLocation(program, 'squaresMin');
        if (sqMinLoc) gl.uniform2i(sqMinLoc, 20, 20);

        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, texFrom);
        gl.activeTexture(gl.TEXTURE1);
        gl.bindTexture(gl.TEXTURE_2D, texTo);

        gl.viewport(0, 0, cv.width, cv.height);

        var startTime = null;
        function easeInOut(t) {
            return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
        }

        function frame(ts) {
            if (!startTime) startTime = ts;
            var elapsed = ts - startTime;
            var t = Math.min(elapsed / duration, 1.0);
            var p = easeInOut(t);

            gl.uniform1f(uProgress, p);
            gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

            if (t < 1.0) {
                requestAnimationFrame(frame);
            } else {
                
                gl.deleteTexture(texFrom);
                gl.deleteTexture(texTo);
                gl.deleteProgram(program);
                gl.deleteBuffer(buf);
                cv.remove();
                if (callback) callback();
            }
        }
        requestAnimationFrame(frame);

    }).catch(function(err) {
        console.error('ImpulseGLT capture error:', err);
        newEl.style.visibility = origNewVis;
        newEl.style.zIndex = origNewZ;
        newEl.style.opacity = origNewOp;
        if (callback) callback();
    });
}

function getList() {
    return Object.keys(TRANSITIONS);
}

window.ImpulseGLT = {
    run: run,
    getList: getList,
    TRANSITIONS: TRANSITIONS
};

})();
