/*
Author: Jelle Geerts

Usage of the works is permitted provided that this instrument is
retained with the works, so that any entity that uses the works is
notified of this instrument.

DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY.
*/

#include "nowide/nowide.h"
using namespace nowide;

#include <windows.h>

#include <stdexcept>

static const std::string message_box_title = "nowide test";

static const std::string album = (
    "\x46\xE2\x99\xAF\x20\x41\xE2\x99\xAF\x20\xE2\x88\x9E");

static void test0()
{
    std::string message = "The album is '";
    message += album;
    message += "'.";
    MessageBoxW(
        NULL,
        widen(message).c_str(),
        widen(message_box_title).c_str(),
        MB_ICONINFORMATION | MB_OK);
}

static bool test1()
{
    if (widen("").size() != 0)
        return false;
    if (narrow(L"").size() != 0)
        return false;
    return true;
}

class HANDLE_Resource
{
public:
    HANDLE_Resource()
        : m_handle(NULL)
    {
    }

    HANDLE_Resource(HANDLE handle)
        : m_handle(handle)
    {
    }

    HANDLE_Resource & operator=(HANDLE handle)
    {
        if (m_handle != handle)
        {
            release();
            m_handle = handle;
        }
        return *this;
    }

    ~HANDLE_Resource()
    {
        release();
    }

    HANDLE get() const
    {
        return m_handle;
    }

    HANDLE release()
    {
        HANDLE handle = m_handle;
        if (m_handle && (m_handle != INVALID_HANDLE_VALUE))
        {
            CloseHandle(m_handle);
            m_handle = INVALID_HANDLE_VALUE;
        }
        return handle;
    }

private:
    // Make the object non-copyable.
    HANDLE_Resource(const HANDLE_Resource & other);
    HANDLE_Resource & operator=(const HANDLE_Resource & other);

    HANDLE m_handle;
};

static bool write_file(
    const std::string & filename, const std::string & contents)
{
    HANDLE_Resource hfile(CreateFileW(
        widen(filename).c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL,
        CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL));
    if (hfile.get() == INVALID_HANDLE_VALUE)
    {
        std::string message = "File '";
        message += filename;
        message += "' already exists. Test aborted.";
        MessageBoxW(
            NULL,
            widen(message).c_str(),
            widen(message_box_title).c_str(),
            MB_ICONSTOP | MB_OK);
        return false;
    }
    std::string data = narrow(widen(contents)) + " (produced by narrow(const std::wstring &))";
    data += "\r\n";
    data += narrow(widen(contents).c_str()) + " (produced by narrow(const wchar_t *))";
    DWORD bytes_written;
    if (!WriteFile(
            hfile.get(), data.c_str(), data.size(), &bytes_written, NULL))
    {
        throw std::runtime_error("WriteFile() failed");
    }
    return true;
}

static bool test2()
{
    const std::string filename = "test.txt";
    if (!write_file(filename, album))
        return false;

    std::string message = "Please take a look at the file '";
    message += filename;
    message += "'.";
    MessageBoxW(
        NULL,
        widen(message).c_str(),
        widen(message_box_title).c_str(),
        MB_ICONINFORMATION | MB_OK);
    return true;
}

static bool test3()
{
    try
    {
        std::string two_continuation_bytes = "\x80\x80";
        // Call will fail. widen() should throw an exception.
        MessageBoxW(
            NULL,
            widen(two_continuation_bytes).c_str(),
            NULL,
            MB_ICONSTOP | MB_OK);
    }
    catch (const nowide::ConversionError & e)
    {
        // Success.
        return true;
    }
    return false;
}

static bool test4()
{
    try
    {
        std::wstring unpaired_surrogate = L"\xDC00!";
        // Call will fail. narrow() should throw an exception.
        MessageBoxA(
            NULL,
            narrow(unpaired_surrogate).c_str(),
            NULL,
            MB_ICONSTOP | MB_OK);
    }
    catch (const nowide::ConversionError & e)
    {
        // Success.
        return true;
    }
    return false;
}

int main()
{
    test0();
    if (!test1())
        goto fail;
    if (!test2())
        goto fail;
    if (!test3())
        goto fail;
    if (!test4())
        goto fail;

    MessageBoxW(
        NULL,
        widen("All tests succeeded.").c_str(),
        widen(message_box_title).c_str(),
        MB_ICONINFORMATION | MB_OK);
    return 0;

fail:
    MessageBoxW(
        NULL,
        L"At least one test failed. Please note that, for pre-NT6 operating"
        L" systems, this is normal (it doesn't mean the library won't work).",
        NULL,
        MB_ICONSTOP | MB_OK);
    return 1;
}
