/*
 * PROJECT:     xml2sdb
 * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
 * PURPOSE:     Implement platform agnostic read / write / allocation functions, parse commandline
 * COPYRIGHT:   Copyright 2016,2017 Mark Jansen (mark.jansen@reactos.org)
 */

#include "xml2sdb.h"
#include "sdbpapi.h"
#include "sdbstringtable.h"
#include <time.h>
#include <stdio.h>
#include <stdarg.h>

extern "C"
{
ULONG g_ShimDebugLevel = SHIM_WARN;

LPVOID WINAPI SdbpAlloc(SIZE_T size)
{
    return ::calloc(1, size);
}

LPVOID WINAPI SdbpReAlloc(LPVOID mem, SIZE_T size, SIZE_T oldSize)
{
    LPVOID newMem = ::realloc(mem, size);
    if (newMem && size > oldSize)
    {
        memset((BYTE*)newMem + oldSize, 0, size - oldSize);
    }
    return newMem;
}

void WINAPI SdbpFree(LPVOID mem)
{
    return ::free(mem);
}

DWORD SdbpStrlen(PCWSTR string)
{
    size_t len = 0;
    while (string[len])
        len++;
    return len;
}

DWORD WINAPI SdbpStrsize(PCWSTR string)
{
    return (SdbpStrlen(string) + 1) * sizeof(WCHAR);
}

PDB WINAPI SdbpCreate(LPCWSTR path, PATH_TYPE type, BOOL write)
{
    PDB pdb;
    FILE* f;
    std::string pathA(path, path + SdbpStrlen(path));

    f = fopen(pathA.c_str(), write ? "wb" : "rb");
    if (!f)
        return NULL;

    pdb = (PDB)SdbAlloc(sizeof(DB));
    pdb->file = f;
    pdb->for_write = write;

    return pdb;
}

void WINAPI SdbpFlush(PDB pdb)
{
    ASSERT(pdb->for_write);

    fwrite(pdb->data, pdb->write_iter, 1, (FILE*)pdb->file);
}

void WINAPI SdbCloseDatabase(PDB pdb)
{
    if (!pdb)
        return;

    if (pdb->file)
        fclose((FILE*)pdb->file);
    if (pdb->string_buffer)
        SdbCloseDatabase(pdb->string_buffer);
    if (pdb->string_lookup)
        SdbpTableDestroy(&pdb->string_lookup);
    SdbFree(pdb->data);
    SdbFree(pdb);
}

BOOL WINAPI SdbpCheckTagType(TAG tag, WORD type)
{
    if ((tag & TAG_TYPE_MASK) != type)
        return FALSE;
    return TRUE;
}

BOOL WINAPI SdbpReadData(PDB pdb, PVOID dest, DWORD offset, DWORD num)
{
    DWORD size = offset + num;

    /* Either overflow or no data to read */
    if (size <= offset)
        return FALSE;

    /* Overflow */
    if (pdb->size < size)
        return FALSE;

    memcpy(dest, pdb->data + offset, num);
    return TRUE;
}

TAG WINAPI SdbGetTagFromTagID(PDB pdb, TAGID tagid)
{
    TAG data;
    if (!SdbpReadData(pdb, &data, tagid, sizeof(data)))
        return TAG_NULL;
    return data;
}

BOOL WINAPI SdbpCheckTagIDType(PDB pdb, TAGID tagid, WORD type)
{
    TAG tag = SdbGetTagFromTagID(pdb, tagid);
    if (tag == TAG_NULL)
        return FALSE;
    return SdbpCheckTagType(tag, type);
}

BOOL WINAPIV ShimDbgPrint(SHIM_LOG_LEVEL Level, PCSTR FunctionName, PCSTR Format, ...)
{
    va_list ArgList;
    const char* LevelStr;

    if ((ULONG)Level > g_ShimDebugLevel)
        return FALSE;

    switch (Level)
    {
    case SHIM_ERR:
        LevelStr = "Err ";
        break;
    case SHIM_WARN:
        LevelStr = "Warn";
        break;
    case SHIM_INFO:
        LevelStr = "Info";
        break;
    default:
        LevelStr = "User";
        break;
    }
    printf("[%s][%-20s] ", LevelStr, FunctionName);
    va_start(ArgList, Format);
    vprintf(Format, ArgList);
    va_end(ArgList);
    return TRUE;
}


#define TICKSPERSEC        10000000
#if defined(__GNUC__)
#define TICKSTO1970         0x019db1ded53e8000LL
#else
#define TICKSTO1970         0x019db1ded53e8000i64
#endif
VOID NTAPI RtlSecondsSince1970ToTime(IN ULONG SecondsSince1970,
                          OUT PLARGE_INTEGER Time)
{
    Time->QuadPart = ((LONGLONG)SecondsSince1970 * TICKSPERSEC) + TICKSTO1970;
}


}


bool xml_2_db(const char* xml, const WCHAR* sdb);

static bool run_one(std::string& input, std::string& output)
{
    sdbstring outputW(output.begin(), output.end());
    if (!xml_2_db(input.c_str(), outputW.c_str()))
        return false;
    input = output = "";
    return true;
}

static std::string get_strarg(int argc, char* argv[], int& i)
{
    if (argv[i][2] != 0)
        return std::string(argv[i] + 2);

    ++i;
    if (i >= argc || !argv[i])
        return std::string();
    return argv[i];
}

static void update_loglevel(int argc, char* argv[], int& i)
{
    std::string value = get_strarg(argc, argv, i);
    g_ShimDebugLevel = strtoul(value.c_str(), NULL, 10);
}

int main(int argc, char * argv[])
{
    std::string input, output;
    srand(time(0));

    for (int i = 1; i < argc; ++i)
    {
        if (argv[i][0] != '/' && argv[i][0] != '-')
            continue;

        switch(argv[i][1])
        {
        case 'i':
            input = get_strarg(argc, argv, i);
            break;
        case 'o':
            output = get_strarg(argc, argv, i);
            break;
        case 'l':
            update_loglevel(argc, argv, i);
            break;
        }
        if (input.empty() || output.empty())
            continue;

        if (!run_one(input, output))
        {
            printf("Failed converting '%s' to '%s'\n", input.c_str(), output.c_str());
            return 1;
        }
    }
    return 0;
}