#include "swap.h"

#include "common/io.h"
#include "common/mallocHelper.h"

#include <inttypes.h>

static const char* detectByProcMeminfo(FFlist* result) {
    // For Android
    // Ref: #620
    char buf[PROC_FILE_BUFFSIZ];
    ssize_t nRead = ffReadFileData("/proc/meminfo", ARRAY_SIZE(buf) - 1, buf);
    if (nRead < 0) {
        return "ffReadFileData(\"/proc/meminfo\", ARRAY_SIZE(buf)-1, buf)";
    }
    buf[nRead] = '\0';

    uint64_t swapTotal = 0, swapFree = 0;

    char* token = NULL;
    if ((token = strstr(buf, "SwapTotal:")) != NULL) {
        swapTotal = strtoul(token + strlen("SwapTotal:"), NULL, 10);
    }

    if ((token = strstr(buf, "SwapFree:")) != NULL) {
        swapFree = strtoul(token + strlen("SwapFree:"), NULL, 10);
    }

    FFSwapResult* swap = FF_LIST_ADD(FFSwapResult, *result);
    ffStrbufInitStatic(&swap->name, "Total");
    swap->bytesTotal = swapTotal * 1024lu;
    swap->bytesUsed = (swapTotal - swapFree) * 1024lu;

    return NULL;
}

static const char* detectByProcSwaps(FFlist* result) {
    // Ref: #620
    char buf[PROC_FILE_BUFFSIZ];
    ssize_t nRead = ffReadFileData("/proc/swaps", ARRAY_SIZE(buf) - 1, buf);
    if (nRead <= 0) {
        return "ffReadFileData(\"/proc/swaps\", ARRAY_SIZE(buf)-1, buf) failed";
    }
    buf[nRead] = '\0';

    // Skip header
    char* line = memchr(buf, '\n', (size_t) nRead);

    while (line && *++line) {
        uint64_t total, used;
        char name[256];
        if (sscanf(line, "%255s %*[^\t]%" SCNu64 "%" SCNu64, name, &total, &used) != 3) {
            return "Invalid /proc/swaps format found";
        }

        uint32_t nameLen = (uint32_t) strnlen(name, sizeof(name));
        FFSwapResult* swap = FF_LIST_ADD(FFSwapResult, *result);
        ffStrbufInitA(&swap->name, nameLen);
        for (size_t i = 0; i < nameLen; ++i) {
            if (name[i] == '\\') {
                char octal[4] = { name[i + 1], name[i + 2], name[i + 3], '\0' };
                ffStrbufAppendC(&swap->name, (char) strtol(octal, NULL, 8));
                i += 3;
            } else {
                ffStrbufAppendC(&swap->name, name[i]);
            }
        }
        swap->bytesTotal = total * 1024u;
        swap->bytesUsed = used * 1024u;

        line = memchr(line, '\n', (size_t) (nRead - (line - buf)));
    }

    return NULL;
}

const char* ffDetectSwap(FFlist* result) {
    if (detectByProcSwaps(result) == NULL) {
        return NULL;
    }
    return detectByProcMeminfo(result);
}
