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

#include <platform.h>

#include "sdk/common/interface/httpfile.h"
#include "sdk/common/interface/webserver_plugin.h"

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

#include "webserver_static_file.h"

WebserverStaticFile::WebserverStaticFile(httpfile * const staticFile)
    : staticFile(staticFile)
{
    this->dataBuffer = NULL;
    this->dataSize = 0;
    this->bytesSend = 0;
}


WebserverStaticFile::~WebserverStaticFile()
{
}


void WebserverStaticFile::SendNextPart(IWebserverGet * const webserverGet)
{
    if (bytesSend < dataSize) {
        ulong64 dataSizeDiff = dataSize - bytesSend;
        ulong64 sendSize = dataSizeDiff > WS_MAX_DATA_SIZE ? WS_MAX_DATA_SIZE : dataSizeDiff;
        webserverGet->Send(dataBuffer, size_t(sendSize));
        dataBuffer += sendSize;
        bytesSend += sendSize;
        if (bytesSend == dataSize) webserverGet->Close();
    }
}


void WebserverStaticFile::WebserverGetRequestAcceptComplete(IWebserverGet * const webserverGet)
{
    const char * etag = webserverGet->GetETag();
    if (etag && strcmp(etag, staticFile->GetMD5String()) == 0) {
        webserverGet->SetTransferInfo(wsr_type_t(WSR_INT_UNCHANGED), 0, WSP_FLAG_NONE, etag);
        webserverGet->Close();
    }
    else {
        if (webserverGet->GetRangeCount() > 0) {
            // Only handle the first range given by the request. If the day will come where this
            // might be a problem, we need to resolve it.
            IWebserverGetRange * range = webserverGet->GetRange(0);

            ulong64 rstart = 0, rend = 0;
            switch (range->rangeType)
            {
            case IWebserverGetRange::RANGE_START_END:
                rstart = range->start;
                rend = range->end;
                if (rend >= staticFile->GetSize())
                    rend = staticFile->GetSize() - 1;
                break;

            case IWebserverGetRange::RANGE_START_ONLY:
                rstart = range->start;
                rend = staticFile->GetSize() - 1;
                break;

            case IWebserverGetRange::RANGE_LAST_BYTES:
                rend = staticFile->GetSize();
                rstart = rend - range->start; // The number of last bytes is stored in start
                rend--; // Need to be zero based...
                break;

            case IWebserverGetRange::RANGE_NONE:
                webserverGet->Cancel(WSP_CANCEL_BAD_REQUEST);
                return;
            }

            if (rstart > rend) {
                webserverGet->Cancel(WSP_CANCEL_BAD_REQUEST);
                return;
            }

            webserverGet->SetTransferRange(rstart, rend);
            dataSize = rend - rstart + 1;
            dataBuffer = const_cast<byte *>(staticFile->GetData() + rstart);

            // Special case - start and end of range is the same. This will often be used to determine, if
            // the server supports http range field. For that case, simply send a OK to the browser, but with
            // a flag to let the browser report support for range. In that case, send only one byte.
            if (rend == rstart) {
                webserverGet->SetTransferInfo(wsr_type_t(staticFile->GetResourceType()), staticFile->GetSize(), WSP_FLAG_NONE, staticFile->GetMD5String());
                webserverGet->Send(dataBuffer, 1);
                webserverGet->Close();
                return;
            }
            else {
                webserverGet->SetTransferInfo(wsr_type_t(staticFile->GetResourceType()),
                                              staticFile->GetSize(),
                                              wsr_flags_t(staticFile->GetFlags()),
                                              staticFile->GetMD5String());
            }
        }
        else {
            dataBuffer = const_cast<byte *>(staticFile->GetData());
            dataSize = staticFile->GetSize();
            webserverGet->SetTransferInfo(wsr_type_t(staticFile->GetResourceType()),
                                          staticFile->GetSize(),
                                          wsr_flags_t(staticFile->GetFlags()),
                                          staticFile->GetMD5String());
            
            // For ttf files, we need to add an additional header to enable Cross Origin Resource Sharing (CORS).
            size_t l = strlen(staticFile->GetFileName());
            if ((l > 4 && strcmp(staticFile->GetFileName() + l - 4, ".ttf") == 0) ||
                (l > 4 && strcmp(staticFile->GetFileName() + l - 4, ".mjs") == 0) ||
                (l > 5 && strcmp(staticFile->GetFileName() + l - 5, ".wasm") == 0) ||
                (l > 5 && strcmp(staticFile->GetFileName() + l - 5, ".jpeg") == 0) ||
                (l > 7 && strcmp(staticFile->GetFileName() + l - 7, ".tflite") == 0)) {
                webserverGet->SetHeaderField("Access-Control-Allow-Origin", "*");
            }
        }

        this->SendNextPart(webserverGet);
    }
}


void WebserverStaticFile::WebserverGetSendResult(IWebserverGet * const webserverGet)
{
    this->SendNextPart(webserverGet);
}


void WebserverStaticFile::WebserverGetCloseComplete(IWebserverGet * const webserverGet)
{
    delete webserverGet;
    delete this;
}


void WebserverStaticFile::WebserverGetConnectionShutdownComplete(IWebserverGet * const webserverGet)
{
    delete webserverGet;
    delete this;
}