// bmapcalc.c: el formato del disco de xv6-riscv y qué bloque guarda un byte.
//
// Las constantes son las de kernel/fs.h, kernel/param.h y mkfs/mkfs.c.
//   bmapcalc                 muestra el formato del disco
//   bmapcalc off [off...]    para cada offset: bloque lógico y dónde está
//   bmapcalc -i inum         en qué bloque y byte está el inodo inum
//   bmapcalc -max bs ptr nd  tamaño máximo con nd directos y un indirecto

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

// kernel/fs.h
#define BSIZE     1024
#define NDIRECT   12
#define NINDIRECT (BSIZE / sizeof(unsigned int))
#define MAXFILE   (NDIRECT + NINDIRECT)
#define DINODE    64                     // sizeof(struct dinode)
#define IPB       (BSIZE / DINODE)
#define BPB       (BSIZE * 8)
// kernel/param.h y mkfs/mkfs.c
#define FSSIZE    2000
#define LOGBLOCKS 30
#define NINODES   200

static int nlog, ninodeblocks, nbitmap, nmeta;
static int logstart, inodestart, bmapstart;

static void layout(void) {
    // Mismas fórmulas que mkfs/mkfs.c
    nbitmap = FSSIZE / BPB + 1;
    ninodeblocks = NINODES / IPB + 1;
    nlog = LOGBLOCKS + 1;                 // cabecera + bloques del log
    nmeta = 2 + nlog + ninodeblocks + nbitmap;
    logstart = 2;
    inodestart = 2 + nlog;
    bmapstart = 2 + nlog + ninodeblocks;
}

static void print_layout(void) {
    printf("xv6: %d bloques de %d B\n", FSSIZE, BSIZE);
    printf("  %4d          arranque (boot)\n", 0);
    printf("  %4d          superbloque\n", 1);
    printf("  %4d - %4d   log (cabecera + %d bloques)\n", logstart,
           logstart + nlog - 1, LOGBLOCKS);
    printf("  %4d - %4d   inodos (%d por bloque)\n", inodestart,
           inodestart + ninodeblocks - 1, IPB);
    printf("  %4d          bitmap (%d bits por bloque)\n", bmapstart, BPB);
    printf("  %4d - %4d   datos (%d bloques)\n", nmeta, FSSIZE - 1, FSSIZE - nmeta);
    printf("archivo máximo: %d + %d = %d bloques = %d KB\n", NDIRECT,
           (int)NINDIRECT, (int)MAXFILE, (int)MAXFILE * BSIZE / 1024);
}

static void where(unsigned long off) {
    unsigned long bn = off / BSIZE;
    printf("offset %lu: bloque lógico %lu, byte %lu dentro del bloque\n",
           off, bn, off % BSIZE);
    if (bn < NDIRECT) {
        printf("  directo: addrs[%lu]; lecturas = inodo + datos = 2\n", bn);
    } else if (bn < MAXFILE) {
        unsigned long k = bn - NDIRECT;
        printf("  indirecto: addrs[%d] -> entrada %lu (bytes %lu-%lu del bloque)\n",
               NDIRECT, k, k * 4, k * 4 + 3);
        printf("  lecturas = inodo + indirecto + datos = 3\n");
    } else {
        printf("  fuera de rango: MAXFILE = %d bloques (esto es P5)\n", (int)MAXFILE);
    }
}

int main(int argc, char *argv[]) {
    layout();
    if (argc == 1) {
        print_layout();
        return 0;
    }
    if (strcmp(argv[1], "-i") == 0 && argc == 3) {
        int inum = atoi(argv[2]);
        // IBLOCK(i, sb) de kernel/fs.h
        printf("inodo %d: bloque %d, byte %d\n", inum, inum / IPB + inodestart,
               (inum % IPB) * DINODE);
        return 0;
    }
    if (strcmp(argv[1], "-max") == 0 && argc == 5) {
        long bs = atol(argv[2]), ptr = atol(argv[3]), nd = atol(argv[4]);
        long nptr = bs / ptr;
        long blocks = nd + nptr;
        printf("%ld punteros por bloque\n", nptr);
        printf("%ld + %ld = %ld bloques = %ld KB\n", nd, nptr, blocks, blocks * bs / 1024);
        return 0;
    }
    for (int i = 1; i < argc; i++)
        where(strtoul(argv[i], NULL, 0));
    return 0;
}
