/*
 * DWN - Desktop Window Manager
 * retoor <retoor@molodetz.nl>
 * XDG Autostart support
 */

#include "autostart.h"
#include "dwn.h"
#include "config.h"
#include "util.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/stat.h>
#include <limits.h>
#include <errno.h>

static void scan_xdg_directory(const char *dir_path);
static void scan_symlink_directory(const char *dir_path);
static int parse_desktop_file(const char *path, char *exec_cmd, size_t exec_size);
static void strip_exec_field_codes(char *exec);

void autostart_init(void)
{
    if (dwn == NULL || dwn->config == NULL) {
        return;
    }

    if (!dwn->config->autostart_enabled) {
        LOG_DEBUG("Autostart disabled in config");
        return;
    }

    if (dwn->config->autostart_path[0] != '\0') {
        struct stat st;
        if (stat(dwn->config->autostart_path, &st) != 0) {
            if (mkdir(dwn->config->autostart_path, 0755) == 0) {
                LOG_INFO("Created autostart directory: %s", dwn->config->autostart_path);
            }
        }
    }

    LOG_INFO("Autostart initialized");
}

void autostart_run(void)
{
    if (dwn == NULL || dwn->config == NULL) {
        return;
    }

    if (!dwn->config->autostart_enabled) {
        return;
    }

    LOG_INFO("Running autostart applications");

    if (dwn->config->autostart_xdg) {
        scan_xdg_directory("/etc/xdg/autostart");

        char *user_autostart = expand_path("~/.config/autostart");
        if (user_autostart != NULL) {
            scan_xdg_directory(user_autostart);
            free(user_autostart);
        }
    }

    if (dwn->config->autostart_path[0] != '\0') {
        scan_symlink_directory(dwn->config->autostart_path);
    }

    LOG_INFO("Autostart complete");
}

void autostart_cleanup(void)
{
    LOG_DEBUG("Autostart cleanup");
}

static void scan_xdg_directory(const char *dir_path)
{
    if (dir_path == NULL) {
        return;
    }

    DIR *dir = opendir(dir_path);
    if (dir == NULL) {
        LOG_DEBUG("Cannot open autostart directory: %s", dir_path);
        return;
    }

    LOG_DEBUG("Scanning XDG autostart directory: %s", dir_path);

    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL) {
        if (entry->d_name[0] == '.') {
            continue;
        }

        if (!str_ends_with(entry->d_name, ".desktop")) {
            continue;
        }

        char full_path[PATH_MAX];
        int ret = snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, entry->d_name);
        if (ret < 0 || (size_t)ret >= sizeof(full_path)) {
            continue;
        }

        char exec_cmd[PATH_MAX];
        if (parse_desktop_file(full_path, exec_cmd, sizeof(exec_cmd)) == 0) {
            LOG_INFO("Autostart: %s -> %s", entry->d_name, exec_cmd);
            spawn_async(exec_cmd);
        }
    }

    closedir(dir);
}

static void scan_symlink_directory(const char *dir_path)
{
    if (dir_path == NULL) {
        return;
    }

    DIR *dir = opendir(dir_path);
    if (dir == NULL) {
        LOG_DEBUG("Cannot open symlink directory: %s", dir_path);
        return;
    }

    LOG_DEBUG("Scanning symlink directory: %s", dir_path);

    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL) {
        if (entry->d_name[0] == '.') {
            continue;
        }

        char full_path[PATH_MAX];
        int ret = snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, entry->d_name);
        if (ret < 0 || (size_t)ret >= sizeof(full_path)) {
            continue;
        }

        if (access(full_path, X_OK) != 0) {
            LOG_DEBUG("Skipping non-executable: %s", entry->d_name);
            continue;
        }

        LOG_INFO("Autostart (symlink): %s", entry->d_name);
        spawn_async(full_path);
    }

    closedir(dir);
}

static int parse_desktop_file(const char *path, char *exec_cmd, size_t exec_size)
{
    if (path == NULL || exec_cmd == NULL || exec_size == 0) {
        return -1;
    }

    FILE *f = fopen(path, "r");
    if (f == NULL) {
        return -1;
    }

    char line[1024];
    int in_desktop_entry = 0;
    int hidden = 0;
    int enabled = 1;
    char try_exec[256] = {0};
    exec_cmd[0] = '\0';

    while (fgets(line, sizeof(line), f) != NULL) {
        size_t len = strlen(line);
        while (len > 0 && (line[len-1] == '\n' || line[len-1] == '\r')) {
            line[--len] = '\0';
        }

        char *trimmed = str_trim(line);
        if (trimmed[0] == '\0' || trimmed[0] == '#') {
            continue;
        }

        if (strcmp(trimmed, "[Desktop Entry]") == 0) {
            in_desktop_entry = 1;
            continue;
        }

        if (trimmed[0] == '[') {
            in_desktop_entry = 0;
            continue;
        }

        if (!in_desktop_entry) {
            continue;
        }

        if (strcmp(trimmed, "Hidden=true") == 0) {
            hidden = 1;
        } else if (strcmp(trimmed, "X-GNOME-Autostart-enabled=false") == 0) {
            enabled = 0;
        } else if (strncmp(trimmed, "TryExec=", 8) == 0) {
            strncpy(try_exec, trimmed + 8, sizeof(try_exec) - 1);
            try_exec[sizeof(try_exec) - 1] = '\0';
        } else if (strncmp(trimmed, "Exec=", 5) == 0) {
            strncpy(exec_cmd, trimmed + 5, exec_size - 1);
            exec_cmd[exec_size - 1] = '\0';
        } else if (strncmp(trimmed, "NotShowIn=", 10) == 0) {
            if (strstr(trimmed + 10, "DWN") != NULL) {
                hidden = 1;
            }
        } else if (strncmp(trimmed, "OnlyShowIn=", 11) == 0) {
            if (strstr(trimmed + 11, "DWN") == NULL) {
                hidden = 1;
            }
        }
    }

    fclose(f);

    if (hidden || !enabled || exec_cmd[0] == '\0') {
        exec_cmd[0] = '\0';
        return -1;
    }

    if (try_exec[0] != '\0') {
        char *space = strchr(try_exec, ' ');
        if (space != NULL) {
            *space = '\0';
        }

        if (access(try_exec, X_OK) != 0) {
            char which_cmd[PATH_MAX];
            snprintf(which_cmd, sizeof(which_cmd), "which %s >/dev/null 2>&1", try_exec);
            if (system(which_cmd) != 0) {
                exec_cmd[0] = '\0';
                return -1;
            }
        }
    }

    strip_exec_field_codes(exec_cmd);

    return 0;
}

static void strip_exec_field_codes(char *exec)
{
    if (exec == NULL) {
        return;
    }

    char result[PATH_MAX];
    size_t j = 0;

    for (size_t i = 0; exec[i] != '\0' && j < sizeof(result) - 1; i++) {
        if (exec[i] == '%' && exec[i+1] != '\0') {
            char code = exec[i+1];
            if (code == 'f' || code == 'F' || code == 'u' || code == 'U' ||
                code == 'd' || code == 'D' || code == 'n' || code == 'N' ||
                code == 'i' || code == 'c' || code == 'k' || code == 'v' ||
                code == 'm') {
                i++;
                if (exec[i+1] == ' ') {
                    i++;
                }
                continue;
            }
        }
        result[j++] = exec[i];
    }
    result[j] = '\0';

    while (j > 0 && (result[j-1] == ' ' || result[j-1] == '\t')) {
        result[--j] = '\0';
    }

    size_t result_len = strlen(result);
    if (result_len >= 512) {
        result_len = 511;
    }
    memcpy(exec, result, result_len);
    exec[result_len] = '\0';
}
