/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 *
 * Copyright (C) 2022 Aidan MacDonald
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/

#include "plugin.h"
#include "logf.h"

#undef DEBUGF
#define DEBUGF(...)
//#define DEBUGF printf

#define EV_EXIT MAKE_SYS_EVENT(SYS_EVENT_CLS_PRIVATE, 0xFF)

unsigned char stack[DEFAULT_STACK_SIZE];
struct event_queue queue;
int thread_id;
const char* state = "none";
const char* prev_state = "none";

static void main_loop(void)
{
    bool exiting = false;
    struct queue_event ev;

    while(true) {
        rb->queue_wait(&queue, &ev);

        /* events that are handled whether exiting or not */
        switch(ev.id) {
        case EV_EXIT:
            return;
        }

        if(exiting)
            continue;

        /* events handled only when not exiting */
        switch(ev.id) {
        case SYS_USB_CONNECTED:
            prev_state = state;
            state = "connected";
            logf("test_usb: connect ack %ld", *rb->current_tick);
            DEBUGF("test_usb: connect ack %ld\n", *rb->current_tick);
            rb->usb_acknowledge(SYS_USB_CONNECTED_ACK, ev.data);
            break;

        case SYS_USB_DISCONNECTED:
            prev_state = state;
            state = "disconnected";
            logf("test_usb: disconnect %ld", *rb->current_tick);
            DEBUGF("test_usb: disconnect %ld\n", *rb->current_tick);
            break;

        case SYS_POWEROFF:
        case SYS_REBOOT:
            prev_state = state;
            state = "exiting";
            exiting = true;
            break;
        }
    }
}

static void kill_tsr(void)
{
    rb->queue_post(&queue, EV_EXIT, 0);
    rb->thread_wait(thread_id);
    rb->queue_delete(&queue);
}

static int exit_tsr(bool reenter)
{
    MENUITEM_STRINGLIST(menu, "USB test menu", NULL,
                        "Status", "Stop plugin", "Back");

    while(true) {
        int result = reenter ? rb->do_menu(&menu, NULL, NULL, false) : 1;
        switch(result) {
        case 0:
            rb->splashf(HZ, "State: %s", state);
            rb->splashf(HZ, "Prev: %s", prev_state);
            break;
        case 1:
            rb->splashf(HZ, "Stopping USB test thread");
            kill_tsr();
            return (reenter ? PLUGIN_TSR_TERMINATE : PLUGIN_TSR_SUSPEND);
        case 2:
            return PLUGIN_TSR_CONTINUE;
        }
    }
}

static void run_tsr(void)
{
    rb->queue_init(&queue, true);
    thread_id = rb->create_thread(main_loop, stack, sizeof(stack),
                                  0, "test_usb TSR"
                                  IF_PRIO(, PRIORITY_BACKGROUND)
                                  IF_COP(, CPU));
    rb->plugin_tsr(exit_tsr);
}

enum plugin_status plugin_start(const void* parameter)
{
    bool resume = (parameter == rb->plugin_tsr);

    MENUITEM_STRINGLIST(menu, "USB test menu", NULL,
                        "Start", "Quit");

    switch(!resume ? rb->do_menu(&menu, NULL, NULL, false) : 0) {
    case 0:
        run_tsr();
        rb->splashf(HZ, "USB test thread started");
        return PLUGIN_OK;
    case 1:
        return PLUGIN_OK;
    default:
        return PLUGIN_ERROR;
    }
}
