#include "grade_book.h"

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

#define INITIAL_CAPACITY 4
#define MAX_GRADE 20

struct entry {
    char *name;
    int grade;
};

struct grade_book {
    struct entry *entries;
    int size;
    int capacity;
};

// Devuelve una copia de text en un bloque nuevo, o NULL si no hay memoria.
static char *copy_text(const char *text) {
    char *copy = malloc(strlen(text) + 1);
    if (copy != NULL) {
        strcpy(copy, text);
    }
    return copy;
}

static int is_valid_grade(int grade) {
    return grade >= 0 && grade <= MAX_GRADE;
}

// Devuelve la posición de name, o -1 si no está.
static int find_index(const GradeBook *book, const char *name) {
    for (int i = 0; i < book->size; i++) {
        if (strcmp(book->entries[i].name, name) == 0) {
            return i;
        }
    }
    return -1;
}

// Asegura un lugar libre. Devuelve 0 si no hay memoria.
static int make_room(GradeBook *book) {
    if (book->size < book->capacity) {
        return 1;
    }
    int capacity = 2 * book->capacity;
    struct entry *entries = realloc(book->entries, capacity * sizeof *entries);
    if (entries == NULL) {
        return 0;
    }
    book->entries = entries;
    book->capacity = capacity;
    return 1;
}

GradeBook *grade_book_new(void) {
    GradeBook *book = malloc(sizeof *book);
    if (book == NULL) {
        return NULL;
    }
    book->entries = malloc(INITIAL_CAPACITY * sizeof *book->entries);
    if (book->entries == NULL) {
        free(book);
        return NULL;
    }
    book->size = 0;
    book->capacity = INITIAL_CAPACITY;
    return book;
}

void grade_book_free(GradeBook *book) {
    if (book == NULL) {
        return;
    }
    for (int i = 0; i < book->size; i++) {
        free(book->entries[i].name);
    }
    free(book->entries);
    free(book);
}

int grade_book_add(GradeBook *book, const char *name, int grade) {
    if (name[0] == '\0' || !is_valid_grade(grade) || find_index(book, name) >= 0) {
        return 0;
    }
    if (!make_room(book)) {
        return 0;
    }
    char *copy = copy_text(name);
    if (copy == NULL) {
        return 0;
    }
    book->entries[book->size].name = copy;
    book->entries[book->size].grade = grade;
    book->size++;
    return 1;
}

int grade_book_set(GradeBook *book, const char *name, int grade) {
    int index = find_index(book, name);
    if (index < 0 || !is_valid_grade(grade)) {
        return 0;
    }
    book->entries[index].grade = grade;
    return 1;
}

int grade_book_get(const GradeBook *book, const char *name) {
    int index = find_index(book, name);
    return index < 0 ? -1 : book->entries[index].grade;
}

int grade_book_remove(GradeBook *book, const char *name) {
    int index = find_index(book, name);
    if (index < 0) {
        return 0;
    }
    free(book->entries[index].name);
    for (int i = index; i < book->size - 1; i++) {
        book->entries[i] = book->entries[i + 1];
    }
    book->size--;
    return 1;
}

int grade_book_size(const GradeBook *book) {
    return book->size;
}

const char *grade_book_name_at(const GradeBook *book, int index) {
    if (index < 0 || index >= book->size) {
        return NULL;
    }
    return book->entries[index].name;
}
