#if !defined(RT_HR_H)
#define RT_HR_H
// hr.h
//
// Description: Handling unexpected COM HRESULTs as C++ exceptions.
// 
// The utilities provided here aid in debugging (and logging after
// delivery) as they record the source file/line location where the
// error was encountered.
// 
// Setting breakpoints on the throw statemens in this header file
// will stop execution for all errors encountered by the program
// through THR(), TWS(), etc., before the exception is thrown,
// allowing you to check the call stack for the source of the error
// in the debugger.
//
// This file is meant to be used to trap unexepected errors.  For
// expected error conditions (such as the HRESULTs returned by
// IDirect3D8::CheckDeviceType, or the D3DERR_DEVICELOST result
// returned by IDirect3DDevice8::Present), you should explicitly
// test against expected failure codes and only THR() on error
// code you do not expect.
//
// This will avoid the cost of exceptions for the normal flow of
// control, and the overhead of exceptions is perfectly reasonable
// when you expect the call to succeed but it fails anyway.  These
// failures usually represent an invalid parameter passed to Direct3D
// and the returned HRESULT will be D3DERR_INVALIDCALL with coresponding
// additional information in the debug output.
//
// Provides:
//  hr_message
//      Exception class that records a failed HRESULT, file/line source
//      file pair indicating where the failed HRESULT was generated, and
//      possible context message.  Its constructor (not inlined) looks
//      up the HRESULT via FormatMessage() and a few other places specific
//      to DirectX to generate the message text.
//
//  display_error
//      Displays a message box containing the error string inside an
//      hr_message and returns the HRESULT.
//
//  throw_hr, throw_win functions
//      Inline function for checking a result and throwing an exception
//      of type hr_message upon failure.
//
//  THR(), TWS() and variants
//      Macros to supply __FILE__ and __LINE__ at to throw_hr/throw_win
//      so that the file/line is recorded from the source including this
//      header and not this header itself.
//
// Example:
//      try
//      {
//          THR(some_interface_ptr->SomeMethod());
//          HFONT font = TWS(::CreateFontIndirect(&lf));
//          // other stuff that may throw rt::hr_message
//      }
//      catch (const rt::hr_message &bang)
//      {
//          return rt::display_error(bang);
//      }
//
// Copyright (C) 2000-2001, Rich Thomson, all rights reserved.
//

#include <windows.h>
#include <tchar.h>

namespace rt {
  //////////////////////////////////////////////////////////////////////
  // hr_message
  //
  // Class for bundling up an HRESULT and a message and a source code
  // file/line number.
  //
  class hr_message
  {
  public:
    hr_message(const TCHAR *file, unsigned line,
              HRESULT hr = E_FAIL, const TCHAR *message = NULL);
    ~hr_message() {}
    
    const TCHAR *file() const    { return m_file; }
    unsigned line() const        { return m_line; }
    HRESULT result() const       { return m_result; }
    const TCHAR *message() const { return m_message; }
    
  private:
    enum {
      MESSAGE_LEN = 1024
    };
    const TCHAR *m_file;
    unsigned m_line;
    HRESULT m_result;
    TCHAR m_message[MESSAGE_LEN];
  };
  
  //////////////////////////////////////////////////////////////////////
  // throw_hr
  //
  // Function that throws an exception when the given HRESULT failed.
  //
  inline HRESULT
  throw_hr(const TCHAR *file, unsigned line,
       HRESULT hr, const TCHAR *message = NULL)
  {
    if (FAILED(hr)) {
      throw hr_message(file, line, hr, message);
    }
    return hr;
  }

  //////////////////////////////////////////////////////////////////////
  // throw_win
  //
  // Function that throws an exception when a Win32 return value
  // indicates failure.
  //
  template<typename T>
  inline T
  throw_win(const TCHAR *file, unsigned line,
            T status, const TCHAR *message = NULL,
            int error = GetLastError())
  {
    if (!status)
    {
      throw_hr(file, line, HRESULT_FROM_WIN32(error), message);
    }
    return status;
  }

  //////////////////////////////////////////////////////////////////////
  // display_error
  //
  // Takes an hr_message and displays the message string in a message
  // box and returns the HRESULT value.
  //
  inline HRESULT
  display_error(const hr_message &bang, const TCHAR *title = NULL)
  {
    ::MessageBox(0, bang.message(), title, 0);
    return bang.result();
  }
};

// macros to fill in __FILE__, __LINE__ and _T() automatically

// THR => throw HRESULT
#define THR(hr_)         rt::throw_hr(_T(__FILE__), __LINE__, hr_, _T(#hr_))
#define THRM(hr_, msg_)  rt::throw_hr(_T(__FILE__), __LINE__, hr_, msg_)
#define THRMT(hr_, msg_) rt::throw_hr(_T(__FILE__), __LINE__, hr_, _T(msg_))


// Win32 has lots of functions that return zero on failure: a
// NULL pointer or handle, a zero return count, etc.
// Most Win32 functions return the error code via GetLastError()
// Some Win32 functions return the error code as a non-zero status
// So throw_win takes both a status code and an error code.
//
// TWS   => throw Win32 function status
// TWSM  => Win32 status with message
// TWSMT => Win32 status with message constant needing _T()
#define TWS(status_) \
    rt::throw_win(_T(__FILE__), __LINE__, status_)
#define TWSM(status_, msg_) \
    rt::throw_win(_T(__FILE__), __LINE__, status_, msg_)
#define TWSMT(status_, msg_) \
    rt::throw_win(_T(__FILE__), __LINE__, status_, _T(msg_))

// variations with error code supplied
#define TWSE(status_, error_) \
    rt::throw_win(_T(__FILE__), __LINE__, status_, NULL, error_)
#define TWSME(status_, error_, msg_) \
    rt::throw_win(_T(__FILE__), __LINE__, status_, msg_, error_)
#define TWSMTE(status_, error_, msg_) \
    rt::throw_win(_T(__FILE__), __LINE__, status_, _T(msg_), error_)

#endif
