#include <rt/hr.h>
#include <rt/misc.h>
#include <dxerr8.h>
#include <d3dx8.h>
#include <stdio.h>

using namespace rt;

///////////////////////////////////////////////////////////////////////////
// MESSAGE_DLL
//
// Name of DLL containing message resources
const TCHAR *const MESSAGE_DLL = _T("rtmessages.dll");

///////////////////////////////////////////////////////////////////////////
// dll
//
// Simple helper class wrapping LoadLibrary/FreeLibrary
//
class dll 
{
public:
    dll(LPCTSTR fileName)
        : m_handle(::LoadLibrary(fileName))
    {
    }
    ~dll()
    {
        if (m_handle)
        {
            ::FreeLibrary(m_handle);
        }
    }
      
      operator HMODULE() const { return m_handle; }

private:
  HMODULE m_handle;
};

///////////////////////////////////////////////////////////////////////////
// hr_message::hr_message
//
// Constructor for an hr_message exception class.  Construct a post-mortem
// string consisting of 'File(Line): HRESULT Facility Description Context'
// For DirectDraw/Direct3D/D3DX errors, look up the error in a message
// table resource.  If not found, try the system.  If still not found,
// use DXGetErrorString8.  For all other errors look up the string from
// the system.
//
hr_message::hr_message(const TCHAR *file, unsigned line,
                       HRESULT hr, const TCHAR *message)
                       : m_file(file),
                       m_line(line),
                       m_result(hr)
{
    TCHAR buffer[MESSAGE_LEN] = { 0 };
    LPCTSTR error = buffer;
    TCHAR *facility = NULL;
    const DWORD lang_id = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
    
    const DWORD fac_code = HRESULT_FACILITY(hr);
    switch (fac_code)
    {
    case FACILITY_DIRECT3D:
    case FACILITY_DIRECT3D_UTIL:
        // figure out the facility string
        if (FACILITY_DIRECT3D == fac_code)
        {
            facility = _T("D3D");
        }
        else
        {
            facility = _T("D3DX");
        }
        {
            // first try message DLL
            dll messages(MESSAGE_DLL);
            if ((messages && !::FormatMessage(FORMAT_MESSAGE_FROM_HMODULE,
                        messages, hr, lang_id, buffer, MESSAGE_LEN, NULL)) ||
                // next try FormatMessage() w/system
                !::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr,
                                 lang_id, buffer, MESSAGE_LEN, NULL))
            {
                // finally try DXGetErrorString8()
                error = DXGetErrorString8(hr);
            }
        }
        break;
    
    default:
        if (FACILITY_ITF == fac_code)
        {
            facility = _T("Interface");
        }
        else
        {
            facility = NULL;
        }
        if (!::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr, lang_id,
                             buffer, MESSAGE_LEN, NULL))
        {
            _tcscpy(buffer, _T("Unknown error"));
        }
    }

    // start filename into message, but exclude directory name
    const TCHAR *start = _tcsrchr(file, _T('\\'));
    start = start ? start+1 : file;

    // use facility text if we know it
    if (facility)
    {
        // file(line): 0xDEADBEEF:D3D error [context]
        _sntprintf(m_message, MESSAGE_LEN, _T("%s(%d): 0x%08x:%s %s"),
                   start, line, hr, facility, error);
    }
    else
    {
        // file(line): 0xDEADBEEF error [context]
        _sntprintf(m_message, MESSAGE_LEN, _T("%s %d: 0x%08x %s"),
                   start, line, hr, error);
    }

    // append supplied context message, if any
    if (message)
    {
        _tcscat(m_message, _T(" "));
        _tcscat(m_message, message);
    }

    // ensure constructed message is zero terminated.
    m_message[MESSAGE_LEN-1] = 0;
}
