// Notas de un curso: lee un archivo de notas y escribe un reporte y un resumen.
//
// El programa lee de la entrada el nombre del archivo:
//
//   printf 'notas.txt\n' | ../build/lab/grades
//
// Cada línea del archivo tiene una sección, un nombre de una palabra y cuatro notas:
//
//   A Ana 14 9 17 12
#include <stdio.h>

#define NAME_SIZE 20
#define N_GRADES 4
#define MAX_STUDENTS 50
#define MAX_GRADE 20
#define PASS_GRADE 11
#define PATH_SIZE 128
#define LINE_SIZE 64
#define READ_END 0
#define READ_OK 1
#define READ_TOO_LONG 2

struct student {
    char section;
    char name[NAME_SIZE];
    int grades[N_GRADES];
};

// Quita el salto de línea de s. Devuelve 1 si lo encontró y 0 si no.
int remove_newline(char s[]) {
    for (int i = 0; s[i] != '\0'; i++) {
        if (s[i] == '\n') {
            s[i] = '\0';
            return 1;
        }
    }
    return 0;
}

// Lee una línea sin su salto. Si no cabe, descarta el resto de la línea.
int read_line(FILE *file, char line[], int size) {
    if (fgets(line, size, file) == NULL) {
        return READ_END;
    }
    if (remove_newline(line)) {
        return READ_OK;
    }
    int c = fgetc(file);
    if (c == '\n' || c == EOF) {
        return READ_OK;
    }
    while (c != '\n' && c != EOF) {
        c = fgetc(file);
    }
    return READ_TOO_LONG;
}

// Devuelve 1 si todas las notas están entre 0 y MAX_GRADE.
int grades_in_range(struct student s) {
    for (int i = 0; i < N_GRADES - 1; i++) {
        if (s.grades[i] < 0 || s.grades[i] > MAX_GRADE) {
            return 0;
        }
    }
    return 1;
}

// Si la línea es válida, guarda el estudiante en list[pos] y devuelve 1.
int parse_student(const char line[], struct student list[], int pos) {
    struct student s;
    char extra[2];
    int fields = sscanf(line, " %c %19s %d %d %d %d %1s", &s.section, s.name, &s.grades[0],
                        &s.grades[1], &s.grades[2], &s.grades[3], extra);
    if (fields != 2 + N_GRADES) {
        return 0;
    }
    if (s.section < 'A' || s.section > 'Z' || !grades_in_range(s)) {
        return 0;
    }
    list[pos] = s;
    return 1;
}

// Lee el archivo en list. Devuelve cuántos estudiantes guardó, o -1 si no pudo abrirlo.
int read_students(const char path[], struct student list[], int capacity) {
    FILE *file = fopen(path, "r");
    if (file == NULL) {
        perror(path);
        return -1;
    }
    char line[LINE_SIZE];
    int count = 0;
    int number = 0;
    int result = read_line(file, line, LINE_SIZE);
    while (result != READ_END) {
        number++;
        if (count == capacity) {
            fprintf(stderr, "Demasiados estudiantes: solo se leen %d\n", capacity);
            break;
        }
        if (result == READ_OK && parse_student(line, list, count)) {
            count++;
        } else {
            fprintf(stderr, "Línea %d: mal formada, se omite\n", number);
        }
        result = read_line(file, line, LINE_SIZE);
    }
    fclose(file);
    return count;
}

int total_points(struct student s) {
    int total = 0;
    for (int i = 0; i < N_GRADES; i++) {
        total = total + s.grades[i];
    }
    return total;
}

double average(struct student s) {
    return (double) (total_points(s) / N_GRADES);
}

// Compara enteros para no depender de la exactitud de un double.
int is_passed(struct student s) {
    return total_points(s) >= PASS_GRADE * N_GRADES;
}

// Sesión 2: escríbanla según el README y borren estas dos líneas.
double average_without_lowest(struct student s) {
    (void) s;
    return -1.0;
}

int count_passed(const struct student list[], int n) {
    int count = 0;
    for (int i = 0; i < n; i++) {
        if (is_passed(list[i])) {
            count++;
        }
    }
    return count;
}

// Promedio de los promedios. Con n = 0 devuelve 0.
double class_average(const struct student list[], int n) {
    if (n <= 0) {
        return 0.0;
    }
    double total = 0.0;
    for (int i = 0; i < n; i++) {
        total = total + average(list[i]);
    }
    return total / n;
}

// Posición del mejor promedio, o -1 si no hay nadie. En un empate gana el primero.
int best_index(const struct student list[], int n) {
    if (n <= 0) {
        return -1;
    }
    int best = 0;
    for (int i = 1; i < n; i++) {
        if (average(list[i]) > average(list[best])) {
            best = i;
        }
    }
    return best;
}

void print_student(struct student s) {
    printf("%-3c %-12s", s.section, s.name);
    for (int i = 0; i < N_GRADES; i++) {
        printf(" %2d", s.grades[i]);
    }
    printf("  %5.2f  ", average(s));
    if (is_passed(s)) {
        printf("%-11s", "aprobado");
    } else {
        printf("%-11s", "desaprobado");
    }
    printf("\n");
}

void print_report(const struct student list[], int n) {
    printf("Sec Nombre       N1 N2 N3 N4   Prom  Estado\n");
    for (int i = 0; i < n; i++) {
        print_student(list[i]);
    }
}

void print_summary(const struct student list[], int n) {
    int best = best_index(list, n);
    printf("Estudiantes: %d\n", n);
    printf("Aprobados: %d\n", count_passed(list, n));
    printf("Promedio del curso: %.2f\n", class_average(list, n));
    printf("Mejor promedio: %s (%.2f)\n", list[best].name, average(list[best]));
}

int main(void) {
    char path[PATH_SIZE];
    if (read_line(stdin, path, PATH_SIZE) != READ_OK) {
        fprintf(stderr, "Falta el nombre del archivo\n");
        return 1;
    }
    struct student list[MAX_STUDENTS];
    int n = read_students(path, list, MAX_STUDENTS);
    if (n < 0) {
        return 1;
    }
    if (n == 0) {
        printf("No hay estudiantes\n");
        return 0;
    }
    print_report(list, n);
    print_summary(list, n);
    return 0;
}
