
/*---------------------------------------------------------------------------*/
/* webserver_websocket_handler.cpp                                           */
/* copyright (c) innovaphone 2016                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

#include <platform.h>

#include <stdint.h>

#include "sdk/common/os/iomux.h"
#include "sdk/common/interface/socket.h"
#include "sdk/common/interface/webserver_plugin.h"

#include "sdk/common/service/webserver_shared/webserver_internal.h"

#include "webserver_message_handler.h"
#include "webserver_websocket_handler.h"


WebserverWebsocketHandler::WebserverWebsocketHandler(UWebserverMessageHandler * baseUser,
                                                     byte * extHeaderBuffer,
                                                     dword connectionID,
                                                     char * host,
                                                     char * path,
                                                     char * resReqPath,
                                                     class ExtendedHeader & extHeader,
                                                     IWebserverMessage * const webserverMessageSend,
                                                     IInstanceLog * const log,
                                                     word protocolVersion)
    : WebserverMessageHandler("IWebsocket", MSG_HANDLER_WEBSOCKET, baseUser, extHeaderBuffer, connectionID, 0, webserverMessageSend, path, resReqPath, log, extHeader, protocolVersion)
{
    this->user = nullptr;
    this->host = host;
}


WebserverWebsocketHandler::~WebserverWebsocketHandler()
{
}


void WebserverWebsocketHandler::HandleMessage(IWebserverMessage * const message, void * buffer, size_t len)
{
    if (message->GetMessageID() != WSM_WEBSOCKET_MESSAGE) {
        this->log->Log(LOG_WEBSERVER_PLUGIN, "WARNING: IWebsocket received non-websocket related message id (%u) - ignoring", message->GetMessageID());
        return;
    }

    word flags;
    ws_close_reason_t tmp;
    switch (message->GetWebsocketMessageID())
    {
    case WS_WEBSOCKET_CONNECT_COMPLETE:
        user->WebsocketAcceptComplete(this);
        break;

    case WS_WEBSOCKET_SEND_RESULT:
        user->WebsocketSendResult(this);
        sendCount--;
        if (sendCount == 0 && closeHandler) {
            ExtendedHeader extHeader(this->log, this->protocolVersion);
            extHeader.SetWebsocketCloseReason(WSCR_NORMAL_CLOSE);
            extHeader.Encode(connectionID, static_cast<byte *>(alloca(extHeader.BufferSize())));
            webserverMessageSend->WebsocketSend(connectionID, WS_WEBSOCKET_CLOSE, WS_WEBSOCKET_FLAG_NONE, extHeader.Buffer(), extHeader.BufferSize());
        }
        break;

    case WS_WEBSOCKET_RECV_RESULT:
        flags = message->GetWebsocketFlags();
        userBuffer = nullptr;
        userBufferSize = 0;
        recvCalled = false;
        user->WebsocketRecvResult(this, buffer, len, (flags & WS_WEBSOCKET_FLAG_TEXT) == WS_WEBSOCKET_FLAG_TEXT,
                                  (flags & WS_WEBSOCKET_FLAG_FRAGMENTED) == WS_WEBSOCKET_FLAG_FRAGMENTED);
        break;

    case WS_WEBSOCKET_CLOSE_COMPLETE:
        if (this->userBuffer != nullptr) {
            this->RecvCanceled(userBuffer);
            this->userBuffer = nullptr;
            this->userBufferSize = 0;
        }
        tmp = ws_close_reason_t(READ_WORD_NET(buffer));
        ASSERT(tmp != ws_close_reason_t::WSCR_DOUBLE_RECV_CALL, "ERROR: Webserver reported double websocket recv call from this app.");
        user->WebsocketCloseComplete(this, tmp);
        free(buffer);
        break;

    default:
        this->log->Log(LOG_WEBSERVER_PLUGIN, "WARNING: IWebsocket received unexpected message id (%u)", message->GetWebsocketMessageID());
    }
}


void WebserverWebsocketHandler::WebserverPluginWillClose(wsp_close_reason_t reason)
{
    if (!this->InterfaceUserSet()) {
        delete this;
        return;
    }
    if (this->userBuffer != nullptr) {
        this->RecvCanceled(userBuffer);
        this->userBuffer = nullptr;
        this->userBufferSize = 0;
    }
    ws_close_reason_t closeReason;
    switch (reason)
    {
    case WSP_NORMAL_CLOSE:
        closeReason = WSCR_NORMAL_CLOSE;
        break;

    case WSP_CONNECTION_ERROR:
        closeReason = WSCR_SOCKET_LOST;
        break;

    default:
        closeReason = WSCR_ERROR;
    }
    user->WebsocketCloseComplete(this, closeReason);
}


void WebserverWebsocketHandler::Send(const void * buffer, size_t len, bool text)
{
    this->log->Log(LOG_WEBSERVER_PLUGIN, "WebserverWebsocketHandler(%08x,%08x,%u)::Send buffer:0x%08x len:%u text:%s",
                   this, this->user, this->connectionID, buffer, len, text ? "TRUE" : "FALSE");
    this->log->HexDump(LOG_WEBSERVER_PLUGIN, buffer, len);

    if (!closeHandler) {
        ws_websocket_flags_t flags = text ? WS_WEBSOCKET_FLAG_TEXT : WS_WEBSOCKET_FLAG_NONE;
        webserverMessageSend->WebsocketSend(connectionID, WS_WEBSOCKET_SEND, flags, static_cast<const byte *>(buffer), len);
        sendCount++;
    }
    else
        this->log->Log(LOG_WEBSERVER_PLUGIN, "WebserverWebsocketHandler(%08x,%08x,%u)::Send NOTE - handler is closing, data will not be sent!",
                       this, this->user, this->connectionID);
}


void WebserverWebsocketHandler::Recv(void * buffer, size_t len)
{
    this->log->Log(LOG_WEBSERVER_PLUGIN, "WebserverWebsocketHandler(%08x,%08x,%u)::Recv buffer:0x%08x len:%u",
                   this, this->user, this->connectionID, buffer, len);

    ASSERT(recvCalled == false, "IWebsocket::Recv() called multiple times - please wait for an incoming RecvResult() first.");
    ASSERT(userBuffer == nullptr, "IWebsocket::Recv() called with active Recv()");
    ASSERT(len <= WS_MAX_DATA_SIZE, "IWebsocket::Recv() called with len > WS_MAX_DATA_SIZE");

    recvCalled = true;
    if (!closeHandler) {
        userBuffer = static_cast<byte *>(buffer);
        userBufferSize = len;
        webserverMessageSend->WebsocketSend(connectionID, WS_WEBSOCKET_RECV, ws_websocket_flags_t(len), nullptr, 0);
    }
}


void WebserverWebsocketHandler::Close()
{
    this->log->Log(LOG_WEBSERVER_PLUGIN, "WebserverWebsocketHandler(%08x,%08x,%u)::Close", this, this->user, this->connectionID);

    if (!closeHandler) {
        closeHandler = true;
        if (sendCount == 0) {
            ExtendedHeader extHeader(this->log, this->protocolVersion);
            extHeader.SetWebsocketCloseReason(WSCR_NORMAL_CLOSE);
            extHeader.Encode(connectionID, static_cast<byte *>(alloca(extHeader.BufferSize())));
            webserverMessageSend->WebsocketSend(connectionID, WS_WEBSOCKET_CLOSE, WS_WEBSOCKET_FLAG_NONE, extHeader.Buffer(), extHeader.BufferSize());
        }
    }
}
