/*
* Copyright (C) 2018 Global Graphics Software Ltd. All rights reserved.
*
* This example is provided on an "as is" basis and without
* warranty of any kind. Global Graphics Software Ltd. does not
* warrant or make any representations regarding the use or results
* of use of this example.
*/
#define _CRT_SECURE_NO_WARNINGS

#include <string>
#include "ScreenPro.h"
#include "ScreenData.h"

#ifdef _WIN32
#include <stdio.h>
__declspec(thread) size_t screenDataOffset = 0;
__declspec(thread) size_t configDataOffset = 0;
#else
#include <cstring>
#include <sys/stat.h>

size_t screenDataOffset = 0;
size_t configDataOffset = 0;
#endif

static bool fileIsMemScreenCache(const char *filename)
{
#ifndef _WIN32
    const char * screenCache0 = "Screens/mem/0.bin";
    const char * screenCache1 = "Screens/mem/1.bin";
    const char * screenCache2 = "Screens/mem/2.bin";
    const char * screenCache3 = "Screens/mem/3.bin";
    const char * screenCache4 = "Screens/mem/4.bin";
    const char * screenCache5 = "Screens/mem/5.bin";
    const char * screenCache6 = "Screens/mem/6.bin";
#else
    const char * screenCache0 = "Screens\\mem/0.bin";
    const char * screenCache1 = "Screens\\mem/1.bin";
    const char * screenCache2 = "Screens\\mem/2.bin";
    const char * screenCache3 = "Screens\\mem/3.bin";
    const char * screenCache4 = "Screens\\mem/4.bin";
    const char * screenCache5 = "Screens\\mem/5.bin";
    const char * screenCache6 = "Screens\\mem/6.bin";
#endif	
    return (strstr(filename, screenCache0) != NULL || strstr(filename, screenCache1) != NULL || strstr(filename, screenCache2) != NULL ||
        strstr(filename, screenCache3) != NULL || strstr(filename, screenCache4) != NULL || strstr(filename, screenCache5) != NULL ||
        strstr(filename, screenCache6) != NULL);
}

static bool fileIsMemScreenConfig(const char *filename)
{
#ifndef _WIN32
    const char * configCache = "Screens/mem/config.txt";
#else
    const char * configCache = "Screens\\mem/config.txt";
#endif	
    return strstr(filename, configCache) != NULL;
}

// static IOHandle ioOpen(const wchar_t* filename)
static IOHandle ioOpen(const char * filename)
{
    if (fileIsMemScreenCache(filename))
        return (IOHandle)screenData;

    if (fileIsMemScreenConfig(filename))
        return (IOHandle)configData;
    
    FILE* hFile = fopen(filename, "rb");
    return (IOHandle)hFile;
}

static size_t ioRead(IOHandle handle, void* dest, size_t byteCount)
{
    if (handle == (IOHandle)screenData) {
        size_t bytesRead = byteCount;
        if (byteCount + screenDataOffset > screenByteCount)
        {
            bytesRead = screenByteCount - screenDataOffset;
        }

        memcpy(dest, &screenData[screenDataOffset], bytesRead);
        screenDataOffset += bytesRead;
        return bytesRead;
    }

    if (handle == (IOHandle)configData)
    {
        size_t bytesRead = byteCount;
        if (byteCount + configDataOffset > configByteCount)
        {
            bytesRead = configByteCount - configDataOffset;
        }

        memcpy(dest, &configData[configDataOffset], bytesRead);
        configDataOffset += bytesRead;
        return bytesRead;
        
    }

    FILE* hFile = (FILE*)handle;
    return fread(dest, 1, byteCount, hFile);
}

static void ioClose(IOHandle handle)
{
    if (handle != (IOHandle)screenData && (handle != (IOHandle)configData)) {
        FILE* hFile = (FILE*)handle;
        fclose(hFile);
    }

    screenDataOffset = 0;
    configDataOffset = 0;
}

static size_t iofilesize(const char *filename)
{
    if (fileIsMemScreenCache(filename))
        return screenByteCount;

    if (fileIsMemScreenConfig(filename))
        return configByteCount;

    struct stat sb;
    return stat(filename, &sb) < 0 ? 0 : sb.st_size;
}

// inlineDataIOHandler loads screen from memory (screenData and configData) when using
// screen path "Screens\mem".
static  ScreenProFileIO inlineDataIOHandler = { ioOpen, ioRead, ioClose, iofilesize };
ScreenProFileIO * getInlineDataIOHandler()
{
    // Do not inline - We want ioHandler to go out of scope.

    return (ScreenProFileIO *) &inlineDataIOHandler;
}
