/* file_processor.c - Implementasi pemrosesan file */
#include "file_processor.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

FILE *file_open(const char *path, const char *mode) {
    if (path == NULL) {
        if (strcmp(mode, "r") == 0) {
            return stdin;
        }
        if (strcmp(mode, "w") == 0) {
            return stdout;
        }
        return NULL;
    }
    return fopen(path, mode);
}

static void close_file(FILE *fp, const char *path) {
    if (path != NULL && fp != NULL) {
        fclose(fp);
    }
    (void)fp;
}

/* Helper: duplikasi string (pengganti strdup agar portable C11 murni). */
static char *dup_str(const char *s) {
    size_t len = strlen(s) + 1;
    char *copy = malloc(len);
    if (copy != NULL) {
        memcpy(copy, s, len);
    }
    return copy;
}

/* Helper: cek apakah string `s` mengandung substring `pattern`
 * secara case-insensitive. */
static int contains_ci(const char *s, const char *pattern) {
    if (pattern[0] == '\0') {
        return 1;
    }
    for (const char *p = s; *p != '\0'; p++) {
        const char *si = p;
        const char *pi = pattern;
        while (*si != '\0' && *pi != '\0' &&
               tolower((unsigned char)*si) == tolower((unsigned char)*pi)) {
            si++;
            pi++;
        }
        if (*pi == '\0') {
            return 1;
        }
    }
    return 0;
}

int file_stats_compute(const char *path, file_stats *out) {
    if (out == NULL) {
        return -1;
    }
    out->lines = 0;
    out->words = 0;
    out->chars = 0;
    FILE *fp = file_open(path, "r");
    if (fp == NULL) {
        return -1;
    }
    int prev_was_space = 1;
    int c;
    while ((c = fgetc(fp)) != EOF) {
        out->chars++;
        if (c == '\n') {
            out->lines++;
            prev_was_space = 1;
        } else if (isspace(c)) {
            prev_was_space = 1;
        } else {
            if (prev_was_space) {
                out->words++;
            }
            prev_was_space = 0;
        }
    }
    /* Hitung baris terakhir bila tidak diakhiri newline. */
    if (out->chars > 0 && !prev_was_space && out->lines == 0) {
        out->lines = 1;
    }
    close_file(fp, path);
    return 0;
}

int file_head(const char *path, int n) {
    if (n < 0) {
        return -1;
    }
    FILE *fp = file_open(path, "r");
    if (fp == NULL) {
        return -1;
    }
    int printed = 0;
    char buf[4096];
    while (printed < n && fgets(buf, sizeof(buf), fp) != NULL) {
        fputs(buf, stdout);
        printed++;
    }
    close_file(fp, path);
    return printed;
}

int file_tail(const char *path, int n) {
    if (n <= 0) {
        return 0;
    }
    FILE *fp = file_open(path, "r");
    if (fp == NULL) {
        return -1;
    }
    /* Alokasikan buffer sirkular untuk N baris terakhir. */
    char **ring = calloc((size_t)n, sizeof(char *));
    if (ring == NULL) {
        close_file(fp, path);
        return -1;
    }
    int idx = 0;
    int filled = 0;
    char buf[4096];
    while (fgets(buf, sizeof(buf), fp) != NULL) {
        if (ring[idx] != NULL) {
            free(ring[idx]);
        }
        ring[idx] = dup_str(buf);
        idx = (idx + 1) % n;
        if (filled < n) {
            filled++;
        }
    }
    /* Cetak sesuai urutan kronologis. */
    int start = (filled < n) ? 0 : idx;
    for (int k = 0; k < filled; k++) {
        int pos = (start + k) % n;
        if (ring[pos] != NULL) {
            fputs(ring[pos], stdout);
            free(ring[pos]);
        }
    }
    free(ring);
    close_file(fp, path);
    return filled;
}

int file_grep(const char *path, const char *pattern, int ignore_case) {
    if (pattern == NULL) {
        return -1;
    }
    FILE *fp = file_open(path, "r");
    if (fp == NULL) {
        return -1;
    }
    int printed = 0;
    char buf[4096];
    while (fgets(buf, sizeof(buf), fp) != NULL) {
        int match;
        if (ignore_case) {
            match = contains_ci(buf, pattern);
        } else {
            match = (strstr(buf, pattern) != NULL);
        }
        if (match) {
            fputs(buf, stdout);
            printed++;
        }
    }
    close_file(fp, path);
    return printed;
}
