/*
 * tio - a serial device I/O tool
 *
 * Copyright (c) 2014-2024  Martin Lund
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

#define _GNU_SOURCE  // For statx()
#include "config.h"
#include <dirent.h>
#include <fcntl.h>
#include <regex.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <errno.h>
#include <poll.h>
#include <termios.h>
#include "error.h"
#include "print.h"
#include "options.h"

bool fs_dir_exists(const char *path)
{
    struct stat st;

    if (stat(path, &st) != 0)
    {
        return false;
    }
    else if (!S_ISDIR(st.st_mode))
    {
        return false;
    }

    return true;
}

// Function to read the content of a file but stripped of newline
ssize_t fs_read_file_stripped(char *buf, size_t bufsiz, const char *format, ...)
{
    char filename[PATH_MAX];
    int bytes_printed = 0;
    va_list args;

    va_start(args, format);
    bytes_printed = vsnprintf(filename, sizeof(filename), format, args);
    va_end(args);

    if (bytes_printed < 0)
    {
        return -1;
    }

    FILE *file = fopen(filename, "r");
    if (!file)
    {
        return -1;
    }
    ssize_t length = fread(buf, 1, bufsiz - 1, file);
    if (length == -1)
    {
        fclose(file);
        return -1;
    }

    // Strip any newline
    buf[strcspn(buf, "\n")] = 0;
    buf[length] = '\0'; // Make sure to null-terminate the string

    fclose(file);

    return length;
}

bool fs_file_exists(const char *format, ...)
{
    char filename[PATH_MAX];
    int bytes_printed = 0;
    struct stat st;
    va_list args;

    va_start(args, format);
    bytes_printed = vsnprintf(filename, sizeof(filename), format, args);
    va_end(args);

    if (bytes_printed < 0)
    {
        return false;
    }

    return stat(filename, &st) == 0;
}

char* fs_search_directory(const char *dir_path, const char *dirname)
{
    struct dirent *entry;
    char path[PATH_MAX];
    struct stat st;
    DIR *dir;

    if ((dir = opendir(dir_path)) == NULL)
    {
        // Error opening directory
        return NULL;
    }

    while ((entry = readdir(dir)) != NULL)
    {
        snprintf(path, PATH_MAX, "%s/%s", dir_path, entry->d_name);

        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
        {
            continue;
        }

        if (lstat(path, &st) == -1)
        {
            // Error getting directory status
            closedir(dir);
            return NULL;
        }

        if (S_ISLNK(st.st_mode))
        {
            // Skip symbolic links
            continue;
        }

        if (S_ISDIR(st.st_mode))
        {
            // If it's a directory, check if it's the one we're looking for
            if (strcmp(entry->d_name, dirname) == 0)
            {
                char *result = strndup(path, PATH_MAX);
                closedir(dir);
                return result;
            }
            else
            {
                // Recursively search within directories
                char* result = fs_search_directory(path, dirname);
                if (result != NULL)
                {
                    closedir(dir);
                    return result;
                }
            }
        }
    }

    closedir(dir);
    return NULL;
}

#if defined(__linux__) && defined(STATX_BTIME)

// Function to return creation time of file
double fs_get_creation_time(const char *path)
{
    struct statx stx;
    int fd = open(path, O_RDONLY);
    if (fd == -1)
    {
        return -1;
    }

    if (statx(fd, "", AT_EMPTY_PATH, STATX_ALL, &stx) != 0)
    {
        close(fd);
        return -1;
    }

    // Close the file
    close(fd);

    return stx.stx_btime.tv_sec + stx.stx_btime.tv_nsec / 1e9;
}

#elif defined(__APPLE__) || defined(__MACH__)

double fs_get_creation_time(const char *path)
{
    struct stat st;

    if (stat(path, &st) != 0)
    {
        return -1;
    }

    return st.st_mtimespec.tv_sec + st.st_mtimespec.tv_nsec / 1e9;
}

#else

double fs_get_creation_time(const char *path)
{
    struct stat st;

    if (stat(path, &st) != 0)
    {
        return -1;
    }

    return (double) st.st_ctime;
}

#endif
