#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "fuse.h"
#include "fuseactions.h"

// Main handler for Fuse
void Fuse(int argc, char* argv[])
{
    fuseArgStruct fuseArgs;
    FuseGetArgs(argc, argv, &fuseArgs);
    FuseGivenTest(&fuseArgs, argv[0]);
}

// New method for getting arguments
void FuseGetArgs(int argc, char* argv[], fuseArgStruct* fuseArgs)
{
    for (int i = 0; i < argc; i++)
    {
        if (argv[i][0] == '-' && argv[i][1] == 'l')
            fuseArgs->list = 1;
        if (argv[i][0] == '-' && argv[i][1] == 'a')
        {
            fuseArgs->add = 1;
            fuseArgs->toAdd = strdup(argv[i+1]);
        }
        if (argv[i][0] == '-' && argv[i][1] == 'r')
        {
            fuseArgs->remove = 1;
            fuseArgs->toRemove = strdup(argv[i+1]);
        }
        if (argv[i][0] == '-' && argv[i][1] == 'e')
        {
            fuseArgs->extract = 1;
            fuseArgs->toExtract = strdup(argv[i+1]);
        }
        if (argv[i][0] == '-' && argv[i][1] == 'f')
        {
            fuseArgs->filefsname = 1;
            fuseArgs->fsname = strdup(argv[i+1]);
        }
    }
}

// Given code for default functionality
void FuseGivenTest(fuseArgStruct* fuseArgs, char* programPath)
{
    if (!fuseArgs->filefsname)
        FuseUsageError(programPath);
    fuseArgs->fd = open(fuseArgs->fsname, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
    if (fuseArgs->fd == -1)
    {
        perror("open failed");
        exit(EXIT_FAILURE);
    }
    if (zerosize(fuseArgs->fd))
        fuseArgs->newfs = 1;
    if (fuseArgs->newfs)
    {
        if (lseek(fuseArgs->fd, FSSIZE-1, SEEK_SET) == -1)
        {
            perror("seek failed");
            exit(EXIT_FAILURE);
        }
        if (write(fuseArgs->fd, "\0", 1) == -1)
        {
            perror("write failed");
            exit(EXIT_FAILURE);
        }
    }
    MapFS(fuseArgs->fd);
    if (fuseArgs->newfs)
        FormatFS();
    LoadFS();
    if (fuseArgs->add)
        AddFileToFS(fuseArgs->toAdd);
    if (fuseArgs->remove)
        RemoveFileFromFS(fuseArgs->toRemove);
    if (fuseArgs->extract)
        ExtractFileFromFS(fuseArgs->toExtract);
    if(fuseArgs->list)
        ListFS();
    UnmapFS();
}

// Initialize entire fuseStruct
void FuseStructInit(fuseArgStruct* fuseStruct)
{
    fuseStruct->create = 0;
    fuseStruct->list = 0;
    fuseStruct->add = 0;
    fuseStruct->remove = 0;
    fuseStruct->extract = 0;
    fuseStruct->toAdd = NULL;
    fuseStruct->toRemove = NULL;
    fuseStruct->toExtract = NULL;
    fuseStruct->fsname = NULL;
    fuseStruct->fd = -1;
    fuseStruct->newfs = 0;
    fuseStruct->filefsname = 0;
}

// Print error if usage is wrong
void FuseUsageError(char* programPath)
{
    fprintf(stderr, "Usage %s [-l] [-a path] [-e path] [-r path] -f name\n",
            programPath);
    exit(EXIT_FAILURE);
}

// Return bool of if the size is 0
int zerosize(int fd)
{
    struct stat stats;
    fstat(fd, &stats);
    if(stats.st_size == 0)
        return 1;
    return 0;
}