#ifndef _CONTAINER
#define _CONTAINER

#include "prototype.h"

#define __USE_GNU
#define _GNU_SOURCE

#include <sched.h>
#include <stdarg.h>
#include <sys/wait.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>

#define STACKSIZE (1024 * 1024)
static char child_stack[STACKSIZE];

/**
 * Child_exec is the func that will be executed as the result of clone
 */
int container_create(void *stuff)
{
    char rootfs_path[] = "./busybox";
    container_t *c = (container_t *)stuff;
    struct clone_args *cloneArgs = &(c->cloneArgs);

    if (pivot_root(rootfs_path) != 0)
    {
        error_handler_container(c, "failed to pivot root");
    }

    // mount dev, sys, proc into pivot root fs
    remount();

    if (sethostname(cloneArgs->hostname, strlen(cloneArgs->hostname)) != 0)
    {
        error_handler_container(c, "fail to set new hostname");
    }

    // run container command
    if (run_subprocess_async(cloneArgs->argc, cloneArgs->argv) != 0)
    {
        error_handler_container(c, "failed to execvp arguments");
    }

    free(c); // free twice don't know why but maybe clone lost c reference
    c = NULL;

    // we end container
    return EXIT_SUCCESS;
}

/**
 * Container constructor initialize container_t object
 */
container_t *initialize_container(struct clone_args cloneArgs)
{
    container_t *c = (container_t *)malloc(sizeof(container_t));
    // in order new net ns, new mount ns, new hostname ns, new pid ns, child ns
    c->_cloneFlags = CLONE_NEWUSER | CLONE_NEWNET | CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWCGROUP;
    c->cloneArgs = cloneArgs;
    return c;
}

/**
 * Launch command into container
 */
pid_t run_container(container_t *c)
{
    // the result of this call is that our child_exec will be run_container in another
    // process returning it's pid
    pid_t pid = clone(container_create, child_stack + STACKSIZE, c->_cloneFlags | SIGCHLD, c);
    if (pid < 0)
    {
        error_handler_container(c, "failed to run_container clone");
    }
    // lets wait on our child process here before we, the parent, exit
    if (waitpid(pid, NULL, 0) == -1)
    {
        error_handler_container(c, "failed to wait pid %d", pid);
    }

    return pid;
}

/**
 * Handle error occured during container creation
 * this methode permit to call container_destroy
 * before printing error
 * take at last parameter variadic argument
 */
void error_handler_container(container_t *c, const char *error_msg, ...)
{
    va_list arg;
    va_start(arg, error_msg);

    destroy_container(c);
    last_error(error_msg, arg);
}

/**
 * Container destructor
 */
void destroy_container(container_t *c)
{
    free(c);
}

#endif
