#include "table.h"

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

#define LINE_SIZE 256
#define NAME_SIZE 32
#define FIRST_CAPACITY 4

struct table {
    int columns;
    char names[TABLE_MAX_COLUMNS][NAME_SIZE];
    int rows;
    int capacity;  // cuántas filas caben en cells
    Value *cells;  // rows * columns celdas, fila por fila; la tabla es owner de ellas
};

// --- Hecho: lean estas funciones, no hace falta cambiarlas. ---

// Parte la línea en campos, separados por comas. Cambia cada coma por un '\0', y deja en
// fields[i] la dirección de cada campo. Un campo puede quedar vacío.
// Devuelve cuántos campos hay, o -1 si son más que TABLE_MAX_COLUMNS.
static int split_line(char *line, char *fields[]) {
    int count = 0;
    fields[count] = line;
    count++;
    for (int i = 0; line[i] != '\0'; i++) {
        if (line[i] == ',') {
            if (count == TABLE_MAX_COLUMNS) {
                return -1;
            }
            line[i] = '\0';
            fields[count] = &line[i + 1];
            count++;
        }
    }
    return count;
}

// Quita el salto de línea final, con o sin '\r'. Devuelve 0 si la línea no entró completa.
static int strip_newline(char *line, FILE *file) {
    size_t length = strlen(line);
    if (length > 0 && line[length - 1] == '\n') {
        line[length - 1] = '\0';
        length--;
    } else if (length + 1 == LINE_SIZE && !feof(file)) {
        return 0;
    }
    if (length > 0 && line[length - 1] == '\r') {
        line[length - 1] = '\0';
    }
    return 1;
}

// Asegura que quepa una fila más. Devuelve 1 si pudo, y 0 si no hay memoria.
static int reserve_row(Table *t) {
    if (t->rows < t->capacity) {
        return 1;
    }
    int capacity = t->capacity == 0 ? FIRST_CAPACITY : t->capacity * 2;
    size_t bytes = (size_t) capacity * (size_t) t->columns * sizeof(Value);
    Value *cells = realloc(t->cells, bytes);
    if (cells == NULL) {
        return 0;
    }
    t->cells = cells;
    t->capacity = capacity;
    return 1;
}

static int read_header(Table *t, char *line) {
    char *fields[TABLE_MAX_COLUMNS];
    int count = split_line(line, fields);
    if (count < 0 || line[0] == '\0') {
        return 0;
    }
    for (int c = 0; c < count; c++) {
        if (strlen(fields[c]) >= NAME_SIZE) {
            return 0;
        }
        strcpy(t->names[c], fields[c]);
    }
    t->columns = count;
    return 1;
}

static int add_row(Table *t, char *line);

// Lee el resto del archivo, fila por fila. Se salta las líneas vacías.
static int read_rows(Table *t, FILE *file) {
    char line[LINE_SIZE];
    while (fgets(line, LINE_SIZE, file) != NULL) {
        if (!strip_newline(line, file)) {
            return 0;
        }
        if (line[0] != '\0' && !add_row(t, line)) {
            return 0;
        }
    }
    return 1;
}

static int read_table(Table *t, FILE *file) {
    char line[LINE_SIZE];
    if (fgets(line, LINE_SIZE, file) == NULL) {
        return 0;
    }
    return strip_newline(line, file) && read_header(t, line) && read_rows(t, file);
}

Table *table_load(const char *name) {
    FILE *file = fopen(name, "r");
    if (file == NULL) {
        return NULL;
    }

    Table *t = malloc(sizeof(Table));
    if (t == NULL) {
        fclose(file);
        return NULL;
    }
    t->columns = 0;
    t->rows = 0;
    t->capacity = 0;
    t->cells = NULL;

    int ok = read_table(t, file);
    fclose(file);
    if (!ok) {
        table_free(t);
        return NULL;
    }
    return t;
}

int table_rows(const Table *t) {
    return t->rows;
}

int table_columns(const Table *t) {
    return t->columns;
}

const char *table_column_name(const Table *t, int column) {
    return t->names[column];
}

const Value *table_cell(const Table *t, int row, int column) {
    return &t->cells[row * t->columns + column];
}

// --- Por escribir. ---

// Convierte una línea en una fila nueva. Devuelve 1 si pudo, y 0 si la fila no tiene tantos
// campos como columnas o si no hay memoria. Si devuelve 0, rows no cambió y no quedó nada a
// medias. Usen split_line, reserve_row y value_parse.
static int add_row(Table *t, char *line) {
    (void) t;
    (void) line;
    (void) split_line;
    (void) reserve_row;
    return 0;
}

void table_free(Table *t) {
    (void) t;
}

ColumnSummary table_summary(const Table *t, int column) {
    (void) t;
    (void) column;
    ColumnSummary summary = {0, 0, 0, 0, 0.0, 0.0, 0.0};
    return summary;
}
