#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include "fs.h"
#include <arpa/inet.h>

int lread(int bpos, void *buf, int size);
int readino(int ino, struct dinode *di);
char *readfile(struct dinode di, int *size);
int tree(int ino, int level);

struct superblock sb;
int fsfd;

char *type[] = {"?", "DIR ", "FILE", "DEV "};

int main(int argc, char *argv[])
{
  if (argc < 2) {
    printf("Usage:disksb <image>\n");
    return 1;
  }

  fsfd = open(argv[1], O_RDWR);
  if(fsfd < 0){
    perror(argv[1]);
    exit(1);
  }

  if (lread(1 * BSIZE, &sb, sizeof(sb)) != sizeof(sb)) return 1;

  tree(1, 0);
  return 0;
}

int tree(int ino, int level)
{
  int size;
  struct dinode di;

  if (readino(ino, &di) < 0) return -1;
  char *dir = readfile(di, &size);

  struct dirent *de = (struct dirent *)dir;
  for(int dp=0; dp < size; dp+=sizeof(struct dirent), de++) {
    if (de->inum == 0) continue;
    if (strcmp(de->name, ".") == 0 || strcmp(de->name, "..") == 0) continue;

    if (readino(de->inum, &di) < 0) return -1;
    printf("%.*s%s %2d %5d %s\n", level, "                ", type[di.type], de->inum, di.size, de->name);
    if (di.type == 1) tree(de->inum, level+4);
  }
}

char *readfile(struct dinode di, int *size)
{
  char buf[BSIZE];
  uint indirect[NINDIRECT];
  int sz = di.size, rd = 0;
  int blks = sz/BSIZE, blk = 0, db;

  char *data = NULL;
  
  if (blks >= NDIRECT) lread(di.addrs[NDIRECT], indirect, BSIZE);

  for(blk = 0; sz > 0; blk++) {
    if (blk < NDIRECT) db = di.addrs[blk];
    else db = indirect[blk-NDIRECT];

    lread(db * BSIZE, buf, BSIZE);
    int mx = sz < BSIZE? sz: BSIZE;

    data = realloc(data, rd+mx);
    memcpy(data+rd, buf, mx);

    rd += mx;
    sz -= mx;
  }

//  printf("Read %d bytes\n", rd);
  *size = rd;
  return data;
}

int readino(int ino, struct dinode *di)
{
  if (lread((sb.inodestart * BSIZE) + (ino*sizeof(*di)), di, sizeof(*di)) != sizeof(*di))
    return -1;
  return 0;
}

int lread(int bpos, void *buf, int size)
{
  int n;
  if (lseek(fsfd, bpos, SEEK_SET) != bpos) {
    perror("lseek");
    return -1;
  }
  if ( (n = read(fsfd, buf, size)) < 0) {
    perror("read");
    return -1;
  }
//  printf("read %d = %d\n", size, n);
  return n;
}
