#include <raylib.h>
#include <rlgl.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <string.h>

#define WIDTH 1920
#define HEIGHT 1080

char *hyprland_socket_path;

Vector2 hyprland_get_cursor_position()
{
    Vector2 pos = {0};

    int sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock_fd == -1)
    {
        perror("socket");
        return pos;
    }

    struct sockaddr_un addr = {0};
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, hyprland_socket_path, sizeof(addr.sun_path) - 1);

    if (connect(sock_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
    {
        printf("Using hyprland socket: %s\n", hyprland_socket_path);
        perror("connect");
        close(sock_fd);
        return pos;
    }

    const char *message = "/cursorpos";
    if (send(sock_fd, message, strlen(message), 0) == -1)
    {
        perror("send");
        close(sock_fd);
        return pos;
    }

    char buffer[256] = {0};
    ssize_t bytes_received = recv(sock_fd, buffer, sizeof(buffer) - 1, 0);
    if (bytes_received == -1)
    {
        perror("recv");
        close(sock_fd);
        return pos;
    }

    // format is x, y so parse here
    buffer[bytes_received] = '\0';

    if (sscanf(buffer, "%f, %f", &pos.x, &pos.y) != 2)
    {
        fprintf(stderr, "Failed to parse cursor position\n");
    }

    close(sock_fd);
    return pos;
}

Texture2D load_texture_from_file(const char *file_path)
{
    Texture2D texture = {0};

    if (FileExists(file_path))
    {
        texture = LoadTexture(file_path);
    }
    else
    {
        const char *system_path = TextFormat("/usr/share/wallpaper/%s", file_path);
        if (FileExists(system_path))
        {
            texture = LoadTexture(system_path);
        }
    }

    if (texture.id == 0)
    {
        TraceLog(LOG_ERROR, "Failed to load texture from file: %s", file_path);
    }
    return texture;
}

Shader load_shader_from_file(const char *vs_path, const char *fs_path)
{
    Shader shader = {0};

    if (FileExists(vs_path) && FileExists(fs_path))
    {
        shader = LoadShader(vs_path, fs_path);
    }
    else
    {
        const char *system_vs_path = TextFormat("/usr/share/wallpaper/%s", vs_path);
        const char *system_fs_path = TextFormat("/usr/share/wallpaper/%s", fs_path);
        if (FileExists(system_vs_path) && FileExists(system_fs_path))
        {
            shader = LoadShader(system_vs_path, system_fs_path);
        }
    }

    if (shader.id == 0)
    {
        TraceLog(LOG_ERROR, "Failed to load shader from files: %s, %s", vs_path, fs_path);
    }
    return shader;
}

int main(void)
{
    InitWindow(WIDTH, HEIGHT, "pihkaal-wallpaper");
    SetWindowState(FLAG_WINDOW_RESIZABLE);
    SetTargetFPS(60);

    // initialize hyprland socket path
    hyprland_socket_path = strdup(TextFormat("%s/hypr/%s/.socket.sock", getenv("XDG_RUNTIME_DIR"), getenv("HYPRLAND_INSTANCE_SIGNATURE")));

    // initialize textures
    Texture2D distanceFieldTex = load_texture_from_file("resources/textures/background_distance_field.png");
    Texture2D backgroundTex = load_texture_from_file("resources/textures/background_transparent.png");
    RenderTexture2D target = LoadRenderTexture(WIDTH, HEIGHT);

    // initialize shader
    Shader shader = load_shader_from_file("resources/shaders/basic.vs", "resources/shaders/distance_field.fs");

    int resolutionLoc = GetShaderLocation(shader, "resolution");
    int mousePosLoc = GetShaderLocation(shader, "mousePos");
    int ballRadiusLoc = GetShaderLocation(shader, "ballRadius");
    int shapeInfluenceLoc = GetShaderLocation(shader, "shapeInfluence");
    int distanceFieldTexLoc = GetShaderLocation(shader, "distanceFieldTex");

    Vector2 resolution = {WIDTH, HEIGHT};
    SetShaderValue(shader, resolutionLoc, &resolution, SHADER_UNIFORM_VEC2);

    float ballRadius = 100.0f;
    SetShaderValue(shader, ballRadiusLoc, &ballRadius, SHADER_UNIFORM_FLOAT);

    float shapeInfluence = 200.0f;
    SetShaderValue(shader, shapeInfluenceLoc, &shapeInfluence, SHADER_UNIFORM_FLOAT);

    int textureUnit = 1;
    SetShaderValue(shader, distanceFieldTexLoc, &textureUnit, SHADER_UNIFORM_INT);

    while (!WindowShouldClose())
    {
        int screenWidth = GetScreenWidth();
        int screenHeight = GetScreenHeight();

        if (IsWindowResized())
        {
            UnloadRenderTexture(target);
            target = LoadRenderTexture(screenWidth, screenHeight);

            Vector2 resolution = {screenWidth, screenHeight};
            SetShaderValue(shader, resolutionLoc, &resolution, SHADER_UNIFORM_VEC2);
        }

        Vector2 hyprCursor = hyprland_get_cursor_position();
        Vector2 mousePos = {hyprCursor.x, (float)screenHeight - hyprCursor.y};
        SetShaderValue(shader, mousePosLoc, &mousePos, SHADER_UNIFORM_VEC2);

        BeginDrawing();

        rlActiveTextureSlot(1);
        rlEnableTexture(distanceFieldTex.id);
        rlActiveTextureSlot(0);

        BeginShaderMode(shader);
        DrawTextureRec(target.texture, (Rectangle){0, 0, screenWidth, -screenHeight}, (Vector2){0, 0}, WHITE);
        EndShaderMode();

        DrawTextureRec(backgroundTex, (Rectangle){0, 0, screenWidth, screenHeight}, (Vector2){0, 0}, WHITE);

        EndDrawing();
    }

    UnloadRenderTexture(target);
    UnloadTexture(distanceFieldTex);
    UnloadTexture(backgroundTex);
    UnloadShader(shader);
    CloseWindow();

    free(hyprland_socket_path);

    return 0;
}