
/*---------------------------------------------------------------------------*/
/* webserverresource.cpp                                                     */
/* copyright (c) innovaphone 2015                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

#include <platform.h>

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

#include "webserver_internal.h"
#include "webserver_message.h"

#define WR_READ_BASE_HEADER         0
#define WR_READ_EXT_HEADER          1
#define WR_READ_POST_TARGET_LEN     2
#define WR_SHUTDOWN                 3


IWebserverMessage * IWebserverMessage::Create(ISocket * const socket, IInstanceLog * log)
{
    return new WebserverMessage(socket, log);
}


WebserverMessage::WebserverMessage(class ISocket * const socket, IInstanceLog * log)
	: log(log),
      socket(socket)      
{
    this->dontLog = false;
    this->resourceName = NULL;
    this->resourceNameLen = 0;
    this->sendResourceName = false;
    this->state = WR_READ_BASE_HEADER;
    this->buffer = (byte *)calloc(this->GetHeaderSize(), sizeof(byte));
}


WebserverMessage::~WebserverMessage()
{
    free(buffer);
    free(resourceName);
}


void WebserverMessage::Shutdown()
{
    // Shutdown only is used to prevent sending data while the system is in shutdown process.
    // So settings a flag will be enough.
    state = WR_SHUTDOWN;
}


dword WebserverMessage::GetConnectionID()
{
    return READ_DWORD_NET(&buffer[MSG_IDX_CONNECTION_ID]);
}


ws_message_t WebserverMessage::GetMessageID()
{
    return ws_message_t(READ_WORD_NET(&buffer[MSG_IDX_MESSAGE_ID]));
}


inline dword WebserverMessage::GetGeneralPurpose()
{
    return READ_DWORD_NET(&buffer[MSG_IDX_GENERAL_PURPOSE]);
}


ws_msg_websocket_t WebserverMessage::GetWebsocketMessageID()
{
    return ws_msg_websocket_t(LOW_WORD(this->GetGeneralPurpose()));
}


ws_websocket_flags_t WebserverMessage::GetWebsocketFlags()
{
    return ws_websocket_flags_t(HI_WORD(this->GetGeneralPurpose()));
}


size_t WebserverMessage::GetDataSize()
{
    return READ_WORD_NET(&buffer[MSG_IDX_DATA_SIZE]);
}


inline void WebserverMessage::Send(const char * handlerName, dword connectionID, ws_message_t message, dword generalPurpose, const void * dataBuf, size_t dataSize)
{
    if (state == WR_SHUTDOWN) return;

    WRITE_DWORD_NET(&buffer[MSG_IDX_CONNECTION_ID], connectionID);
    WRITE_WORD_NET(&buffer[MSG_IDX_MESSAGE_ID], word(message));
    WRITE_DWORD_NET(&buffer[MSG_IDX_GENERAL_PURPOSE], generalPurpose);

    ASSERT(dataSize <= WS_MAX_DATA_SIZE, "WebserverResource::SetBuffer(): size greater then WS_MAX_DATA_SIZE");
    WRITE_WORD_NET(&buffer[MSG_IDX_DATA_SIZE], (word)dataSize);

    if (!dontLog && log->LogFlagSet(LOG_WEBSERVER_PLUGIN)) {
        log->Log(LOG_WEBSERVER_PLUGIN, "%s(connection id: %u)::Send(): Sending message %s (data size: %u)", handlerName, this->GetConnectionID(), WsMessageToStr(message), dataSize);
        if (dataSize > 0 && !this->log->LogFlagSet(LOG_LDS)) log->HexDump(LOG_WEBSERVER_PLUGIN, dataBuf, dataSize);
    }
    
    socket->Send(buffer, this->GetHeaderSize());

    if (dataBuf != nullptr) socket->Send(dataBuf, dataSize);
}


void WebserverMessage::WebsocketSend(dword connectionID, ws_msg_websocket_t message, ws_websocket_flags_t flags, const byte * data, size_t len)
{
    if (log->LogFlagSet(LOG_WEBSERVER_PLUGIN)) {
        log->Log(LOG_WEBSERVER_PLUGIN, "WebserverMessage(connection id: %u)::WebsocketSend: Sending Websocket message %s (fragmented: %s, text: %s, data size: %u)", this->GetConnectionID(),
                                       WsWebsocketMsgToStr(message), (flags & WS_WEBSOCKET_FLAG_FRAGMENTED ? "true" : "false"), (flags & WS_WEBSOCKET_FLAG_TEXT ? "true" : "false"), len);

        if (len > 0 && this->log->LogFlagSet(LOG_WEBSERVER_PLUGIN) && !this->log->LogFlagSet(LOG_LDS))
            log->HexDump(LOG_WEBSERVER_PLUGIN, data, len);
    }

    dontLog = true; // We already printed a log message if necessary, so don't do it again in Send()...
    this->Send("", connectionID, WSM_WEBSOCKET_MESSAGE, MAKE_DWORD(flags, message), data, len);
    dontLog = false;
}


void WebserverMessage::SetBufferData(const byte * source)
{
    memcpy(buffer, source, this->GetHeaderSize());
}


void WebserverMessage::LogRecvMessage(const char * callingFncName, byte * buf, size_t size)
{
    if (this->GetMessageID() == WSM_WEBSOCKET_MESSAGE) {
        ws_msg_websocket_t msg = this->GetWebsocketMessageID();
        ws_websocket_flags_t flags = this->GetWebsocketFlags();
        log->Log(LOG_WEBSERVER_PLUGIN, "%s: Received Websocket message %s (fragmented: %s, text: %s, data size: %u)", callingFncName, 
            WsWebsocketMsgToStr(msg), (flags & WS_WEBSOCKET_FLAG_FRAGMENTED ? "true" : "false"), (flags & WS_WEBSOCKET_FLAG_TEXT ? "true" : "false"), size);
    }
    else {
        ws_message_t msg = this->GetMessageID();
        log->Log(LOG_WEBSERVER_PLUGIN, "%s: Received message %s (data size: %u)", callingFncName, WsMessageToStr(msg), this->GetDataSize());
        if (this->GetDataSize() != size)
            log->Log(LOG_WEBSERVER_PLUGIN, "WARNING: Datasize of header is %u, but received data size is %u", this->GetDataSize(), size);
    }

    if (size > 0 && this->log->LogFlagSet(LOG_WEBSERVER_PLUGIN) && !this->log->LogFlagSet(LOG_LDS))
        log->HexDump(LOG_WEBSERVER_PLUGIN, buf, size);
}
