#ifdef WIN32

#include <windows.h>
#include <iostream>
#include "CImage.h"

static const char * strClassName = "CImageWindow";

///////////////////////////////////////////////////////////////////
//                                                               //
//  WinData structure containing data needed by the Window proc  //
//                                                               //
///////////////////////////////////////////////////////////////////
struct WinData
{
  const CImage  *pImage;
  
  HINSTANCE      hInstance;
  const char    *pClassName;
  HWND           hWnd;
  HDC            hWinDc;
  HDC            hImageDc;
  HBITMAP        hBitmap;

  // Initialization and clean-up func;
  WinData(const CImage *p);
  ~WinData();
  void Free(void);
};




/////////////////////////////////
//                             //
//  Window callback procedure  //
//                             //
/////////////////////////////////
static LRESULT CALLBACK CImageWndProc( HWND hwnd,        // handle to window
                                       UINT uMsg,        // message identifier
                                       WPARAM wParam,    // first message parameter
                                       LPARAM lParam)    // second message parameter
{
  WinData *pData = reinterpret_cast<WinData*> (GetWindowLong(hwnd, 0));
  POP_ASSERT(!pData || !pData->hWnd || pData->hWnd == hwnd,
             "Received wrong window message");

  LRESULT returnValue = 0;

  switch (uMsg)
  {
  case WM_PAINT:
    {
      PAINTSTRUCT ps;
      pData->hWinDc = BeginPaint(hwnd, &ps);
      BitBlt(
        pData->hWinDc,                        // handle to destination DC
        ps.rcPaint.left,                      // x-coord of destination upper-left corner
        ps.rcPaint.top,                       // y-coord of destination upper-left corner
        ps.rcPaint.right  - ps.rcPaint.left,  // width of destination rectangle
        ps.rcPaint.bottom - ps.rcPaint.top,   // height of destination rectangle
        pData->hImageDc,                      // handle to source DC
        ps.rcPaint.left,                      // x-coordinate of source upper-left corner
        ps.rcPaint.top,                       // y-coordinate of source upper-left corner
        SRCCOPY                               // raster operation code
      );
      EndPaint(hwnd, &ps);
      break;
    }
  case WM_KEYDOWN:
  case WM_CLOSE:
    {
      DestroyWindow(hwnd);
      pData->hWnd = NULL;
      break;
    }
  default:
    {
      returnValue = DefWindowProc(hwnd, uMsg, wParam, lParam);
      break;
    }
  }

  return returnValue;
}




/////////////////////////////////////
//                                 //
//  Function that shows the image  //
//                                 //
/////////////////////////////////////
void CImage::ExecuteShow() const
{
  WinData windata(this);

  // Register window class
  WNDCLASS wndClass;
  wndClass.style         = 0;
  wndClass.lpfnWndProc   = CImageWndProc;
  wndClass.cbClsExtra    = 0;
  wndClass.cbWndExtra    = sizeof(&windata);
  wndClass.hIcon         = NULL;
  wndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
  wndClass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
  wndClass.lpszMenuName  = NULL;
  wndClass.lpszClassName = strClassName;
  GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
                    (LPCTSTR) CImageWndProc,
                    &wndClass.hInstance);

  if (!RegisterClass(&wndClass))
  {
    std::cerr << "Couldn't register class." << std::endl;
    return;
  }

  windata.hInstance  = wndClass.hInstance;
  windata.pClassName = strClassName;


  // Create window
  windata.hWnd = CreateWindow(
    windata.pClassName,                      // Class
    NULL,                                    // Name
    WS_VISIBLE | WS_OVERLAPPED | WS_SYSMENU, // Style
    CW_USEDEFAULT,                           // x
    CW_USEDEFAULT,                           // y
    width(),                                 // Width
    height(),                                // Height
    NULL,                                    // Parent
    NULL,                                    // Menu
    windata.hInstance,                       // Module instance
    NULL);                                   // Userdata

  if (!windata.hWnd)
  {
    std::cerr << "Couldn't create window." << std::endl;
    return;
  }

  SetWindowLong(windata.hWnd, 0, reinterpret_cast<LONG> (&windata));


  // Create what is needed to draw the bitmap
  windata.hWinDc = GetDC(windata.hWnd);

  if (!windata.hWinDc)
  {
    std::cerr << "Couldn't get window DC." << std::endl;
    return;
  }

  windata.hImageDc = CreateCompatibleDC(windata.hWinDc);
  if (!windata.hImageDc)
  {
    std::cerr << "Couldn't create compatible DC." << std::endl;
    return;
  }

  //windata.hBitmap = CreateCompatibleBitmap(windata.hImageDc, width(), height());
  windata.hBitmap = CreateBitmap(width(), height(), 1, 32, NULL);
  if (!windata.hBitmap)
  {
    std::cerr << "Couldn't create bitmap." << std::endl;
    return;
  }

  if (ReleaseDC(windata.hWnd, windata.hWinDc))
    windata.hWinDc = NULL;

  if (!SelectObject(windata.hImageDc, windata.hBitmap))
  {
    std::cerr << "Couldn't select bitmap object." << std::endl;
    return;
  }

  for (int i = 0; i < width(); ++i)
    for (int j = 0; j < height(); ++j)
    {
      SetPixel(windata.hImageDc, i, j, RGB(255*(*this)(i,j).red(),
                                           255*(*this)(i,j).green(),
                                           255*(*this)(i,j).blue()));
    }

  BOOL bRet;
  MSG  msg;
  while(  windata.hWnd != NULL &&
         (bRet = GetMessage( &msg , windata.hWnd, 0, 0)) != 0 )
  {
    if (bRet == -1)
    {
      std::cerr << "Error in message loop" << std::endl;
    }
    else
    {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
  }
}




////////////////////////////////
//                            //
//  WinData member functions  //
//                            //
////////////////////////////////
WinData::WinData(const CImage *p) : pImage(p)
{
  hInstance    = NULL;
  pClassName   = NULL;
  hWnd         = NULL;
  hWinDc       = NULL;
  hImageDc     = NULL;
  hBitmap      = NULL;
}

WinData::~WinData()
{
  Free();
}

void WinData::Free()
{
  if (hBitmap)
  {
    if (DeleteObject(hBitmap))
      hBitmap = NULL;
  }
  if (hImageDc)
  {
    if (DeleteDC(hImageDc))
      hImageDc = NULL;
  }
  if (hWinDc)
  {
    if (ReleaseDC(hWnd, hWinDc))
      hWinDc = NULL;
  }
  if (hWnd)
  {
    if (DestroyWindow(hWnd))
      hWnd = NULL;
  }
  if (pClassName)
  {
    if (UnregisterClass(pClassName, hInstance))
      pClassName = NULL;
  }
  if (hInstance)
  {
    if (CloseHandle(hInstance))
      hInstance = NULL;
  }
}

#endif // WIN32
