#version 410

#pragma include "vertex.glsl"
#pragma include "spect.glsl"
#pragma include "effects.glsl"
//#pragma include "pointer.glsl"

#define Q0 0.0
#define Q1 HALF_PI
#define Q2 PI
#define Q3 3.0 * HALF_PI


// Array of colors for each of the 12 notes in the Scriabin Keyboard
const vec3 SCRIABIN_KEYBOARD_COLORS[12] = vec3[12](
    vec3(0., 0., 0.), /* C */
    vec3(0., 0., 0.), /* C# */
    vec3(0., 0., 0.), /* D */
    vec3(0., 0., 0.), /* D# */
    vec3(0., 0., 1.), /* E */
    vec3(0., 0., 0.), /* F */
    vec3(0., 1., 0.), /* F# */
    vec3(0., 1., 1.), /* G */
    vec3(0., 0., 0.), /* G# */
    vec3(1., 0., 0.), /* A */
    vec3(0., 0., 0.), /* A# */
    vec3(1., 0., 1.)  /* H */
);

//// Real array of colors for each of the 12 notes in the Scriabin Keyboard
//const vec3 REAL_KEYBOARD_COLORS[12] = vec3[12](
//    vec3(0),  /* C */
//    vec3(0.), /* C# */
//    vec3(0.), /* D */
//    vec3(0.), /* D# */
//    vec3(vec3(0.4824, 0.4078, 0.9333)), /* E */
//    vec3(0.), /* F */
//    vec3(vec3(0.3725, 0.6196, 0.6275)), /* F# */
//    vec3(vec3(0.4980, 1.0000, 0.8314)), /* G */
//    vec3(0.), /* G# */
//    vec3(vec3(0.2824, 0.8196, 0.8000)), /* A */
//    vec3(vec3(0.2549, 0.4118, 0.8824)), /* A# */
//    vec3(vec3(0.6902, 0.8784, 0.9020))  /* H */
//);


// Real array of colors for each of the 12 notes in the Scriabin Keyboard
//const vec3 REAL_KEYBOARD_COLORS[12] = vec3[12](
//    vec3(0),  /* C */
//    vec3(0.), /* C# */
//    vec3(0.), /* D */
//    vec3(0.), /* D# */
//    vec3(vec3(0.8549, 0.4392, 0.8392)), /* E */
//    vec3(0.), /* F */
//    vec3(vec3(0.8667, 0.6275, 0.8667)), /* F# */
//    vec3(vec3(0.4824, 0.4078, 0.9333)), /* G */
//    vec3(0.), /* G# */
//    vec3(vec3(0.5765, 0.4392, 0.8588)), /* A */
//    vec3(0.), /* A# */
//    vec3(vec3(0.5765, 0.4392, 0.8588))  /* H */
//);
const vec3 REAL_KEYBOARD_COLORS[12] = vec3[12](
    vec3(0),              /* C */
    vec3(0.),             /* C# */
    vec3(0.),             /* D */
    vec3(0.),             /* D# */
    vec3(0.1961, 0.5882, 1.0),  /* E - Niebieski neonowy */
    vec3(0.),             /* F */
    vec3(0.1961, 1.0, 0.1961),  /* F# - Zielony neonowy */
    vec3(0.7059, 0.1961, 1.0),  /* G - Fioletowy elektryczny */
    vec3(0.),             /* G# */
    vec3(1.0, 0.1961, 0.1961),  /* A - Czerwony intensywny */
    vec3(0.),             /* A# */
    vec3(1.0, 0.5490, 0.0)      /* H - Pomarańczowy jaskrawy */
);



// Define an array of domino colors
vec3 domCols[4] = vec3[](
    vec3(1.0, 0.0, 0.0), // Red
    vec3(0.0, 1.0, 0.0), // Green
    vec3(0.0, 0.0, 1.0), // Blue
    vec3(1.0, 1.0, 0.0)  // Yellow
);

// Simple and readable defines
#define _ false  // Empty space (underscore represents false)
#define X true   // Filled space (X represents true)

// Total width and height considering letters and spacing
const int BEG_W = 5 * 6 + 5;
const int BEG_H = 11;
const int END_W = 43;
//const int END_H = 11;
const int END_H = 43;

// Uniform inputs
uniform samplerBuffer u_InstanceDataEFF;  // Instance data buffer wall

uniform bool u_Bullet;
uniform bool u_Pop;
uniform vec3 u_BulletPos;
uniform float u_Variable;
uniform sampler2D u_GreetTex;
uniform int u_From;
uniform int u_To;
//uniform int u_Effect;

// Wall
uniform float u_Emboss;
uniform float u_Fall;
uniform float u_WallBright;
uniform sampler2D u_MusicTex;
uniform bool u_frontEffect;
uniform float u_Factor0;
uniform float u_Factor1;
uniform float u_Factor2;
uniform float u_Factor3;
uniform float u_Factor4;
uniform float u_Factor5;
uniform vec2 u_Pair0;
uniform vec2 u_Pair1;
uniform vec2 u_Pair2;
uniform vec2 u_Pair3;
uniform vec2 u_Pair4;
uniform vec2 u_Pair5;
uniform float u_Glow;
uniform bool u_Remove;
uniform bool u_Begin;
uniform sampler2D u_CreditsTex;
uniform float u_DamageCols;
uniform sampler2D u_GradientTex;
uniform float u_GripX;
uniform float u_GripY;
uniform bool u_Swaps;
uniform float u_WallWhite;
uniform sampler2D u_FinalTex;
uniform vec2 u_Min;
uniform vec2 u_Max;
uniform float u_Print;
uniform bool u_Cold;
uniform float u_Angle;


// Torus
uniform float u_Blast;
uniform bool u_Loco;
uniform bool u_Extra;
uniform float u_Bass;
uniform float u_False;
uniform vec3 u_FalseVect;
uniform vec3 u_FalseBase;
uniform bool u_Pulse;
uniform float u_SpreadExtra;
uniform float u_Deform;

// ProTracker
uniform float u_Pattern;
uniform sampler2D u_WaveformTex;
uniform sampler2D u_EnvelopeTex;
uniform sampler2D u_PointerTex;
uniform float u_Force;
uniform float u_Select;

// Piano
uniform float u_Note;
uniform bool u_Upper;
uniform float u_Sign;

// Plane
uniform sampler2D u_SpectrogramTex;
//uniform float u_Intensity;

// Ball
uniform float u_Explode;
uniform float u_Rot;
uniform float u_Onion;

// Corner
uniform sampler2D u_CornerTex;
uniform float u_FaceX;
uniform float u_FaceY;
uniform float u_FaceZ;

// Gramophone
uniform sampler2D u_RevTex;
uniform float u_Progress;
uniform float u_RotTime;
uniform float u_Needle;

// Old
uniform float u_Dune;


uniform float u_TimeArray[10];

//out vec4 emission;

struct InstanceData {
    vec3 offset;
    vec3 color;
};

//struct InstanceRange {
//    int from;
//    int to;
//};

struct EffectResult {
    vec3 position;
    vec4 color;
    vec3 offset;
    vec3 angle;
};

InstanceData fetchInstanceData(samplerBuffer u_InstanceData, int index) {
    InstanceData data;
    data.offset = texelFetch(u_InstanceData, index).xyz;
    data.color = texelFetch(u_InstanceData, index + 1).rgb;
    return data;
}


EffectResult effectNON(vec3 position, vec3 color, vec3 offset) {
    return EffectResult(position, vec4(color, 0.0), offset, vec3(0.0));
}


struct ColorAngle {
    vec3 color;
    vec3 angle;
};

struct OffsetColorAngle {
    vec3 offset;
    vec3 color;
    vec3 angle;
};


vec3 getAngle(float brightness, bool vertical) {

    vec3 hexColsH[6] = vec3[](
        vec3(Q2, Q0, Q0),
        vec3(Q1, Q0, Q0),
        vec3(Q0, Q3, Q0),
        vec3(Q0, Q1, Q0),
        vec3(Q3, Q0, Q0),
        vec3(Q0, Q0, Q0)
    );
    vec3 hexColsV[6] = vec3[](
        vec3(Q0, Q0, Q2),
        vec3(Q1, Q0, Q0),
        vec3(Q0, Q0, Q3),
        vec3(Q0, Q0, Q1),
        vec3(Q3, Q0, Q0),
        vec3(Q0, Q0, Q0)
    );

    int brightIndex = int(brightness * 6.0);
//    brightIndex = 5;
    vec3 angle;
    if (vertical) {
        angle = hexColsV[brightIndex];
    } else {
        angle = hexColsH[brightIndex];
    }

    return angle;
}


ColorAngle getFaceData(vec2 st, int portrait, bool vertical) {
    #define PORTRAITS 13 + 1 + 1

    ColorAngle colorAngle;

    vec2 uv = (st + vec2(0.5, 0.5)) / 80.0 + 0.5;
    colorAngle.color = texture(u_MusicTex, (uv + vec2(0.0, portrait)) / vec2(1.0, PORTRAITS)).rgb;
    float brightness = dot(colorAngle.color, vec3(0.2126, 0.7152, 0.0722)) - 1. / 255.;

    colorAngle.angle = getAngle(brightness, vertical);

    return colorAngle;
}


float softCos(float factor) {
    return cos((factor - 0.5) * TWO_PI) * 0.5 + 0.5;
}


OffsetColorAngle swapFace(vec3 offset, vec3 angle, vec2 st, bool vertical, vec2 pair, float factor) {
    #define COL_GAIN 10.0
    #define OFF_GAIN 0.4

    ColorAngle caf = getFaceData(st, int(pair.x), vertical);
    ColorAngle cat = getFaceData(st, int(pair.y), vertical);
    ColorAngle ca;
    ca.color = mix(caf.color, cat.color, factor);
    ca.angle = mix(caf.angle, cat.angle, factor);

    #define SIMPLEX_SCALE 0.1
    #define TIME_FACTOR 0.8
    #define AMPLITUDE 0.7
    float perlin;
    if (u_Swaps) {
        perlin = simplex3d(vec3(st * SIMPLEX_SCALE, u_Time * TIME_FACTOR)) * AMPLITUDE + 1.0;
    } else {
        perlin = 1.0;
    }

    float trans = softCos(factor) * perlin;
    ca.color *= trans * COL_GAIN + 1.0;
    float len = (34.64 - length(offset)) * 0.1732;
    offset *= trans * 0.732 * len * OFF_GAIN + 1.0;
    angle += vec3(trans);

    OffsetColorAngle oca;
    oca.offset = offset;
    oca.color = ca.color;
    oca.angle = ca.angle;
    return oca;
}

// ===========================================================
// ========================[ WLL ]============================
// ===========================================================

EffectResult effectWLL(vec3 position, vec3 color, vec3 offset) {

    #define SHOW_SPREAD 10.0
    #define GROUPS 10.0
    #define FALL_SPREAD 2.0
    #define FALL_Y_SPEED 180.0
    #define FALL_Z_SPEED 180.0
    #define ANGLE_X_SPEED 40.0
    #define ANGLE_Y_SPEED 20.0
    #define ANGLE_Z_SPEED 20.0
    #define CUB_X 23
    #define CUB_Y 11

    vec3 angle = vec3(0.0);

    ColorAngle colorAngle;
    OffsetColorAngle oca;
    int portrait;

    if (
        ((gl_InstanceID >= 1600 && gl_InstanceID < 3200) || (gl_InstanceID >= 4800 && gl_InstanceID < 9600))
//        gl_InstanceID < 9600
        && u_Remove
    ) {
        offset.x = -1000.0;
    }

    if (u_Begin) {
        if (offset.x >= 20.0) {
            angle += vec3(0.0, 0.0, -Q1);
            vec2 st = vec2(offset.y, offset.z);
            oca = swapFace(offset, angle, st, true, u_Pair0, u_Factor0);
        } else if (offset.y >= 20.0) {
            angle += vec3(0.0, 0.0, Q2);
            vec2 st = vec2(-offset.x, offset.z);
            oca = swapFace(offset, angle, st, true, u_Pair1, u_Factor1);
        } else if (offset.x <= -21.0) {
            angle += vec3(0.0, 0.0, Q1);
            vec2 st = vec2(-offset.y, offset.z);
            oca = swapFace(offset, angle, st, true, u_Pair2, u_Factor2);
        } else if (offset.y <= -21.0) {
            angle += vec3(0.0, 0.0, 0.0);
            vec2 st = vec2(offset.x, offset.z);
            oca = swapFace(offset, angle, st, true, u_Pair3, u_Factor3);
        } else if (offset.z >= 20.0) {
            angle += vec3(Q1, 0.0, 0.0);
            vec2 st = vec2(offset.x, offset.y);
            oca = swapFace(offset, angle, st, false, u_Pair4, u_Factor4);
        } else if (offset.z <= -21.0) {
            angle += vec3(Q1, Q2, Q0);
            vec2 st = vec2(offset.x, -offset.y);
            oca = swapFace(offset, angle, st, false, u_Pair5, u_Factor5);
        } else {
            oca.offset = offset;
            oca.angle = vec3(0.0);
            oca.color = color;
        }
        offset = oca.offset;
        angle += oca.angle;
        color = oca.color;

        if (gl_InstanceID >= 9600 && u_Begin && !u_Cold) {
            float glow = softCos(u_Glow);
            color = vec3(100.0, 0.0, 0.0) * glow;
//            angle += vec3(osg_FrameTime * 2.0 + rand3(offset) * 10.0);
            angle += vec3(u_Time * 2.0 + rand3(offset) * 10.0);
        }
    } else {
        vec3 tile = round((offset.xyz + vec3(44.0)) / 8.);

        // Random color generation
//        color = rand3(tile);

        // Gradient color generation
        color = texture(u_GradientTex, rand2(tile)).rgb;

        float delay = rand(tile);
//        color *= sin(osg_FrameTime + delay * TWO_PI) + 1.0;
        color *= sin(u_Time + delay * TWO_PI) + 1.0;
        color = mix(color, vec3((5. / 6.) + 2. / 255.), smoothstep(0.0, 1.0, u_DamageCols + delay));
    }

    int w = (u_Begin) ? BEG_W : END_W;
    int h = (u_Begin) ? BEG_H : END_H;

    bool elev;
    vec2 st;
    vec2 uv;
    vec2 move;
    float wall;
    float cred;

//    float u_GripX = 0.0;
//    float u_GripY = 1.0;

    float grip;
//    grip = sin(osg_FrameTime * 2.0) * 0.5 + 0.5;

    if (u_Begin) {
        elev = offset.y <= -21.0;
        st = vec2(offset.x, offset.z);
        uv = (st + vec2(-0.5, 0.5)) / 80. + 0.5;
        move = vec2(0.0, -1.0);
        grip = 1.0;
    } else {
        if (abs(offset.x) >= 22.0) {
            elev = true;
            st = vec2(offset.y, offset.z);
            uv = (st + vec2(-0.5, 0.5)) / 88. + 0.5;
            wall = sign(offset.x);
            move = vec2(wall, 0.0);
            cred = 2.0 + wall;
            grip = u_GripX;
        } else if (abs(offset.y) >= 22.0) {
            elev = true;
            st = vec2(offset.x, offset.z);
            uv = (st + vec2(-0.5, 0.5)) / 88. + 0.5;
            wall = sign(offset.y);
            move = vec2(0.0, wall);
            cred = 3.0 + wall;
            grip = u_GripY;
        } else {
            elev = false;
        }
    }

    if (
        st.x >= -w / 2.&&
        st.x <= w / 2. &&
        st.y >= -h / 2. &&
        st.y <= h / 2. &&
        u_frontEffect
    ) {
        float title;
        if (u_Begin) {
            title = texture(u_MusicTex, (uv + vec2(0.0, 14.0)) / vec2(1.0, PORTRAITS)).r;
        } else {
            title = texture(u_CreditsTex, (uv + vec2(0.0, cred)) / vec2(1.0, 5.0)).r;
        }

        if (title > 0.5 && elev) {
            float show_rnd;
            if (u_Begin) {
                show_rnd = round(rand(offset.xz) * (1.0 - 1.0 / SHOW_SPREAD) * GROUPS) / GROUPS;
            } else {
                show_rnd = round(rand(offset.xz * 2.9) * (1.0 - 1.0 / SHOW_SPREAD) * GROUPS) / GROUPS;
            }
//            show_rnd = round(rand(offset.xz) * (1.0 - 1.0 / SHOW_SPREAD) * GROUPS) / GROUPS;
            float show = clamp((u_Emboss - show_rnd) * SHOW_SPREAD, 0.0, 1.0);
            float fall_rnd = rand(offset.zx) * (1.0 - 1.0 / FALL_SPREAD);
            float fall = pow(max(0.0, u_Fall - fall_rnd), 2);

            angle += vec3(
                ANGLE_X_SPEED * -fall,
                ANGLE_Y_SPEED *  fall * (rand(offset.xz) - 0.5),
                ANGLE_Z_SPEED *  fall * (rand(offset.zx) - 0.5)
            );

            offset.xy += show * 0.85 * move * grip;

            offset.y -= fall * FALL_Y_SPEED;
            offset.z -= fall * FALL_Z_SPEED;

//            #define WALL_COLOR vec3(2.00, -0.06, -0.12)
//            #define WALL_COLOR vec3(2.00, -0.09, -0.18)
            #define WALL_COLOR vec3(2.00, -0.10, -0.20)
//            #define WALL_WHITE vec3(255. / 255., 198./255., 90./255.) * 1.0
            #define WALL_WHITE vec3(0.1)
//            float u_WallWhite = 1.0;
            if (u_Begin) {
                color += WALL_COLOR * (u_WallBright - 1.0) + (u_WallBright - 1.0) * 0.1;
                color = mix(color, WALL_WHITE, u_WallWhite);
            } else {
                // Yo+Min: u_Min = vec2(-23.0, -23.0)
                // Mik: u_Min = vec2(-50.0, -23.0); u_Max = vec2( 22.0,  50.0)
                // Dan: u_Min = vec2(-50.0, -50.0); u_Max = vec2( 22.0,  22.0);
//                vec2 u_Min = vec2(-50.0, -50.0);
//                vec2 u_Max = vec2( 22.0,  22.0);
                if (
                    offset.x > u_Min.x
                    &&
                    offset.x < u_Max.x
                    &&
                    offset.y > u_Min.y
                    &&
                    offset.y < u_Max.y
                ) {
                    color = mix(color, vec3(27.5), show);
                }
            }
        }
    }

    float print = u_Print * 5./6;
    float wave = abs(sin(u_Time * 5.0 + offset.x * 0.3)) * print;
    if (mod(offset.x, 2.0) != 0.0 && abs(offset.z) < wave * 7.0 && print > 0.0) {
        color = mix(color, vec3(0.0), print);
        angle += getAngle(0.9999999 - print, true);
        offset.y -= print * 0.3;
    }




//    vec2 sixSize = textureSize(u_FinalTex, 0);
//    vec2 xy = vec2(-1.0, 1.0) * offset.xz / sixSize + 0.5;
//    if (xy.x >= 0.0 && xy.x <= 1.0 && xy.y >= 0.0 && xy.y <= 1.0) {
//        float six = texture(u_FinalTex, xy).r * u_Print;
////        six = 1.0;
//        color = mix(color, vec3(0.0), six);
//        vec3 a = getAngle(0.9999999 - six, true);
//        angle += a;
//    }


    return EffectResult(position, vec4(color, 1.0), offset, angle);
}

// ===========================================================
// ========================[ TRS ]============================
// ===========================================================

EffectResult effectTRS(vec3 position, vec3 color, vec3 offset) {
    #define MIN_R 25.0
    #define MAX_R 36.0
    #define BASS_FREQUENCY 82.41 // Frequency of the bass oscillation (E2)


//    float u_Blast = sin(osg_FrameTime) * 0.5 + 0.5;
    float r = length(offset.xy);
    float spread = smoothstep(MIN_R, MAX_R, r);

//    float blast = u_Blast * 0.6;

    vec3 angle = rand3(offset) * TWO_PI * u_Blast * spread;
    offset *= (spread * u_Blast + 1.0);

    if (u_Loco) {
        vec3 radCoords = radial(offset);
        float azimuth = radCoords.y;
//        float growth = 1.0 * pow(sin(azimuth * 8. - osg_FrameTime * 5.0) * 0.5 + 0.5, 5.);  // Change direction for last scene!
        float growth = 1.5 * pow(sin(azimuth * 8. - u_Time * 5.0) * 0.5 + 0.5, 5.);  // Change direction for last scene!
        color = color * (1.0 + growth * 5.0) + growth * 2.0;
    }

//    float u_Bass = exp(-fract(osg_FrameTime) * 2.) * 0.2;
    vec3 randomPhase = rand3(offset) * TWO_PI;
//    offset += sin(osg_FrameTime * BASS_FREQUENCY + randomPhase) * u_Bass * 0.5;
    offset += sin(u_Time * BASS_FREQUENCY + randomPhase) * u_Bass * 0.5;

//    offset += u_FalseBase + u_FalseVect * u_False * 5.;
    offset += u_FalseVect * u_False * 5.;

    color *= 1 + u_False * 5.;

    float angle_spread = spread * (1.0 + u_SpreadExtra);

//    if (r > MIN_R && u_Loco) {
//        angle.z += sin(osg_FrameTime * 3.0) * angle_spread * 2.;
//        if (u_Extra) {
//            angle.x += cos(osg_FrameTime * 3.0) * angle_spread * 2.;
//            angle.y += cos(osg_FrameTime * 4.0) * angle_spread * 3.;
//        }
//    }

    if (r > MIN_R && u_Loco) {
    angle.z += sin(u_Time * 3.0) * angle_spread * 2.;
        if (u_Extra) {
            angle.x += cos(u_Time * 3.0) * angle_spread * 2.;
            angle.y += cos(u_Time * 4.0) * angle_spread * 3.;
        }
    }

    if (u_Pulse) {
        offset.xyz *= 1.0 + sin(u_Time * 4.0) * 0.3 + 0.3;
    }

    return EffectResult(position, vec4(color, 1.0), offset, angle);
}

// ===========================================================
// ========================[ PTR ]============================
// ===========================================================

// Structure for a rectangle with minimum and maximum coordinates
struct Rectangle {
    vec2 min; // Bottom-left corner of the rectangle
    vec2 max; // Top-right corner of the rectangle
};

// Structure to return the result of the rectangle hit detection
struct RectangleHit {
    int index;     // Index of the rectangle that was hit (-1 if no hit)
    vec2 relativeUV; // Relative UV coordinates within the rectangle (0 to 1)
};

// Define the number of rectangles
#define NUM_RECTS 9

// Constant array of rectangles
// You can modify these values as needed during experimentation
const Rectangle rects[NUM_RECTS] = Rectangle[NUM_RECTS](
    Rectangle(vec2(-10.0- 1.0, 15.0), vec2(2.0+00.0, 27.0)),  // 0: Scope L0
    Rectangle(vec2(-10.0+15.0, 15.0), vec2(2.0+16.0, 27.0)),  // 1: Scope L1
    Rectangle(vec2(-10.0+31.0, 15.0), vec2(2.0+32.0, 27.0)),  // 2: Scope R0
    Rectangle(vec2(-10.0+47.0, 15.0), vec2(2.0+48.0, 27.0)),  // 3: Scope R1
//    Rectangle(vec2(-51.0, -28.0), vec2(51.0, -24.0)),  // 4: Highlight bar
//    Rectangle(vec2(13.0-48.0, -24.0), vec2(16.0-48.0, -5.0)),  // 4: Bar L0
//    Rectangle(vec2(13.0-24.0, -24.0), vec2(16.0-24.0, -5.0)),  // 5: Bar L1
//    Rectangle(vec2(13.0, -24.0), vec2(16.0, -5.0)),  // 6: Bar R0
//    Rectangle(vec2(13.0+24.0, -24.0), vec2(16.0+24.0, -5.0)),  // 7: Bar R1
    Rectangle(vec2(-51.0, -47.0), vec2(-44.0, -5.0)),  // 4: Pattern number
    Rectangle(vec2(-42.0, -47.0), vec2(-20.0, -5.0)),   // 5: Pattern L0
    Rectangle(vec2(-42.0+24.0, -47.0), vec2(-20.0+23.0, -5.0)),   // 6: Pattern L1
    Rectangle(vec2(-42.0+47.0, -47.0), vec2(-20.0+47.0, -5.0)),   // 7: Pattern R0
    Rectangle(vec2(-42.0+71.0, -47.0), vec2(-20.0+71.0, -5.0))   // 8: Pattern R1
);

// Function to find which rectangle (if any) contains the given UV coordinates
RectangleHit findRectangle(vec2 uv) {
    // Loop over all rectangles defined by NUM_RECTS
    for (int i = 0; i < NUM_RECTS; ++i) {
        // Check if the UV coordinates are inside the current rectangle
        if (uv.x >= rects[i].min.x && uv.x <= rects[i].max.x &&
            uv.y >= rects[i].min.y && uv.y <= rects[i].max.y) {

            // Compute the relative UV coordinates inside the rectangle
            vec2 relativeUV = (uv - rects[i].min) / (rects[i].max - rects[i].min);

            // Return a successful hit result
            return RectangleHit(i, relativeUV);
        }
    }
    // If no rectangle contains the UV, return a "no hit" result
    return RectangleHit(-1, vec2(0.0));
}


EffectResult effectPTR(vec3 position, vec3 color, vec3 offset) {
    #define WAV_W 1.0    // Width of the waveform, default 1.0
    #define WAV_H 6.0    // Height of the waveform, default 6.0
    #define ENV_H 1.0    // Height of the envelope, default 1.0
    #define TXT_POW 2.0  // Power of the text, default 2.0

    vec3 protect = offset;

//    float u_Pattern = fract(osg_FrameTime * 3. / 6.4);
//    float u_Pattern = fract(u_Time * 3. / 6.4);
    float u_Pattern = fract((u_Time + 0.2) * 1. / 6.4);
//    u_Pattern = 1.0;
    float line = floor(u_Pattern * 64.0);

    float brightness = dot(color, vec3(0.2126, 0.7152, 0.0722));

    // Call the function to find which rectangle (if any) contains the UV
    RectangleHit hit = findRectangle(offset.xz);

    float alpha = 1.0;

    bool build = true;

    // Check the hit result
    if (hit.index != -1) {
        // A rectangle was hit
        vec2 uv = hit.relativeUV; // Relative UV within the rectangle
        int id = hit.index;        // Index of the rectangle hit

//        vec3 area = rand3(float(id * 12.9898));
//        color = mix(color, area, 0.75);

        if (id >= 0 && id <= 3) {
            if (offset.z == 15.0) {
//                color = vec3(249./255., 222./255., 74./255.);
                color = vec3(1.0, 1.0, 0.0);
                vec2 wavSize = textureSize(u_WaveformTex, 0);
                float level = texture(u_WaveformTex, vec2((float(id) + uv.x * WAV_W) / 4.0, 1.0 - ((u_Time - 64.0) * 60.0) / wavSize.y)).r;
                offset.z += (level - 0.5) * WAV_H + 6.0;
            } else {
                vec2 crd = vec2(offset.x + 11.0, offset.z - 16.0);
                if (crd.x >= 14.0) {
                    crd.x -= 2.0;
                    offset.x -= 2.0;
                }
                if (crd.x >= 28.0) {
                    crd.x -= 2.0;
                    offset.x -= 2.0;
                }
                if (crd.x >= 0.0 && crd.x < 32.0 && crd.y >= 0.0 && crd.y < 8.0) {
                    build = false;
                    offset.z += crd.y;
                    crd.t *= 2.0;
                    if (crd.x >= 16.0) {
                        crd.x -= 16.0;
                        offset.x -= 16.0;
                        offset.z -= 1.0;
                        crd.y -= 1.0;
                    }
                    crd.x += 0.5;
                    crd.y += 1.5;
                    float probe = texture(u_PointerTex, crd  / 16.0).r;
                    if (probe > 0.0) {
                        color = vec3(1.0, 1.0, 0.0) * probe;
                        offset.y = -20.0;
                        offset.x += 11.0 - 8.0 + 0.5;
                        offset.z -= 16.0 + 8.0 - 1.0;
                        offset.x += sin(u_Time * 1.1 + 1.0) * 24.0;
                        offset.z += cos(u_Time * 0.7) * 24.0;
//                        offset.x += sin(osg_FrameTime * 1.1) * 44.5;
//                        offset.z += sin(osg_FrameTime * 0.7) * 40.0;
                    } else {
                        offset.z -= 17.0;
                        offset.y = 2.0;
                    }
                } else {
//                    build = true;
                    offset.z -= 17.0;
                    offset.y = 2.0;
                }
            }
        } else if (id >= 4 && id <= 8) {

            int bar = 4;
            vec2 st;
            if (offset.z >= -23. && offset.z <= -5.) {
                if (offset.x >= -35. + 24. * 0. && offset.x <= -32. + 24. * 0.) {
                    bar = 0;
                    st = vec2((offset.x + 35. - 24. * 0.) / 4., (offset.z + 23.) / 19.);
                } else
                if (offset.x >= -35. + 24. * 1. && offset.x <= -32. + 24. * 1.) {
                    bar = 1;
                    st = vec2((offset.x + 35. - 24. * 1.) / 4., (offset.z + 23.) / 19.);
                } else
                if (offset.x >= -35. + 24. * 2. && offset.x <= -32. + 24. * 2.) {
                    bar = 2;
                    st = vec2((offset.x + 35. - 24. * 2.) / 4., (offset.z + 23.) / 19.);
                } else
                if (offset.x >= -35. + 24. * 3. && offset.x <= -32. + 24. * 3.) {
                    bar = 3;
                    st = vec2((offset.x + 35. - 24. * 3.) / 4., (offset.z + 23.) / 19.);
                }
            }

            vec2 envSize = textureSize(u_EnvelopeTex, 0);
            float level = texture(u_EnvelopeTex, vec2(float(bar) / 4.0, 1.0 - ((u_Time - 64.0) * 60.0) / envSize.y)).r * ENV_H;
            bool draw = bar < 4 && st.y < level;

            if (draw) {
                if (st.y <= 0.5) {
                    color = mix(vec3(0.0, 1.0, 0.0), vec3(1.0, 1.0, 0.0), st.y * 2.0);  // Blend green to yellow
                } else {
                    color = mix(vec3(1.0, 1.0, 0.0), vec3(1.0, 0.0, 0.0), (st.y - 0.5) * 2.0);  // Blend yellow to red
                }
                if (st.x < 0.25) {
//                    color *= 2.0;
                    offset.y = -0.5;
                } else if (st.x >= 0.75) {
//                    color *= 0.5;
                    offset.y = -0.5;
                } else {
                    offset.y = -1.0;
                }
            } else {
                float pos = offset.z - line * 1.;
                float fill;
                if (pos > -93. && pos < -23.) {  // Keep!!!
                    fill = pow(rand(vec2(offset.x, pos)), TXT_POW);
                } else {  // Keep!!!
                    fill = 0.0;  // Keep!!!
                    alpha = 0.0;
                }  // Keep!!!
                if (offset.z == -24.) {
                    color = vec3(187./255.);
                    offset.y = -0.5;
                } else if (offset.z == -28.) {
                    color = vec3(85./255.);
                    offset.y = -0.5;
                } else if (offset.z > -28. && offset.z < -24.) {
                    color = vec3(136./255) * (1.0 - fill);// Random color
                    offset.y = -1.0;
                } else {
                    //                color = vec3(54./255., 68./255., 245./255.) * fill;  // Random color
                    color = vec3(0.0, 0.0, 1.0) * fill;// Random color
                }
            }
        }
//        else if (id == 4) {
//            color = vec3(1.0, 1.0, 0.0);
//            offset.y = -1.0;
//        }

    } else {
        // No rectangle was hit
        // Handle the case where the UV is not within any rectangle
//        offset.y -= ((1.0 - abs(brightness - 0.5)) - 1.0) * 4.0;
        offset.y -= (brightness - 0.5) * 4.0;
    }


    brightness = dot(color, vec3(0.2126, 0.7152, 0.0722));
    vec3 angle = getAngle(brightness, true);

    float choose = rand(protect);
    if (choose < u_Select && build && offset.y < 2.0) {
//        float beat = rand(-protect * 2.3);
        float beat = rand(-protect * 2.3) * 0.5 + 0.5;
        float jump = beat * u_Force * 10.;
        angle += (rand3(protect) - 0.5) * jump * 1.0;
        offset.y -= jump;
    }

    return EffectResult(position, vec4(color, alpha), offset, angle);
}

// ===========================================================
// ========================[ PNO ]============================
// ===========================================================

EffectResult effectPNO(vec3 position, vec3 color, vec3 offset) {

    #define NOTE_BRIGHT 70.;

    vec3 vertCode = color;

    if (vertCode == vec3(0., 1., 0.)) {
        color = vec3(0., 0., 0.);
    } else if (
        vertCode == vec3(0., 0., 1.) ||
        vertCode == vec3(0., 1., 1.) ||
        vertCode == vec3(1., 0., 0.) ||
        vertCode == vec3(1., 0., 1.)
    ) {
        color = vec3(1., 1., 1.);
    }

    float noteBright;
    if (offset.z > 0.0) {
        color = vec3(1.0);
        neg = u_Sign;
        noteBright = -NOTE_BRIGHT;
    } else {
        noteBright = NOTE_BRIGHT;
    }

//    float u_Note = 12.0;

    int note = int(u_Note); // The note index (0 for C, 1 for C#, ..., 11 for B)
    float noteOffset = fract(u_Note); // The offset from the note index (0.0 for C, 0.1 for C#, ..., 0.9 for B)
    vec3 noteColor = SCRIABIN_KEYBOARD_COLORS[note];  // Get the color of a note

    bool octave = (u_Upper && offset.x >= 0.0) || (!u_Upper && offset.x < 0.0);

    bool hit = vertCode == noteColor;

//    note = -1;

    if (hit && octave) {
        color += noteOffset * noteBright * REAL_KEYBOARD_COLORS[note];
        offset.z -= noteOffset * 3.;
    }

    vec3 angle = vec3(Q1, 0.0, Q2);

    return EffectResult(position, vec4(color, 1.0), offset, angle);
}

// ===========================================================
// ========================[ EQR ]============================
// ===========================================================

EffectResult effectEQR(vec3 position, vec3 color, vec3 offset) {

    #define DIM_X 33.0
    #define DIM_Y 33.0
    #define DIM_Z 21.0

    float pos_x = (offset.x + DIM_X / 2.0) / DIM_X;
    float pos_y = (offset.y + DIM_Y / 2.0) / DIM_Y;
    float dist = length(offset.xy) * 0.001;

//    float magnitude = getMagnitude(u_SpectrogramTex, dist, osg_FrameTime);
    float magnitude = getMagnitude(u_SpectrogramTex, dist, u_Time);

    vec3 angle = (round(rand3(offset) * 4.0) * HALF_PI);

    offset.z += magnitude * 20.0 - 20.;
    offset.z *= 1.325;

    float add = simplex3d(
        vec3(
            (offset.x - DIM_X / 2.0) * (offset.y - DIM_Y / 2.0),
            (offset.z - DIM_Z / 2.0) * 0.1,
            u_Time * 0.8
        )
    ) * HALF_PI;
    angle += vec3(0.0, 0.0, add);

//    position = rot(position, angle);

//    color *= 2.;

    return EffectResult(position, vec4(color, 1.0), offset, angle);
//    return EffectResult(position, vec4(color, 0.0), offset, angle);
}

// ===========================================================
// ========================[ BLL ]============================
// ===========================================================

//#define RADIAL_THRESHOLD 14.0
//#define OFFSET_MULTIPLIER 2.4
//#define POSITION_MULTIPLIER 5.0

//#define RADIAL_THRESHOLD 13.0
//#define OFFSET_MULTIPLIER 2.4
//#define POSITION_MULTIPLIER 1.0

#define RADIAL_THRESHOLD 13.0
#define OFFSET_MULTIPLIER 2.4
#define POSITION_MULTIPLIER 1.1

#define SMALL_BALL 8.0

EffectResult effectBLL(vec3 position, vec3 color, vec3 offset) {
    color = vec3(0.1);
    vec3 radialCoords = radial(offset);
    vec3 angle = vec3(0.0, -radialCoords.z, -radialCoords.y);  // Rotate to face inward

    vec3 origOffset = offset;
    float explode;
    if (radialCoords.x > SMALL_BALL) {
        explode = u_Explode;
    } else {
        explode = 0.0;
//        angle += vec3(1.9, 2.0, 2.1) * u_Time;
        angle += vec3(1.9, 2.0, 2.1) * u_Time * (rand3(origOffset) * 4.0 - 2.0);
    }
    angle += (vec3(u_Rot) + rand3(origOffset) * HALF_PI) * explode;

    if (radialCoords.x >= 13.93 && offset.z >= -0.5 && offset.z <= 0.5) {
//        color = vec3(1.0, 0.0, 0.0);
        offset.xy *= 0.93;
    }

    offset *= (explode * 3.0 + 1.0);

//    float u_Onion = sin(osg_FrameTime * 0.5) * 0.5 + 0.5;

    if (radialCoords.x > RADIAL_THRESHOLD) {
        offset *= 1.0 + (OFFSET_MULTIPLIER - 1.0) * u_Onion;
        position *= 1.0 + (POSITION_MULTIPLIER - 1.0) * u_Onion;
        float rotation = sin(u_Time * 2.0);
        angle.x += rotation * u_Onion;
    }

    return EffectResult(position, vec4(color, 1.0), offset, angle);
}

// ===========================================================
// ========================[ CNR ]============================
// ===========================================================

#define DIM_CNR 57.0
#define DIM_HLF DIM_CNR * 0.5
#define DIM_TEX DIM_CNR * 2
#define DIM_OFF 10.0
#define LOGOS 14 + 1
#define DARK 0.1
#define SHINE 50.0
#define LOGO_COL vec3(0.92, 0.86, 0.42)
#define EMBOSS 10.0

vec3 getCorner(vec2 uv, float face) {
//    vec2 uv = (st + vec2(0.5, 0.5)) / 80.0 + 0.5;
    vec2 my_uv = (vec2((uv.xy - 1.) / DIM_HLF + 0.5) + vec2(0., -floor(face + 1.))) / vec2(1., LOGOS);
    return texture(u_CornerTex, my_uv).rgb;
}

EffectResult effectCNR(vec3 position, vec3 color, vec3 offset) {

    vec3 code = color;
//    color = vec3(0.1);
    color = LOGO_COL * DARK;
    vec3 bright = LOGO_COL * SHINE;

    if (code == vec3(0.0, 1.0, 0.0)) {
        vec3 cornerColor = getCorner(offset.xy, u_FaceZ);
//        color += cornerColor * fract(u_FaceZ) * 5.;
        color = mix(color, bright, cornerColor * fract(u_FaceZ));
        offset.z += cornerColor.r * fract(u_FaceZ) * EMBOSS;
    } else if (code == vec3(0.0, 0.0, 1.0)) {
        vec3 cornerColor = getCorner(offset.yz, u_FaceX);
//        color += cornerColor * fract(u_FaceX) * 5.;
        color = mix(color, bright, cornerColor * fract(u_FaceX));
        offset.x += cornerColor.r * fract(u_FaceX) * EMBOSS;
    } else if (code == vec3(1.0, 0.0, 0.0)) {
        vec3 cornerColor = getCorner(offset.zx, u_FaceY);
//        color += cornerColor * fract(u_FaceY) * 5.;
         color = mix(color, bright, cornerColor * fract(u_FaceY));
        offset.y += cornerColor.r * fract(u_FaceY) * EMBOSS;
    } else {
        offset *= 1.5;
    }

    return EffectResult(position, vec4(color, 1.0), offset, vec3(0.0));
}

// ===========================================================
// ========================[ GRM ]============================
// ===========================================================

EffectResult effectGRM(vec3 position, vec3 color, vec3 offset) {
    #define GAIN 2.0  //2.0
    #define NEEDLE_COLOR vec3(1000.0, 500.0, 0.0)
    #define LABEL_COLOR vec3(10.0, 0.2, 0.0)
    #define VINYL_COLOR vec3(0.1, 0.1, 0.13) * 0.5
    #define TONEARM_COLOR vec3(0.6, 0.6, 0.65) * 1.0
    #define SPINDLE_COLOR vec3(0.85, 0.85, 0.9)

//    float u_Progress = sin(osg_FrameTime) * 0.5 + 0.5;

//    float time = osg_FrameTime;
//    float time = u_Time;
    float time = u_RotTime;
//    float time = 1743037654.066411;
//    float time = 1000001.066411;

    vec3 coord = radial(offset);
    float radius = coord.x;
    float deform = sin(coord.y ) * radius * 0.02;
    float lift = sin(time * 3.487 + 2.6) * 0.02 * (1.0 - u_Progress);

    vec3 angle = vec3(0.0);

    if (offset.z == 0.0) {
        vec2 uv = (offset.xy) / 110.0 + 0.5;
        vec3 channels = texture(u_RevTex, uv).rgb;
        if (radius < 1.0) {
            color = SPINDLE_COLOR;
            offset.z = 1.0;
        } else if (radius <= 16.5) {
            color = LABEL_COLOR;
            color += vec3(channels.b) * GAIN;
        } else {
            color = VINYL_COLOR;
            float groove = (55.0 - 16.5) * (1.0 - u_Progress) + 16.5;
//            if (radius <= groove) {
            color += vec3(max(channels.r, channels.g)) * GAIN;
//            }
        }

        offset.z += deform;

        angle = vec3(0.0, 0.0, time * 3.487 * 1.0);
        offset.xy = rot(offset, angle).xy;
    } else {
        color = TONEARM_COLOR;
        float phase = 0.75 * u_Progress + 3.05;
        offset.xy += vec2(-7.0, 5.0);
        angle = vec3(lift, 0.0, phase);
        if (offset.z == 14.0 && offset.y > 0.0) {
            color = NEEDLE_COLOR * u_Needle;
        }
        offset.xyz = rot(offset, angle).xyz;
        offset += vec3(45.0, 30.0, -13.0 + 0.3);  // 45, 25.0
    }

    float brightness = clamp(dot(color, vec3(0.2126, 0.7152, 0.0722)), 0.0, 5./ 6.);
    angle += getAngle(brightness, false) + vec3(Q1, 0.0, 0.0);

//    color = vec3(sin(u_RotTime * 10.) * 0.5 + 0.5);
    return EffectResult(position, vec4(color, 1.0), offset, angle);
}


// ===========================================================
// ========================[ OLD ]============================
// ===========================================================

EffectResult effectOLD(vec3 position, vec3 color, vec3 offset) {

//    float dune = fract(osg_FrameTime * -0.1);
    float dune = u_Dune;
//    float dune = 0.0;

    // Decay and irregularity settings
    const float dec_r = 0.5;  // Decay rate
    const float start = -12.0;
    const float irreg_freq = 0.01;  // Irregularity frequency
    const float irreg_amp = 5.0;   // Irregularity amplitude

    // Compute delay based on simplex noise
    float delay = simplex3d(offset.yxz * irreg_freq) * irreg_amp;

    // Calculate the age of the fragment
    float age = ((dune + 0.3) * 10.0 + 15.0) * dec_r - (offset.x * 0.1 - start + delay);

    vec3 angle;

    // If the age is positive, apply transformations
    if (age > 0.0) {
        const float a_long = 20.0;// Longitudinal acceleration
        const float v_long = 1.0;// Longitudinal velocity
        const float a_lat = 5.0;// Latitudinal acceleration
        const float v_lat = 5.0;// Latitudinal velocity
        const float freq_long = 0.1;// Frequency for longitudinal noise
        const float freq_lat = 1.0;// Frequency for lateral noise
        const float flp = 5.0;// Flight period

        // Compute multipliers for motion based on age
        float mult_long = a_long * pow(age, 2.0) + v_long * age;
        float mult_lat = a_lat * pow(age, 2.0) + v_lat * age;

        // Apply color fade based on age
        color.rgb *= 1.0 + smoothstep(0.0, 2.0, age * 0.45) * 200.;

        vec3 factor = vec3(
            -mult_long,
            simplex3d(vec3(offset.yz * freq_long, age * freq_lat)) * mult_lat,
            simplex3d(vec3(offset.zy * freq_long, age * freq_lat)) * mult_lat
        );

        angle = factor * 0.1;

        // Adjust vertex position using noise functions
        offset.xyz += factor;
    } else {
        angle = vec3(0.0);
    }

    #define BASS_FREQUENCY 82.41 // Frequency of the bass oscillation (E2)
    vec3 randomPhase = rand3(offset) * TWO_PI;
//    offset += sin(osg_FrameTime * BASS_FREQUENCY + randomPhase) * u_Bass * 0.5 * 2.0;
    offset += sin(u_Time * BASS_FREQUENCY + randomPhase) * u_Bass * 0.5 * 2.0;

//    float deform = sin(osg_FrameTime * 1.0) * 0.5 + 0.5;
    if (offset.z < -11.0) {
        float deform = u_Deform;
        float picasso = abs(offset.z + 11.0);
        offset.z -= picasso * deform;
        vec3 ang = vec3(0.0, 0.0, deform * picasso * 0.1);
        angle += ang;
        offset = rot(offset, ang);
    }

    return EffectResult(position, vec4(color, 1.0), offset, angle);
}


EffectResult getEffect(int effect, vec3 position, vec3 color, vec3 offset) {
    switch (effect) {
        case WLL: return effectWLL(position, color, offset);
        case TRS: return effectTRS(position, color, offset);
        case PTR: return effectPTR(position, color, offset);
        case PNO: return effectPNO(position, color, offset);
        case EQR: return effectEQR(position, color, offset);
        case BLL: return effectBLL(position, color, offset);
        case CNR: return effectCNR(position, color, offset);
        case GRM: return effectGRM(position, color, offset);
        case CRD: return effectWLL(position, color, offset);
        case OLD: return effectOLD(position, color, offset);
        default:  return effectNON(position, color, offset);
    }
}


void main() {
    BEGIN;

    neg = 0.0;
//    neg = sin(osg_FrameTime * 0.5) * 0.5 + 0.5;

//    int index = gl_InstanceID * 2 + (int(osg_FrameTime * 15) % u_NumFrames) * u_InstanceCount * 2;
    int index = gl_InstanceID * 2;

    InstanceData dataFrom = fetchInstanceData(u_InstanceDataEFF, u_From * 10080 * 2 + index);
    InstanceData dataTo = fetchInstanceData(u_InstanceDataEFF, u_To * 10080 * 2 + index);

    EffectResult effectFrom = getEffect(u_From, vertPosition.xyz, dataFrom.color, dataFrom.offset);
    EffectResult effectTo = getEffect(u_To, vertPosition.xyz, dataTo.color, dataTo.offset);

//    float u_Trans = sin(osg_FrameTime) * 0.5 + 0.5;

    vertPosition.xyz = mix(effectFrom.position, effectTo.position, u_Trans);

    vertColor = mix(effectFrom.color, effectTo.color, u_Trans);

    float fly = 1.0 + (cos((u_Trans - 0.5) * TWO_PI) + 1.0) * 0.6;

    vec3 angle = mix(effectFrom.angle, effectTo.angle, u_Trans);

    angle += vec3(0.9, 1.0, 1.1) * (fly - 1.0) * u_Angle;

    vertPosition.xyz = rot(vertPosition.xyz, angle);

    vec3 vertOffset = mix(effectFrom.offset, effectTo.offset, u_Trans) * fly;
    vertPosition.xyz += vertOffset;

    END_TRIANGLE;

    mat3 rotationMatrix = mat3(
        rot(vec3(1.0, 0.0, 0.0), angle),  // Nowa X-owa baza
        rot(vec3(0.0, 1.0, 0.0), angle),  // Nowa Y-owa baza
        rot(vec3(0.0, 0.0, 1.0), angle)   // Nowa Z-owa baza
    );

    vec3 transformedNormal = normalize(rotationMatrix * p3d_Normal);
    vec3 transformedTangent = normalize(rotationMatrix * p3d_Tangent);
    vertNormal = normalize(p3d_NormalMatrix * transformedNormal);
    tangent = normalize(p3d_NormalMatrix * transformedTangent);
    binormal = cross(vertNormal, tangent);

}
