// journal.c: simula una actualización con y sin journaling, y un fallo
// (crash) después de cada escritura al disco.
//
// La actualización es la de OSTEP cap. 42: agregar un bloque a un archivo.
// Cambian tres bloques: el inodo I, el bitmap de datos B y el bloque Db.
//
//   journal none      escribir I, B y Db en su lugar, sin protección
//   journal data      journaling de datos: TxB I B Db | TxE | checkpoint
//   journal ordered   journaling de metadatos: Db | TxB I B | TxE | checkpoint
//   journal reorder   como data, pero el disco reordena y TxE llega primero

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

enum { I, B, DB, TXB, JI, JB, JDB, TXE, NBLK };
static const char *name[NBLK] = { "I", "B", "Db", "TxB", "j:I", "j:B", "j:Db", "TxE" };

// 0 = viejo (o TxB/TxE ausentes), 1 = nuevo, 2 = basura de una transacción anterior
struct disk { int v[NBLK]; };

// Recuperación: si el journal tiene TxB y TxE, se reproduce (replay).
// Sin checksum, no se puede saber si los bloques del medio llegaron.
static void recover(struct disk *d, int ordered) {
    if (d->v[TXB] && d->v[TXE]) {
        d->v[I] = d->v[JI];
        d->v[B] = d->v[JB];
        if (!ordered) d->v[DB] = d->v[JDB];
    }
    d->v[TXB] = d->v[TXE] = 0;                   // vaciar el journal
}

static const char *verdict(const struct disk *d) {
    if (d->v[I] == 2 || d->v[B] == 2 || d->v[DB] == 2)
        return "CORRUPTO: replay de basura";
    if (d->v[I] != d->v[B]) return "INCONSISTENTE: I y B no coinciden";
    if (d->v[I] == 0) return "ok: estado viejo";
    if (d->v[DB] == 0) return "metadatos ok, Db es basura";
    return "ok: estado nuevo";
}

static void run(const char *mode, const int *order, int n, int ordered) {
    int w = 18;                                  // ancho de la primera columna
    int len = 0;
    for (int i = 0; i < n; i++) len += strlen(name[order[i]]) + 1;
    if (len > w) w = len;
    printf("== %s ==\n", mode);
    printf("%-*s %s\n", w, "en disco al fallar", "tras recuperar");
    for (int k = 0; k <= n; k++) {               // k escrituras llegaron al disco
        struct disk d = {{ 0, 0, 0, 0, 2, 2, 2, 0 }};
        char done[64] = "";
        for (int i = 0; i < k; i++) {
            d.v[order[i]] = 1;
            strcat(done, name[order[i]]);
            strcat(done, " ");
        }
        if (k == 0) strcpy(done, "(nada)");
        recover(&d, ordered);
        printf("%-*s %s\n", w, done, verdict(&d));
    }
}

int main(int argc, char *argv[]) {
    const char *m = argc > 1 ? argv[1] : "data";
    if (strcmp(m, "none") == 0) {
        int o[] = { DB, I, B };
        run("sin journal: Db, I, B", o, 3, 0);
    } else if (strcmp(m, "data") == 0) {
        int o[] = { TXB, JI, JB, JDB, TXE, I, B, DB };
        run("journaling de datos", o, 8, 0);
    } else if (strcmp(m, "ordered") == 0) {
        int o[] = { DB, TXB, JI, JB, TXE, I, B };
        run("journaling de metadatos (ordered)", o, 7, 1);
    } else if (strcmp(m, "reorder") == 0) {
        int o[] = { TXB, TXE, JI, JB, JDB };      // el disco reordenó TxE
        run("sin barrera: TxE llega antes", o, 5, 0);
    } else {
        fprintf(stderr, "uso: %s none|data|ordered|reorder\n", argv[0]);
        return 1;
    }
    return 0;
}
