#version 410

//#pragma include "include/skyboxes.glsl"
#pragma include "spect.glsl"
#pragma include "effects.glsl"
#pragma include "rand.glsl"

//// Uniforms for the texture, fade effect

uniform float u_SkyboxFade;
uniform float u_MixFactor;
uniform int u_From;
uniform int u_To;
uniform sampler2D u_SpectrogramTex;
uniform float u_Time;
//uniform float osg_FrameTime;
uniform bool u_Waveform;
uniform float u_Surface;
uniform bool u_Eq;

uniform sampler2D u_TextureWLL;
uniform sampler2D u_TextureTRS;
uniform sampler2D u_TexturePTR;
uniform sampler2D u_TexturePNO;
uniform sampler2D u_TextureEQR;
uniform sampler2D u_TextureBLL;
uniform sampler2D u_TextureCNR;
uniform sampler2D u_TextureOLD;

// Input texture coordinates from the vertex shader
in vec2 vertMultiTexCoord0;
in vec4 vertPosition;


// Output fragment color
out vec4 p3d_FragColor;


// The MIT License
// Copyright © 2013 Inigo Quilez
float noise( in vec2 p )
{
    vec2 i = floor( p );
    vec2 f = fract( p );
    vec2 u = f*f*(3.0-2.0*f);
    return mix( mix( rand( i + vec2(0.0,0.0) ),
                     rand( i + vec2(1.0,0.0) ), u.x),
                mix( rand( i + vec2(0.0,1.0) ),
                     rand( i + vec2(1.0,1.0) ), u.x), u.y);
}


float computeWeight(float lim, float transition) {
    return smoothstep(0.0, transition, lim) *
           smoothstep(1.0, 1.0 - transition, lim);
}


float getCoefficient(vec2 lim, float transition) {
    return computeWeight(lim.x, transition) * computeWeight(lim.y, transition);
}


const float overallSpeed = 0.2;
const float gridSmoothWidth = 0.015;
const float axisWidth = 0.05;
const float majorLineWidth = 0.025;
const float minorLineWidth = 0.0125;
const float majorLineFrequency = 5.0;
const float minorLineFrequency = 1.0;
const vec4 gridColor = vec4(0.5);
const float scale = 5.0;
const vec4 lineColor = vec4(0.25, 0.5, 1.0, 1.0);
const float minLineWidth = 0.02;
const float maxLineWidth = 0.5;
const float lineSpeed = 1.0 * overallSpeed;
const float lineAmplitude = 1.0;
const float lineFrequency = 0.2;
const float warpSpeed = 0.2 * overallSpeed;
const float warpFrequency = 0.5;
const float warpAmplitude = 1.0;
const float offsetFrequency = 0.5;
const float offsetSpeed = 1.33 * overallSpeed;
const float minOffsetSpread = 0.6;
const float maxOffsetSpread = 2.0;
const int linesPerGroup = 16;

const vec4[] bgColors = vec4[]
    (
        lineColor * 0.5,
        lineColor - vec4(0.2, 0.2, 0.7, 1)
        );

#define drawCircle(pos, radius, coord) smoothstep(radius + gridSmoothWidth, radius, length(coord - (pos)))

#define drawSmoothLine(pos, halfWidth, t) smoothstep(halfWidth, 0.0, abs(pos - (t)))

#define drawCrispLine(pos, halfWidth, t) smoothstep(halfWidth + gridSmoothWidth, halfWidth, abs(pos - (t)))

#define drawPeriodicLine(freq, width, t) drawCrispLine(freq / 2.0, width, abs(mod(t, freq) - (freq) / 2.0))

float drawGridLines(float axis)
{
    return   drawCrispLine(0.0, axisWidth, axis)
           + drawPeriodicLine(majorLineFrequency, majorLineWidth, axis)
           + drawPeriodicLine(minorLineFrequency, minorLineWidth, axis);
}

float drawGrid(vec2 space)
{
    return min(1., drawGridLines(space.x)
                  +drawGridLines(space.y));
}

// probably can optimize w/ noise, but currently using fourier transform
float random(float t)
{
    return (cos(t) + cos(t * 1.3 + 1.3) + cos(t * 1.4 + 1.4)) / 3.0;
}

float getPlasmaY(float x, float horizontalFade, float offset)
{
    return random(x * lineFrequency + u_Time * lineSpeed) * horizontalFade * lineAmplitude + offset;
}


vec3 getFragColor(int index) {
    switch (index) {

        case WLL: return texture(u_TextureWLL, vertMultiTexCoord0).rgb;

        case CRD: {
            /* This work is protected under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License
             * more information canbe found at:
             * https://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US
             */

            vec2 uv = vec2((vertPosition.x + 1.0) * 0.5, (1.0 - vertPosition.z) * 0.5);
            vec2 space = vec2((vertPosition.x + 1.0) * 0.5, (1.0 - vertPosition.z) * 0.5) * scale + vec2(0.0, -2.5);

            float horizontalFade = 1.0 - (cos(uv.x * 6.28) * 0.5 + 0.5);
            float verticalFade = 1.0 - (cos(uv.y * 6.28) * 0.5 + 0.5);

            // fun with nonlinear transformations! (wind / turbulence)
            space.y += random(space.x * warpFrequency + u_Time * warpSpeed) * warpAmplitude * (0.5 + horizontalFade);
            space.x += random(space.y * warpFrequency + u_Time * warpSpeed + 2.0) * warpAmplitude * horizontalFade;

            vec4 lines = vec4(0);

            for(int l = 0; l < linesPerGroup; l++)
            {
                float normalizedLineIndex = float(l) / float(linesPerGroup);
                float offsetTime = u_Time * offsetSpeed;
                float offsetPosition = float(l) + space.x * offsetFrequency;
                float rand = random(offsetPosition + offsetTime) * 0.5 + 0.5;
                float halfWidth = mix(minLineWidth, maxLineWidth, rand * horizontalFade) / 2.0;
                float offset = random(offsetPosition + offsetTime * (1.0 + normalizedLineIndex)) * mix(minOffsetSpread, maxOffsetSpread, horizontalFade);
                float linePosition = getPlasmaY(space.x, horizontalFade, offset);
                float line = drawSmoothLine(linePosition, halfWidth, space.y) / 2.0 + drawCrispLine(linePosition, halfWidth * 0.15, space.y);

                float circleX = mod(float(l) + u_Time * lineSpeed, 25.0) - 12.0;
                vec2 circlePosition = vec2(circleX, getPlasmaY(circleX, horizontalFade, offset));
                float circle = drawCircle(circlePosition, 0.01, space) * 4.0;


                line = line + circle;
                lines += line * lineColor * rand;
            }

            vec4 fragColor;

            fragColor = mix(bgColors[0], bgColors[1], uv.x);
            fragColor *= verticalFade;
            fragColor.a = 1.0;
            // debug grid:
            //fragColor = mix(fragColor, gridColor, drawGrid(space));
            fragColor += lines;

//            return fragColor.rgb;

            vec2 lim = (1.0 - vertPosition.xz) * 0.5;
            float coefficient;
            if (vertPosition.y < 0.0) {
                coefficient = getCoefficient(lim, 0.3);
            } else {
                coefficient = 0.0;
            }
            return fragColor.rgb * coefficient;

        }

//        return texture(u_TextureWLL, vertMultiTexCoord0).rgb;

        case TRS: return texture(u_TextureTRS, vertMultiTexCoord0).rgb;

        case PTR: return texture(u_TexturePTR, vertMultiTexCoord0).rgb;

        case PNO: return texture(u_TexturePNO, vertMultiTexCoord0).rgb;

        case BLL: return texture(u_TextureBLL, vertMultiTexCoord0).rgb;

        case OLD: return texture(u_TextureOLD, vertMultiTexCoord0).rgb;

        case EQR: {
            vec3 baseCol = texture(u_TextureEQR, vertMultiTexCoord0).rgb;
            #define H_MULT 20.0
            #define V_MULT 40.0
            #define BIN_H_PERCENTAGE 0.8
            #define BIN_V_PERCENTAGE 0.8
            #define BOTTOM_LEVEL 0.0 // Lower level, values below this are clamped
            #define TOP_LEVEL 1.2    // Upper level, values above this are clamped
            if (
                u_Eq &&
                vertPosition.x > -0.999 &&
                vertPosition.x < 0.98 &&
                vertPosition.z > -1.00 &&
                vertPosition.z < 1.00 &&
                vertPosition.y > 0.00
            ) {
                vec2 uv = vec2((vertPosition.x + 1.0) * 0.5, (1.0 - vertPosition.z) * 0.5);
                float frequency = floor(uv.x * H_MULT) / H_MULT;
                float height = floor(uv.y * V_MULT) / V_MULT;
                float binWidth = 1.0 / H_MULT;
                float binHeight = 1.0 / V_MULT;
                float positionInHBin = fract(uv.x / binWidth);
                float positionInVBin = fract(uv.y / binHeight);
                float magnitude = floor((getMagnitude(u_SpectrogramTex, frequency, u_Time) * 4.45 - 2.7) * V_MULT) / V_MULT;
                float clampedHeight = clamp(height * 1.3, BOTTOM_LEVEL, TOP_LEVEL);
                if (magnitude > height && positionInHBin < BIN_H_PERCENTAGE && positionInVBin < BIN_V_PERCENTAGE) {
                    if (clampedHeight <= 0.5) { // If the clamped height is in the lower range
                        vec3 eqCol = mix(vec3(0.0, 1.0, 0.0), vec3(1.0, 1.0, 0.0), clampedHeight * 2.0);// Blend green to yellow
                        baseCol = mix(baseCol, eqCol * 10., 0.5);
                    } else { // If the clamped height is in the upper range
                        vec3 eqCol = mix(vec3(1.0, 1.0, 0.0), vec3(1.0, 0.0, 0.0), (clampedHeight - 0.5) * 2.0);// Blend yellow to red
                        baseCol = mix(baseCol, eqCol * 10., 0.5);
                    }
                }
            }
            return baseCol;
        }

//        case BLL: return texture(u_TextureBLL, vertMultiTexCoord0).rgb;

        case CNR: return texture(u_TextureCNR, vertMultiTexCoord0).rgb;

        case GRM: {
            // Based on Vinyl Suface by c0de4 (https://www.shadertoy.com/view/4slBWl)
            #define SCALE 0.2
            #define OFFSET vec2(0.0, 0.0)
            #define SPEED 1.0
            #define GAIN 100.0
            #define TRANSITION 0.5
            #define VINYL_COLOR vec3(0.1, 0.1, 0.13)
            #define TWO_PI 6.28318530718
            vec2 p = (1.0 - vertPosition.xz / SCALE) * 0.5 - OFFSET;
            vec2 lim = (1.0 - vertPosition.xz) * 0.5;
            float color = 0.0;
            float time = u_Surface * TWO_PI;
            for(float i = 0.; i < 4.; i++) {
                float j = i + 1.0;
                p += vec2(noise(p+j)-cos(time*.5)+noise(vec2(time))*.1, noise(p*j)-sin(time*j) * .01 );
                vec2 q = vec2(noise(vec2(p+j)) );
                float a = length(p-q);
                color += .02/length(p-a);
            }
            float coefficient = getCoefficient(lim, TRANSITION);
            return vec3(VINYL_COLOR * color * coefficient * GAIN);
        }
    }
    return vec3(0);
}


void main() {
    // Apply the fade effect to the texture and output the final color

    vec3 fragColor0 = getFragColor(u_From);
    vec3 fragColor1 = getFragColor(u_To);

//    float u_SkyboxFade = 0.1;

    float mixValue = smoothstep(-1, 1, vertPosition.x + (u_MixFactor - 0.5) * 4.0);
    p3d_FragColor = vec4(mix(fragColor0, fragColor1, mixValue) * u_SkyboxFade, 1.0);

}
