/**
 * \file
 * \brief Hello world application
 */

/*
 * Copyright (c) 2016 ETH Zurich.
 * All rights reserved.
 *
 * This file is distributed under the terms in the attached LICENSE file.
 * If you do not find this file, copies can be found by writing to:
 * ETH Zurich D-INFK, CAB F.78, Universitaetstr. 6, CH-8092 Zurich,
 * Attn: Systems Group.
 */


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

#include <aos/aos.h>
#include <aos/aos_rpc.h>
#include <aos/deferred.h>

#define HELLO_CATCH_COMMAND "catch"
#define HELLO_SPAWN_COMMAND "spawn"
#define HELLO_SPAWN_COMMAND_REMOTE "spawn_remote"

#define HELLO_CMDLINE_READ_LEN 100

__attribute__((__used__))
static int null_dereference(void *ignored) {
    debug_printf("[null_dereference] Oh no! :O\n");
    // cause a page fault (slight offset from null simulates NULL struct field access)
    *((volatile char *)(NULL + 7));
    return 1;
}

int main(int argc, char *argv[])
{
    errval_t err;
    printf("Hello, world!\n");

    // test page fault handling by doing a null dereference on a different thread
    // thread_create(null_dereference, NULL);
    // barrelfish_usleep(10000000);

    printf("argv:\n");
    for(int i = 0; i < argc; ++i) {
        printf("  %d -> %s\n", i, argv[i]);
    }

    struct aos_rpc *rpc = aos_rpc_get_init_channel();
    err = aos_rpc_send_number(rpc, 1234);
    if (err_is_fail(err)) USER_PANIC_ERR(err, "Failed to send RPC");

    err = aos_rpc_send_string(rpc, "Hello World via RPC!");
    if (err_is_fail(err)) USER_PANIC_ERR(err, "Failed to send RPC");

    // try to spawn a child using rpc
    if (argc > 1 && !strncmp(argv[1], HELLO_SPAWN_COMMAND, sizeof(HELLO_SPAWN_COMMAND))) {
        debug_printf("Waiting for 1s before showing shell because kernel getchar blocks everything!\n");
        barrelfish_usleep(1000000);

        debug_printf("# ");
        // try to read cmdline from the terminal (a very very primitive shell)
        char cmdline[HELLO_CMDLINE_READ_LEN+1];
        for(int i = 0; i < HELLO_CMDLINE_READ_LEN; ++i) {
            char c = getchar();
            if(c == '\r') {
                printf("\n");
                cmdline[i] = '\0';
                break;
            }
            if(c == 0x7f) {
                if (i > 0) {
                    printf("\b \b"); fflush(stdout);
                }
                --i;
                if(i >= 0) --i;
                continue;
            }

            cmdline[i] = c;
            printf("%c", c); fflush(stdout);
        }
        cmdline[HELLO_CMDLINE_READ_LEN] = '\0';

        printf("Got command line: %s\n", cmdline);

        // start process
        domainid_t pid;
        rpc = aos_rpc_get_process_channel();
        err = aos_rpc_process_spawn(rpc, cmdline, 0, &pid);
        if (err_is_fail(err)) USER_PANIC_ERR(err, "Failed to spawn process");
        printf("Started a new process!\n");

        // get process name
        char *name;
        err = aos_rpc_process_get_name(rpc, pid, &name);
        if (err_is_fail(err)) USER_PANIC_ERR(err, "Failed to get process name");
        printf("Process Name: %s\n", name);

        // get all PIDs
        domainid_t *pids;
        size_t pid_count;
        err = aos_rpc_process_get_all_pids(rpc, &pids, &pid_count);
        if (err_is_fail(err)) USER_PANIC_ERR(err, "Failed to get all PIDs");
        printf("PIDs:\n");
        for(size_t i = 0; i < pid_count; ++i) {
            printf(" %lu\n", pids[i]);
        }
    }

    // if we receive the catch command, RUN!
    if (argc > 1 && !strncmp(argv[1], HELLO_CATCH_COMMAND, sizeof(HELLO_CATCH_COMMAND))) {
        int iter = 0;
        while(1) {
            printf("Catch me if you can! %d\n", iter);
            // wait for 1 second
            barrelfish_usleep(1000000);
            ++iter;
        }
    }

    if (argc > 1 && !strncmp(argv[1], HELLO_SPAWN_COMMAND_REMOTE, sizeof(HELLO_SPAWN_COMMAND_REMOTE))) {
        printf("Spawning process on core 1...\n");
        domainid_t pid;
        rpc = aos_rpc_get_process_channel();
        err = aos_rpc_process_spawn(rpc, "hello world", 1, &pid);
        if (err_is_fail(err)) USER_PANIC_ERR(err, "Failed to spawn process");
        printf("Spawn succeeded, pid=%"PRIuDOMAINID"\n", pid);
    }

    return EXIT_SUCCESS;
}
