
#include "tfs.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>


#define MAX_FD 256

struct fd {
    int fd;
    char *filename;
} fd_list[MAX_FD];

int verbose = 0;


char *prefix; 

void fd_close(struct fd *fd) {
    if (fd->filename) {
        if (verbose) fprintf(stderr, "C: %s\n", fd->filename);
        free(fd->filename);
        close(fd->fd);
        fd->filename = NULL;
        fd->fd = -1;
    }
}

void safe_read(int fd, void *buf, int bytes) {
    int attempt = 0;
    int received;
  again:
    received = read(fd, buf, bytes);
    if (received < 0) {
        fprintf(stderr, "can't read %d bytes: %s\n", bytes, strerror(errno));
        exit(1);
    }
    if (received < bytes) {
        bytes -= received;
        buf += received;
        goto again;
    }
}

char *prefix_filename(char *filename, int size) {
    char *full_name = malloc(size + strlen(prefix));
    strcpy(full_name, prefix);
    strcat(full_name, filename);
    return full_name;
}

int do_cmd(void) {
    struct tfs_cmd cmd;
    int nb_spaces = 60;
    char spaces[nb_spaces];
    safe_read(0, &cmd, sizeof(cmd));
    int payload_size = size_to_buffer(cmd.size) - sizeof(cmd);
    char payload[payload_size];
    if(payload_size) { safe_read(0, payload, payload_size); }
    struct fd *current_fd = &fd_list[cmd.fd];
    char *dirname;

    switch(cmd.cmd) {
    case TFS_MKDIR:
        dirname = prefix_filename(payload, payload_size);
        mkdir(dirname, 0777);
        free(dirname);
        break;
        
    case TFS_CLOSE:
        fd_close(current_fd);
        break;

    case TFS_OPEN:

        // possibly overwrite fd slot
        fd_close(current_fd);

        // create new slot
        current_fd->filename = prefix_filename(payload, payload_size);

        if (-1 == (current_fd->fd = open(current_fd->filename, O_CREAT|O_WRONLY, 0666))) {
            fprintf(stderr, "Can't create file %s: %s\n", current_fd->filename, strerror(errno));
            exit(1);
        }
        if (verbose) fprintf(stderr, "O: %s\n", current_fd->filename);


        break;

    case TFS_WRITE:

        if (!current_fd->filename) {
            fprintf(stderr, "Unknown file ID %d\n", cmd.fd);
            exit(1);
        }
        if (-1 == lseek(current_fd->fd, cmd.write_offset, SEEK_SET)) {
            fprintf(stderr, "Seek to offset %Lu failed for ID %d (%s): %s\n",
                    cmd.write_offset, cmd.fd, current_fd->filename, strerror(errno));
            exit(1);
        } 
        if (-1 == write(current_fd->fd, payload, cmd.write_size)) {
            fprintf(stderr, "Write of %d bytes at offset %Lu failed for fd %d (ID %d : %s): %s\n",
                    cmd.write_size, cmd.write_offset, 
                    current_fd->fd, cmd.fd, 
                    current_fd->filename, strerror(errno));
            exit(1);
        }
        if (verbose) fprintf(stderr, "W: %s %d\n", current_fd->filename, cmd.write_size);

        if (0) {
            memset(spaces, ' ', nb_spaces);
            memcpy(spaces, current_fd->filename, strlen(current_fd->filename));
            spaces[nb_spaces-1] = 0;
            fprintf(stderr, "%s\r", spaces);
        }
        break;

    default: 
        fprintf(stderr, "Invalid command %d\n", cmd.cmd);
        exit(1);
    }    
}

int main(int argc, char **argv) {
    if (argc != 2) {
        fprintf(stderr, "usage: %s <mirror-dir>\n", argv[0]);
	exit(1);
    }
    prefix = argv[1];
    memset(fd_list, 0, sizeof(fd_list));
    for (;;) do_cmd();
}

