#include "crDevNet.h"

#include <SDL3/SDL.h>

#include "crApp.h"
#include "crDevConsole.h"
#include "crNet.h"

static const char* GET_URL     = "https://romakga.duckdns.org/api/sandbox/test_get";
static const char* POST_URL    = "https://romakga.duckdns.org/api/sandbox/test_post";
static const char* WS_URL      = "wss://romakga.duckdns.org/ws/";
static const char* SAMPLE_JSON = "{\"ii\":1234,\"ff\":9.81,\"ss\":\"req\"}";

static const char* JoinArgs( int32_t argc, const char** argv, int32_t start, char* buf, size_t bufSize )
{
    buf[ 0 ] = 0;
    for( int32_t i = start; i < argc; ++i )
    {
        if( i > start )
            SDL_strlcat( buf, " ", bufSize );
        SDL_strlcat( buf, argv[ i ], bufSize );
    }
    return buf;
}

void crDevNet::Init( crApp* app )
{
    _app = app;

    crDevConsole* console = app->devConsole;

    console->Register( "httpget",  "httpget <url>",         CmdHttpGet,  this );
    console->Register( "httppost", "httppost <url> <json>", CmdHttpPost, this );
    console->Register( "wsopen",   "wsopen <url>",          CmdWsOpen,   this );
    console->Register( "wssend",   "wssend <text>",         CmdWsSend,   this );
    console->Register( "wsclose",  "wsclose",               CmdWsClose,  this );

    console->Register( "nettest_get",      "GET preset",    CmdNettestGet,  this );
    console->Register( "nettest_post",     "POST preset",   CmdNettestPost, this );
    console->Register( "nettest_ws",       "WS echo open",  CmdNettestWs,   this );
    console->Register( "nettest_ws_msg",   "WS echo send",  CmdWsSend,      this );   // shares the current socket
    console->Register( "nettest_ws_close", "WS echo close", CmdWsClose,     this );
}

void crDevNet::Cleanup()
{
    _app->devConsole->UnregisterByContext( this );
}

void crDevNet::Drain()
{
    crNet* net = _app->net;

    for( int32_t i = 0; i < net->EventCount(); ++i )
    {
        const crNetEvent e = net->EventAt( i );
        switch( e.type )
        {
            case ENetEventType::HTTP_RESULT:
                _app->devConsole->Print( "net #%d HTTP %d: %s", e.id, e.status, reinterpret_cast<const char*>( e.data ) );
                break;
            case ENetEventType::HTTP_FAILED:
                _app->devConsole->Print( "net #%d HTTP fail %d: %s", e.id, e.status, reinterpret_cast<const char*>( e.data ) );
                break;
            case ENetEventType::WS_OPEN:
                _app->devConsole->Print( "net #%d WS open", e.id );
                break;
            case ENetEventType::WS_TEXT:
                _app->devConsole->Print( "net #%d WS text: %s", e.id, reinterpret_cast<const char*>( e.data ) );
                break;
            case ENetEventType::WS_BINARY:
                _app->devConsole->Print( "net #%d WS binary (%d bytes)", e.id, e.size );
                break;
            case ENetEventType::WS_CLOSED:
                _app->devConsole->Print( "net #%d WS closed (%d)", e.id, e.status );
                break;
            default:
                break;
        }
    }
}

/*static*/ void crDevNet::CmdHttpGet( void* context, int32_t argc, const char** argv )
{
    crDevNet* self = static_cast<crDevNet*>( context );
    if( argc < 2 )
    {
        self->_app->devConsole->Print( "usage: httpget <url>" );
        return;
    }
    const int32_t id = self->_app->net->Get( argv[ 1 ] );
    self->_app->devConsole->Print( "httpget #%d %s", id, argv[ 1 ] );
}

/*static*/ void crDevNet::CmdHttpPost( void* context, int32_t argc, const char** argv )
{
    crDevNet* self = static_cast<crDevNet*>( context );
    if( argc < 3 )
    {
        self->_app->devConsole->Print( "usage: httppost <url> <json>" );
        return;
    }
    char body[ 512 ];
    JoinArgs( argc, argv, 2, body, sizeof( body ) );
    const int32_t id = self->_app->net->Post( argv[ 1 ], "application/json", body, static_cast<int32_t>( SDL_strlen( body ) ) );
    self->_app->devConsole->Print( "httppost #%d %s", id, argv[ 1 ] );
}

/*static*/ void crDevNet::CmdWsOpen( void* context, int32_t argc, const char** argv )
{
    crDevNet* self = static_cast<crDevNet*>( context );
    if( argc < 2 )
    {
        self->_app->devConsole->Print( "usage: wsopen <url>" );
        return;
    }
    self->_wsId = self->_app->net->WsConnect( argv[ 1 ] );
    self->_app->devConsole->Print( "wsopen #%d %s", self->_wsId, argv[ 1 ] );
}

/*static*/ void crDevNet::CmdWsSend( void* context, int32_t argc, const char** argv )
{
    crDevNet* self = static_cast<crDevNet*>( context );
    if( argc < 2 )
    {
        self->_app->devConsole->Print( "usage: wssend <text>" );
        return;
    }
    char text[ 512 ];
    JoinArgs( argc, argv, 1, text, sizeof( text ) );
    const bool ok = self->_app->net->WsSendText( self->_wsId, text );
    self->_app->devConsole->Print( "wssend #%d %s", self->_wsId, ok ? "ok" : "(no open socket)" );
}

/*static*/ void crDevNet::CmdWsClose( void* context, int32_t, const char** )
{
    crDevNet* self = static_cast<crDevNet*>( context );
    self->_app->net->WsClose( self->_wsId );
    self->_app->devConsole->Print( "wsclose #%d", self->_wsId );
}

/*static*/ void crDevNet::CmdNettestGet( void* context, int32_t, const char** )
{
    crDevNet* self = static_cast<crDevNet*>( context );
    const int32_t id = self->_app->net->Get( GET_URL );
    self->_app->devConsole->Print( "nettest_get #%d", id );
}

/*static*/ void crDevNet::CmdNettestPost( void* context, int32_t, const char** )
{
    crDevNet* self = static_cast<crDevNet*>( context );
    const int32_t id = self->_app->net->Post( POST_URL, "application/json", SAMPLE_JSON, static_cast<int32_t>( SDL_strlen( SAMPLE_JSON ) ) );
    self->_app->devConsole->Print( "nettest_post #%d", id );
}

/*static*/ void crDevNet::CmdNettestWs( void* context, int32_t, const char** )
{
    crDevNet* self = static_cast<crDevNet*>( context );
    self->_wsId = self->_app->net->WsConnect( WS_URL );
    self->_app->devConsole->Print( "nettest_ws #%d", self->_wsId );
}
