/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Copyright 2010, 2017
 *     Danny Robson <danny@nerdcruft.net>
 */

#include "except.hpp"

#include "windows.hpp"

#include "../debug/assert.hpp"
#include "../annotation.hpp"

#include <winerror.h>
#include <winbase.h>

using cruft::win32::error;


///////////////////////////////////////////////////////////////////////////////
error::error (DWORD _code):
    runtime_error (code_string (_code)),
    m_code (_code)
{
    CHECK_NEQ (m_code, static_cast<DWORD> (ERROR_SUCCESS));
}


//-----------------------------------------------------------------------------
error::error (void):
    error (last_code ())
{ ; }


///////////////////////////////////////////////////////////////////////////////
DWORD
error::code (void) const
{
    return m_code;
}


//-----------------------------------------------------------------------------
DWORD
error::last_code (void)
{
    return GetLastError ();
}


///////////////////////////////////////////////////////////////////////////////
HMODULE
error::try_value (HMODULE value)
{
    if (likely (value != nullptr))
        return value;

    throw_code ();
}


//-----------------------------------------------------------------------------
HANDLE
error::try_value (HANDLE value)
{
    if (likely (value != INVALID_HANDLE_VALUE))
        return value;

    throw_code ();
}


//-----------------------------------------------------------------------------
BOOL
error::try_value (BOOL value)
{
    if (likely (value))
        return value;
    throw_code ();
}


///////////////////////////////////////////////////////////////////////////////
void
error::try_code (void)
{
    try_code (last_code ());
}


//-----------------------------------------------------------------------------
void
error::try_code (DWORD id)
{
    if (unlikely (id != ERROR_SUCCESS))
        throw_code (id);
}


//-----------------------------------------------------------------------------
void
error::throw_code (void)
{
    throw_code (last_code ());
}


//-----------------------------------------------------------------------------
void
error::throw_code (DWORD code)
{
    CHECK_NEQ (code, static_cast<DWORD> (ERROR_SUCCESS));
    throw error (code);
}


///////////////////////////////////////////////////////////////////////////////
std::string
error::code_string (void)
{
    return code_string (last_code ());
}


//-----------------------------------------------------------------------------
std::string
error::code_string (DWORD code)
{
    char message[256];

    auto res = FormatMessage (
        FORMAT_MESSAGE_FROM_SYSTEM,
        NULL,
        code,
        MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
        message,
        std::size (message),
        NULL
    );

    if (res == 0) {
        error::throw_code ();
    }

    return std::string (message, message + res);
}