/*
 * Copyright (C) 2018 OTA keys S.A.
 *               2018 Inria
 *
 * This file is subject to the terms and conditions of the GNU Lesser
 * General Public License v2.1. See the file LICENSE in the top level
 * directory for more details.
 */

/**
 * @ingroup tests
 * @{
 *
 * @file
 * @brief    AT module test application
 *
 * @author   Vincent Dupont <vincent@otakeys.com>
 * @author   Alexandre Abadie <alexandre.abadie@inria.fr>
 *
 * @}
 */

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

#include "at.h"
#include "shell.h"
#include "timex.h"

#include "periph/uart.h"

static at_dev_t at_dev;
static char buf[256];
static char resp[1024];

static int init(int argc, char **argv)
{
    (void)argc;
    (void)argv;

    if (argc < 3) {
        printf("Usage: %s <uart> <baudrate>\n", argv[0]);
        return 1;
    }

    uint8_t uart = atoi(argv[1]);
    uint32_t baudrate = atoi(argv[2]);

    int res = at_dev_init(&at_dev, UART_DEV(uart), baudrate, buf, sizeof(buf));

    /* check the UART initialization return value and respond as needed */
    if (res == UART_NODEV) {
        puts("Invalid UART device given!");
        return 1;
    }
    else if (res == UART_NOBAUD) {
        puts("Baudrate is not applicable!");
        return 1;
    }

    return res;
}

static int send(int argc, char **argv)
{
    if (argc < 2) {
        printf("Usage: %s <command>\n", argv[0]);
        return 1;
    }

    ssize_t len;
    if ((len = at_send_cmd_get_resp(&at_dev, argv[1], resp, sizeof(resp), 10 * US_PER_SEC)) < 0) {
        puts("Error");
        return 1;
    }

    printf("Response (len=%d): %s\n", (int)len, resp);

    return 0;
}

static int send_ok(int argc, char **argv)
{
    if (argc < 2) {
        printf("Usage: %s <command>\n", argv[0]);
        return 1;
    }

    if (at_send_cmd_wait_ok(&at_dev, argv[1], 10 * US_PER_SEC) < 0) {
        puts("Error");
        return 1;
    }

    puts("OK");

    return 0;
}

static int send_lines(int argc, char **argv)
{
    if (argc < 2) {
        printf("Usage: %s <command>\n", argv[0]);
        return 1;
    }

    ssize_t len;
    if ((len = at_send_cmd_get_lines(&at_dev, argv[1], resp, sizeof(resp), true, 10 * US_PER_SEC)) < 0) {
        puts("Error");
        return 1;
    }

    printf("Response (len=%d): %s\n", (int)len, resp);

    return 0;
}

static int send_recv_bytes(int argc, char **argv)
{
    char buffer[64];

    if (argc < 3) {
        printf("Usage: %s <command> <number of bytes>\n", argv[0]);
        return 1;
    }

    sprintf(buffer, "%s%s", argv[1], CONFIG_AT_SEND_EOL);
    at_send_bytes(&at_dev, buffer, strlen(buffer));

    ssize_t len = at_recv_bytes(&at_dev, buffer, atoi(argv[2]), 10 * US_PER_SEC);

    printf("Response (len=%d): %s\n", (int)len, buffer);

    return 0;
}

static int send_recv_bytes_until_string(int argc, char **argv)
{
    char buffer[128];
    size_t len = sizeof(buffer);

    memset(buffer, 0, sizeof(buffer));

    if (argc < 3) {
        printf("Usage: %s <command> <string to expect>\n", argv[0]);
        return 1;
    }

    sprintf(buffer, "%s%s", argv[1], CONFIG_AT_SEND_EOL);
    at_send_bytes(&at_dev, buffer, strlen(buffer));
    memset(buffer, 0, sizeof(buffer));

    int res = at_recv_bytes_until_string(&at_dev, argv[2], buffer, &len,
                                         10 * US_PER_SEC);

    if (res) {
        puts("Error");
        return 1;
    }

    printf("Response (len=%d): %s\n", (int)len, buffer);
    return 0;
}

static int drain(int argc, char **argv)
{
    (void)argc;
    (void)argv;

    at_drain(&at_dev);

    return 0;
}

static int power_on(int argc, char **argv)
{
    (void)argc;
    (void)argv;

    at_dev_poweron(&at_dev);

    puts("Powered on");

    return 0;
}

static int power_off(int argc, char **argv)
{
    (void)argc;
    (void)argv;

    at_dev_poweroff(&at_dev);

    puts("Powered off");

    return 0;
}

#ifdef MODULE_AT_URC
#ifndef MAX_URC_NB
#define MAX_URC_NB  5
#endif

#ifndef MAX_URC_LEN
#define MAX_URC_LEN 32
#endif

static at_urc_t urc_list[MAX_URC_NB];
static char urc_str[MAX_URC_NB][MAX_URC_LEN];
static bool urc_used[MAX_URC_NB];

static void _urc_cb(void *arg, const char *urc)
{
    (void)arg;
    printf("urc received: %s\n", urc);
}

static int add_urc(int argc, char **argv)
{
    if (argc < 2) {
        printf("Usage: %s <urc>\n", argv[0]);
        return 1;
    }

    if (strlen(argv[1]) > MAX_URC_LEN - 1) {
        puts("urc is too long");
        return 1;
    }

    for (size_t i = 0; i < MAX_URC_NB; i++) {
        if (!urc_used[i]) {
            strcpy(urc_str[i], argv[1]);
            urc_list[i].code = urc_str[i];
            urc_list[i].arg = NULL;
            urc_list[i].cb = _urc_cb;
            urc_used[i] = true;
            at_add_urc(&at_dev, &urc_list[i]);
            puts("urc registered");
            return 0;
        }
    }

    puts("Not enough memory, urc is not registered");
    return 1;
}

static int process_urc(int argc, char **argv)
{
    if (argc < 2) {
        printf("Usage: %s <timeout>\n", argv[0]);
        return 1;
    }

    uint32_t timeout = strtoul(argv[1], NULL, 0);
    at_process_urc(&at_dev, timeout);

    puts("urc processed");

    return 0;
}

static int remove_urc(int argc, char **argv)
{
    if (argc < 2) {
        printf("Usage: %s <urc>\n", argv[0]);
        return 1;
    }

    for (size_t i = 0; i < MAX_URC_NB; i++) {
        if (urc_used[i] && strcmp(urc_list[i].code, argv[1]) == 0) {
            at_remove_urc(&at_dev, &urc_list[i]);
            urc_used[i] = false;
            puts("urc removed");
            return 0;
        }
    }

    puts("urc not found");
    return 1;
}
#endif

static const shell_command_t shell_commands[] = {
    { "init", "Initialize AT device", init },
    { "send", "Send a command and wait response", send },
    { "send_ok", "Send a command and wait OK", send_ok },
    { "send_lines", "Send a command and wait lines", send_lines },
    { "send_recv_bytes", "Send a command and wait response as raw bytes", send_recv_bytes },
    { "send_recv_until_string", "Send a command and receive response until "
      "the expected pattern arrives", send_recv_bytes_until_string },
    { "drain", "Drain AT device", drain },
    { "power_on", "Power on AT device", power_on },
    { "power_off", "Power off AT device", power_off },
#ifdef MODULE_AT_URC
    { "add_urc", "Register an URC", add_urc },
    { "remove_urc", "De-register an URC", remove_urc },
    { "process_urc", "Process the URCs", process_urc },
#endif
    { NULL, NULL, NULL },
};

int main(void)
{
    puts("AT command test app");

    /* run the shell */
    char line_buf[SHELL_DEFAULT_BUFSIZE];
    shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);
    return 0;
}
