/*========================== begin_copyright_notice ============================
Copyright (C) 2021 Intel Corporation

SPDX-License-Identifier: MIT
============================= end_copyright_notice ===========================*/

#include <pin.H>
#include "gtreplay_crt.h"

#include <stdlib.h>
#include <unistd.h>

bool OpenBinFile(FileHandle* file, string& name)
{
    FILE* f = fopen(name.c_str(), "rb");
    if (!f)
        return false;

    *file = (FileHandle)f;
    
    return true;
}

size_t GetFileSize(FileHandle file)
{
    FILE* f = (FILE*)file;
    fseek(f, 0, SEEK_END);
    const size_t size = ftell(f);
    fseek(f, 0, SEEK_SET);

    return size;
}

void ReadBinFile(FileHandle file, char* buffer, size_t size)
{
    FILE* f = (FILE*)file;

    fread(buffer, 1, size, f);
}

void CloseBinFile(FileHandle file)
{
    FILE* f = (FILE*)file;
    fclose(f);
}

void SleepSeconds(uint32_t seconds)
{
    PIN_Sleep(seconds);
}

uint32_t CurrentThreadID()
{
    return ~uint32_t();
}


namespace
{
    typedef std::vector<PIN_THREAD_UID> PinThreadHandles;
    struct ThreadFuncInfo
    {
        RunHostThreadFunc func;
        void* param;
    };

    static void ThreadProxyFunc(void* arg)
    {
        printf("ThreadProxyFunc: start\n"); fflush(stdout);

        ThreadFuncInfo* info = (ThreadFuncInfo*)arg;
        info->func(info->param);
        delete info;

        printf("ThreadProxyFunc: finish\n"); fflush(stdout);
    }
}
ThreadHandles CreateHostThreads(uint32_t numOfThreads, RunHostThreadFunc func, void* param)
{
     //TODO: Investigate why PIN prevents child thread creation from internal thread

    PinThreadHandles* threadHandles = new PinThreadHandles(numOfThreads);
    for (uint32_t i = 0; i < numOfThreads; i++)
    {
        PIN_THREAD_UID& threadId = threadHandles->at(i);
        ThreadFuncInfo* info = new ThreadFuncInfo;
        info->func = func;
        info->param = param;
        if (PIN_SpawnInternalThread(ThreadProxyFunc, info, 0, &threadId) != INVALID_THREADID)
        {
            printf("PIN_SpawnInternalThread - OK\n"); fflush(stdout);
        }
        else
        {
            printf("PIN_SpawnInternalThread - Fail\n"); fflush(stdout);
        }

    }

    return threadHandles;
}

void JoinHostThreads(uint32_t numOfThreads, ThreadHandles th)
{
    PinThreadHandles* threadHandles = (PinThreadHandles*)th;

    for(PIN_THREAD_UID& pinTid : *threadHandles)
        PIN_WaitForThreadTermination(pinTid, PIN_INFINITE_TIMEOUT, nullptr);

    delete threadHandles;
}

string DecStr(uint32_t& num, unsigned int width)
{
    std::stringstream strm;
    strm << std::setw(width) << num;
    return strm.str();
}

static
string HexStrInternal(uint64_t num, string prefix, int width)
{
    std::stringstream strm;
    strm << prefix << std::hex << std::setw(width) << std::setfill('0') << num;
    return strm.str();
}

string HexStr(uint64_t num, int width)
{
    return HexStrInternal(num, "0x", width);
}

string HexStr(void* ptr, int width)
{
    std::stringstream strm;
    strm << std::hex << std::setw(width) << std::setfill('0') << ptr;
    return strm.str();
}

string HexStr(uint64_t num, string prefix, int width)
{
    return HexStrInternal(num, prefix, width);
}

char PathSeparator()
{
#if defined(TARGET_WINDOWS)
    static const char sep = '\\';
#else
    static const char sep = '/';
#endif
    return sep;
}