/*===========================================================================
 *
 * DDSurfMode.CPP - Dave Humphrey (uesp@m0use.net), 12 November 2000
 *
 *=========================================================================*/

	/* Include Files */
#include "ddsurfmode.h"
#include "d3dcom.h"
#include "d3dapp.h"


#undef  __FUNC__
#define __FUNC__ "CDDSurfaceMode::CDDSurfaceMode()"
/*===========================================================================
 *
 * Class CDDSurfaceMode Constructor
 *
 *=========================================================================*/
CDDSurfaceMode::CDDSurfaceMode() {
  pDirectDraw = NULL;
  pPrimarySurface = NULL;
  pClipper = NULL;
  pDDPalette = NULL;
  hMainWindow = NULL;
  pParentApp = NULL;
  Initialized = FALSE;
  Active = FALSE;
  ReferencedDDraw = FALSE;
  ReferencedDDSurface = FALSE;
 }
/*===========================================================================
 *		End of Class CDDSurfaceMode Constructor
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDDSurfaceMode::Destroy()"
/*===========================================================================
 *
 * Class CDDSurfaceMode Destructor
 *
 *=========================================================================*/
void CDDSurfaceMode::Destroy (void) {

	/* Cleanup DirectDraw objects */
  RELEASE(pDDPalette);
  RELEASE(pClipper);

	/* Only release primary surface if we allocated it */
  if (ReferencedDDSurface)
    pPrimarySurface = NULL;
  else
    RELEASE(pPrimarySurface);
  
	/* Only release DirectDraw object if we allocated it */
  if (ReferencedDDraw) 
    pDirectDraw = NULL;
  else {
    SetDD(NULL);
    RELEASE(pDirectDraw);
   }
  
  ReferencedDDraw = FALSE;
  ReferencedDDSurface = FALSE;
  Initialized = FALSE;
  Active = FALSE;
  hMainWindow = NULL;
  pParentApp = NULL;
 }
/*===========================================================================
 *		End of Class CDDSurfaceMode Destructor
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDDSurfaceMode::ClearPrimary()"
/*===========================================================================
 *
 * Class CDDSurfaceMode Method - boolean ClearPrimary (void);
 *
 * Attempts to clear the primary surface.  Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDDSurfaceMode::ClearPrimary (void) {
  boolean Result;
  RECT	  ClientRect;
  HDC	  hDC;

	/* Ensure valid DirectDraw objects */
  ASSERT(pPrimarySurface != NULL);
  ASSERT(hMainWindow     != NULL);

	/* Ensure the mode has been initialized first */
  if (!IsInitialized()) {
    SET_EXT_ERROR2(ERR_NULL, "Surface mode not yet initialized!");
    return (FALSE);
   }

	/* Get the DC for the primary surface */
  Result = GetDisplayDC(hDC);
  if (Result) Result = GetDisplayRect(ClientRect);
  if (!Result) return (FALSE);

	/* Clear the window */
  Rectangle(hDC, ClientRect.left, ClientRect.top, ClientRect.right, ClientRect.bottom);

	/* Release the DC of the primary surface */
  Result = ReleaseDisplayDC(hDC);
  return (Result);
 }
/*===========================================================================
 *		End of Class Method CDDSurfaceMode::ClearPrimary()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDDSurfaceMode::CreateClipper()"
/*===========================================================================
 *
 * Class CDDSurfaceMode Method - boolean CreateClipper (void);
 *
 * Initializes the DirectDraw clipper object for the surface mode. 
 * Returns FALSE on any error.  Protected class method.
 *
 *=========================================================================*/
boolean CDDSurfaceMode::CreateClipper (void) {
  HRESULT   Result;


	/* Ensure valid input */
  ASSERT(pDirectDraw     != NULL);
  ASSERT(hMainWindow     != NULL);
  ASSERT(pPrimarySurface != NULL);
  ASSERT(pClipper        == NULL);	/* Don't want to recreate over a valid object */

  	/* Create the clipper object for the primary surface */
  Result = pDirectDraw->CreateClipper(0, &pClipper, NULL);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to create the DirectDraw clipper object!");
    return (FALSE);
   }

	/* Set the window handle associated with the clipper */
  Result = pClipper->SetHWnd(0, hMainWindow);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to initialize the DirectDraw clipper object!");
    return (FALSE);
   }
  
	/* Set the clipper to the primary surface */      
  Result = pPrimarySurface->SetClipper(pClipper);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to set primary surface clipper object!");
    return (FALSE);
   }
    
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDDSurfaceMode::CreateClipper()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDDSurfaceMode::CreateDirectDraw()"
/*===========================================================================
 *
 * Class CDDSurfaceMode Method - boolean CreateDirectDraw (void);
 *
 * Initializes the DirectDraw object for the surface mode.  Returns FALSE
 * on any error.  Protected class method.
 *
 *=========================================================================*/
boolean CDDSurfaceMode::CreateDirectDraw (void) {
  HRESULT Result;

	/* Ensure valid input */
  ASSERT(pDirectDraw == NULL);	/* Don't want to reinitialize over a valid object */
  ASSERT(pParentApp  != NULL);	/* We need a valid parent application object */
  
	/* Merely use the DirectDraw object used by the main application if
	 * it is valid. */
  if (pParentApp->GetDirectDraw() == NULL) {
    pDirectDraw = pParentApp->GetDirectDraw();
    ReferencedDDraw = TRUE;
    return (TRUE);
   }
	
	/* Create the main DirectDraw object */
  Result = CreateDirectDrawObject(&pDirectDraw);
  if (!Result) return (FALSE);
  ReferencedDDraw = FALSE;
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDDSurfaceMode::CreateDirectDraw()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDDSurfaceMode::CreateSurface()"
/*===========================================================================
 *
 * Class CDDSurfaceMode Method - boolean CreateSurface (void);
 *
 * Initializes the DirectDraw Primary/Back surfaces for the surface mode. 
 * Returns FALSE on any error.  Protected class method.
 *
 *=========================================================================*/
boolean CDDSurfaceMode::CreateSurface (void) {
  HRESULT   Result;
  ddsdesc_t SurfaceDesc;

	/* Ensure valid input */
  ASSERT(pDirectDraw != NULL);
  ASSERT(pParentApp  != NULL);
  
  	/* Merely use the application's surfaces if they are valid */
  if (pParentApp->GetPrimarySurface() != NULL) {
    pPrimarySurface = pParentApp->GetPrimarySurface();
    ReferencedDDSurface = TRUE;
    return (TRUE);
   }

	/* Set the primary surface description */
  memset(&SurfaceDesc, 0, sizeof(SurfaceDesc));
  SurfaceDesc.dwSize = sizeof(SurfaceDesc);
  SurfaceDesc.dwFlags = DDSD_CAPS;
  SurfaceDesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
  ReferencedDDSurface = FALSE;

  	/* Attempt to create the primary surface */
  Result = pDirectDraw->CreateSurface(&SurfaceDesc, &pPrimarySurface, NULL);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to create the primary surface!");
    return (FALSE);
   }

	/* Create the clipper object and attach it to the surface */
  Result = CreateClipper();
  return ((boolean) Result);
 }
/*===========================================================================
 *		End of Class Method CDDSurfaceMode::CreateSurface()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDDSurfaceMode::GetDisplayDC()"
/*===========================================================================
 *
 * Class CDDSurfaceMode Method - boolean GetDisplayDC (hDC);
 *
 * Attempts to retrieve the Device Context handle to the main display.
 * Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDDSurfaceMode::GetDisplayDC (HDC& hDC) { 
  HRESULT Result;

	/* Ensure valid objects */
  ASSERT(pPrimarySurface != NULL);

	/* Get the DC for the primary surface */
  Result = pPrimarySurface->GetDC(&hDC);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to retreive the device context of primary surface!"); 
    return (FALSE);
   }

  return (TRUE);
}
/*===========================================================================
 *		End of Class Method CDDSurfaceMode::GetDisplayDC()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDDSurfaceMode::GetDisplayRect()"
/*===========================================================================
 *
 * Class CDDSurfaceMode Method - boolean GetDisplayRect (Rect);
 *
 * Attempts to retrieve the rectangle defined by the main display.
 * Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDDSurfaceMode::GetDisplayRect (RECT& Rect) { 
  boolean Result;
  POINT   OffsetPoint = { 0, 0 };

	/* Ensure valid objects */
  ASSERT(hMainWindow != NULL);

	/* Get the size of the main window client area */
  Result = GetClientRect(hMainWindow, &Rect);

  if (!Result) {
    SET_WIN_ERROR2("Failed to retreive the size of the main window!"); 
    return (FALSE);
   }

	/* Get the position of the window on the screen */
  Result = ClientToScreen(hMainWindow, &OffsetPoint);

  if (!Result) {
    SET_WIN_ERROR2("Failed to retreive the position of the main window!"); 
    return (FALSE);
   }

	/* Shift the rectangle to get the proper location/size */
  Result = OffsetRect(&Rect, OffsetPoint.x, OffsetPoint.y);

  if (!Result) {
    SET_WIN_ERROR2("Failed to offset the rectangle!"); 
    return (FALSE);
   }

  return (TRUE);
}
/*===========================================================================
 *		End of Class Method CDDSurfaceMode::GetDisplayRect()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDDSurfaceMode::HandleMessages()"
/*===========================================================================
 *
 * Class CDDSurfaceMode Method - boolean HandleMessages (hWindow, Message, wParam, lParam);
 *
 * Handles any general window messages for the DD surface mode. Returns FALSE
 * if the message wasn't handled.
 *
 *=========================================================================*/
boolean CDDSurfaceMode::HandleMessages (LRESULT& lResult, HWND hWindow, UINT Message, WPARAM wParam, LPARAM lParam) {
    
	/* Ignore if not initialized. The default window procedure
	 * should be called. */
  if (!IsInitialized()) return (FALSE);

  switch (Message) {
  
		/* Keyup/down messages */
    case WM_KEYUP:
      return OnKeyUp(lResult, wParam);
    case WM_KEYDOWN:
      return OnKeyDown(lResult, wParam);

		/* Mouse messages */
    case WM_LBUTTONDOWN:
      return OnLButtonDown(lResult, wParam, LOWORD(lParam), HIWORD(lParam));

		/* Window was resized or should be repainted */
    case WM_SIZE:
    case WM_PAINT: 
      Update();
      lResult = 0;
      /* Fall thru to D3D handlers purposely */
   }

  return (FALSE);
 }
/*===========================================================================
 *		End of Class Method CDDSurfaceMode::HandleMessage()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDDSurfaceMode::InitSurfaceMode()"
/*===========================================================================
 *
 * Class CDDSurfaceMode Method - boolean InitSurfaceMode (hWindow, pApp);
 *
 * Initializes the DirectDraw objects for the surface mode.  Returns FALSE
 * on any error.
 *
 *=========================================================================*/
boolean CDDSurfaceMode::InitSurfaceMode (HWND hWindow, CD3DApp* pApp) {
  HRESULT   Result;

	/* Ignore if already setup */
  if (IsInitialized()) return (TRUE);

	/* Ensure valid input */
  ASSERT(hWindow != NULL);
  ASSERT(pApp    != NULL);

	/* Save the window handle and parent application */
  hMainWindow = hWindow;
  SetParent(pApp);
  
	/* Create the direct draw objects */
  Result = CreateDirectDraw();
  if (Result) Result = CreateSurface();
  if (!Result) return (FALSE);
  
	/* Save handles and return success */
  Initialized = TRUE;
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDDSurfaceMode::InitSurfaceMode()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDDSurfaceMode::ReleaseDisplayDC()"
/*===========================================================================
 *
 * Class CDDSurfaceMode Method - boolean ReleaseDisplayDC (hDC);
 *
 * Attempts to release the Device Context handle to the main display as 
 * retrieved by GetDisplayDC(). Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDDSurfaceMode::ReleaseDisplayDC (HDC hDC) { 
  HRESULT Result;

	/* Ensure valid objects */
  ASSERT(pPrimarySurface != NULL);

	/* Release the DC of the primary surface */
  Result = pPrimarySurface->ReleaseDC(hDC);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to release the device context of primary surface!"); 
    return (FALSE);
   }

  return (TRUE);
}
/*===========================================================================
 *		End of Class Method CDDSurfaceMode::GetDisplayDC()
 *=========================================================================*/