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

	/* Include Files */
#include "dfd3dapp.h"
#include "dfcommon_d3d.h"


BEGIN_COMMAND_FUNCTION(CDFD3DApp)
END_COMMAND_FUNCTION()


#undef  __FUNC__
#define __FUNC__ "CDFD3DApp::CDFD3DApp()"
/*===========================================================================
 *
 * Class CDFD3DApp Constructor
 *
 *=========================================================================*/
CDFD3DApp::CDFD3DApp() {

	/* Set DF object default parameters */
  //LandBuilder.SetDetailFactor(DF_DEFAULT_LANDDETAILFACTOR);

	/* Initialize D3D objects */
  pSunFrame = NULL;
  pMainFrame = NULL;
  pSunLight = NULL;
  pAmbientLight = NULL;

	/* Set default parameters */
  MovementSpeed  = DF_DEFAULT_MOVEMENTSPEED;
  ViewBackLength = DF_DEFAULT_VIEWBACKLENGTH;
  FogColor       = D3DRGB(1.0,1.0,1.0);
  FogMode	 = D3DRMFOG_LINEAR;
  FogEnable      = TRUE;
  FogFrontPlane  = DF_DEFAULT_FOGFRONTPLANE;
  FogDensity     = DF_DEFAULT_FOGDENSITY;

  OriginPosition.x = D3DVAL(0.0);
  OriginPosition.y = D3DVAL(0.0);
  OriginPosition.z = D3DVAL(0.0);
  RelativeOriginPosition.x = D3DVAL(0.0);
  RelativeOriginPosition.y = D3DVAL(0.0);
  RelativeOriginPosition.z = D3DVAL(0.0);

  CameraHeight = DF_DEFAULT_CAMERAHEIGHT;
 }
/*===========================================================================
 *		End of Class CDFD3DApp Constructor
 *=========================================================================*/


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

	/* Destroy the land builder object first */
  LandBuilder.Destroy();

	/* Unallocate D3D objects */
  RELEASE(pSunFrame);
  RELEASE(pMainFrame);
  RELEASE(pSunLight);
  RELEASE(pAmbientLight);

	/* Call the base class method */
  CD3DApp::Destroy();  
 }
/*===========================================================================
 *		End of Class CDFD3DApp Destructor
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFD3DApp::AbsToRelMapPosition()"
/*===========================================================================
 *
 * Class CDFD3DApp Method - void AbsToRelMapPosition (Vector);
 *
 * Converts a absolute map position vector to an relative map position in 
 * DF map coordinates.
 *
 *=========================================================================*/
void CDFD3DApp::AbsToRelMapPosition (d3dvector_t& Vector) {
  Vector.x -= DFPixelToMapX(GetOriginXPixel());
  Vector.z = DFPixelToMapZ(GetOriginYPixel()) - Vector.z;
 }
/*===========================================================================
 *		End of Class Method CDFD3DApp::AbsToRelMapPosition()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFD3DApp::CreateFog()"
/*===========================================================================
 *
 * Class CDFD3DApp Method - boolean CreateFog (void);
 *
 * Creates and initializes the fog settings for the scene.  Returns
 * TRUE on success or FALSE on any error.
 *
 *=========================================================================*/
boolean CDFD3DApp::CreateFog (void) {

	/* Ensure a valid scene object */
  ASSERT(pScene    != NULL);

  pScene->SetSceneFogMode(FogMode);
  pScene->SetSceneFogColor(FogColor);
  pScene->SetSceneFogEnable(FogEnable);
  pScene->SetSceneFogParams(FogFrontPlane, ViewBackLength, FogDensity);

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFD3DApp::CreateFog();
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFD3DApp::CreateMainFrame()"
/*===========================================================================
 *
 * Class CDFD3DApp Method - boolean CreateMainFrame (void);
 *
 * Creates and initializes the main frame.  Returns
 * TRUE on success or FALSE on any error.
 *
 *=========================================================================*/
boolean CDFD3DApp::CreateMainFrame (void) {
  HRESULT Result;

	/* Ensure valid objects */
  ASSERT(pDirect3D  != NULL);
  ASSERT(pScene     != NULL);
  ASSERT(pMainFrame == NULL);
  
	/* Create the frame object */
  Result = pDirect3D->CreateFrame(pScene, &pMainFrame);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Error creating the main reference frame!");
    return (FALSE); 
   }

	/* Rotate 180 degrees about the Y-axis */
  Result = pMainFrame->SetOrientation(pScene, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(1.0), 
					      D3DVAL(0.0), D3DVAL(-1.0), D3DVAL(0.0));

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Error setting the main frame orientation!");
    return (FALSE); 
   } 

	/* Set the position of the sub-frame relative to the scene */
  Result = pMainFrame->SetPosition(pScene, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0));

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Error setting main frame position!");
    return (FALSE); 
   }

	/* Set default scene background color to something 'nice' */
  pScene->SetSceneBackgroundRGB(D3DVAL(0.4), D3DVAL(0.4), D3DVAL(0.7));

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFD3DApp::CreateMainFrame()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFD3DApp::CreateSun()"
/*===========================================================================
 *
 * Class CDFD3DApp Method - boolean CreateSun (void);
 *
 * Creates and initializes the sunlight frame and sun light source.  Returns
 * TRUE on success or FALSE on any error.
 *
 *=========================================================================*/
boolean CDFD3DApp::CreateSun (void) {
  HRESULT Result;

	/* Ensure valid objects */
  ASSERT(pDirect3D != NULL);
  ASSERT(pScene    != NULL);
  ASSERT(pSunFrame == NULL);

  	/* Create a sub-frame under the main scene */	
  Result = pDirect3D->CreateFrame(pScene, &pSunFrame);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Error creating the Sun reference frame!");
    return (FALSE); 
   }

    	/* Set the sub-frame rotation */
  Result = pSunFrame->SetRotation(pScene, D3DVAL(1.0), D3DVAL(0.0), D3DVAL(0.0), DF_SUN_ROTATION_RATE*10);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Error setting the Sun frame rotation!");
    return (FALSE); 
   }
   
   	/* Create a directional light source (the sun) */
  Result = pDirect3D->CreateLightRGB(D3DRMLIGHT_DIRECTIONAL, D3DVAL(1.0), D3DVAL(1.0), D3DVAL(1.0), &pSunLight);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Error creating the SunLight object!");
    return (FALSE); 
   }

	/* Create an ambient light source */
  Result = pDirect3D->CreateLightRGB(D3DRMLIGHT_AMBIENT, D3DVAL(0.5), D3DVAL(0.5), D3DVAL(0.5), &pAmbientLight);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Error creating the AmbientLight object!");
    return (FALSE); 
   }

	/* Add the sunlight to the light light frame */
  Result = pSunFrame->AddLight(pSunLight);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Error adding Sunlight to the sun frame!");
    return (FALSE); 
   }

	/* Add the ambient light to the light sub-frame frame */
  Result = pSunFrame->AddLight(pAmbientLight);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Error adding the AmbientLight to the sun frame!");
    return (FALSE); 
   }

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFD3DApp::CreateSun()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFD3DApp::DFInitialization()"
/*===========================================================================
 *
 * Class CDFD3DApp Method - boolean DFInitialization (void);
 *
 * Initializes the required DF components of the application.  Returns
 * FALSE on any error.
 *
 *=========================================================================*/
boolean CDFD3DApp::DFInitialization (void) {
  HRESULT   Result;
  D3DVECTOR CameraVector;

	/* Create the main DF object frame */
  Result = CreateMainFrame();
  if (Result) Result = CreateSun();
  if (Result) Result = CreateFog();
  if (!Result) return (FALSE);

	/* Preload the map tables */
  Result = DFMaps.Read(DFMAP_REGION_MINIMUM);
  if (!Result) return (FALSE);

	/* Set the object references for the land builder */
  LandBuilder.SetParentFrame(pMainFrame);

	/* Retrieve the camera position */
  Result = pCamera->GetPosition(pMainFrame, &CameraVector);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to get the camera position!");
    return (FALSE);
   }

	/* Set the position of the camera */
  //Result = SetOriginPosition (D3DVAL(547.0*32400.0 + 120.0), D3DVAL(-350.0), D3DVAL((500-48)*32400.0 + 56));
  //Result = SetOriginPosition (D3DVAL(19866762), D3DVAL(-350.0), D3DVAL(3796973));
  Result = SetOriginPosition (D3DVAL(20101777), D3DVAL(-350.0), D3DVAL(3850125));
  if (!Result) return (FALSE);

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFD3DApp::DFInitialization()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFD3DApp::HandleMessages()"
/*===========================================================================
 *
 * Class CDFD3DApp Method - long HandleMessages (&lResult, hWindow, Message, wParam, lParam);
 *
 * Handles any general window messages.
 *
 *=========================================================================*/
boolean CDFD3DApp::HandleMessages (LRESULT& Result, HWND hWindow, UINT Message, WPARAM wParam, LPARAM lParam) {
  boolean bResult;

	/* Call the base class first */
  bResult = CD3DApp::HandleMessages(Result, hWindow, Message, wParam, lParam);
  if (bResult) return (TRUE);

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


#undef  __FUNC__
#define __FUNC__ "CDFD3DApp::Initialize()"
/*===========================================================================
 *
 * Class CDFD3DApp Method - boolean Initialize (ThisInstance, CommandShow);
 *
 * Standard intialization function.  Performs all the initialization
 * needed to get the app running.
 *
 *=========================================================================*/
boolean CDFD3DApp::Initialize (HINSTANCE ThisInstance, int CommandShow) {
  boolean Result;

	/* Call the base class method first */
  Result = CD3DApp::Initialize(ThisInstance, CommandShow);
  if (!Result) return (FALSE);

	/* Perform the Daggerfall specific Direct3D initialization */
  Result = DFInitialization();
  Initialized = TRUE;

  return (Result);
 }
/*===========================================================================
 *		End of Class Method CdfD3DApp::Initialize()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFD3DApp::OnKeyDown()"
/*===========================================================================
 *
 * Class CD3DApp Event - boolean OnKeyDown (lResult, KeyDown);
 *
 * Called when a key is pressed
 *
 *=========================================================================*/
boolean CDFD3DApp::OnKeyDown (LRESULT& lResult, int KeyCode) {

 
	/* Call the base class */
  return (CD3DApp::OnKeyDown(lResult, KeyCode));
 }      
/*===========================================================================
 *		End of Class Event CDFD3DApp::OnKeyDown()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFD3DApp::OnKeyUp()"
/*===========================================================================
 *
 * Class CD3DApp Event - boolean OnKeyUp (lResult, KeyCode);
 *
 * Called when a key is pressed
 *
 *=========================================================================*/
boolean CDFD3DApp::OnKeyUp (LRESULT& lResult, int KeyCode) {
  lResult = 0;
  
  switch (KeyCode) {
    case VK_ESCAPE:
      if (pCurrentSurfaceMode == NULL) break;
      RemoveSurfaceMode();
      return (TRUE);
    case 'M':
      SetSurfaceMode(&DFTravelMapDDSM);
      return (TRUE);
   }
        
	/* Call the base class */
  return (CD3DApp::OnKeyUp(lResult, KeyCode));
 }      
/*===========================================================================
 *		End of Class Event CDFD3DApp::OnKeyUp()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDFD3DApp Method - void RelToAbsMapPosition (Vector);
 *
 * Converts a relative map position vector to an absolute map position in 
 * DF map coordinates.
 *
 *=========================================================================*/
void CDFD3DApp::RelToAbsMapPosition (d3dvector_t& Vector) {
  Vector.x += DFPixelToMapX(GetOriginXPixel());
  Vector.z = DFPixelToMapZ(GetOriginYPixel()) - Vector.z;
 }
/*===========================================================================
 *		End of Class Method CDFD3DApp::RelToAbsMapPosition()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDFD3DApp Method - boolean Render (void);
 *
 * Updates the window with one frame.
 *
 *=========================================================================*/
boolean CDFD3DApp::Render (void) {
  HRESULT   Result;
  D3DVECTOR CameraVector;

	/* Ensure we should be rendering now */
  if (!CanRenderScene()) return (FALSE);

	/* Call the base class member first */
  Result = CD3DApp::Render();
  if (!Result) return (FALSE);

	/* Retrieve the camera position */
  Result = pCamera->GetPosition(pMainFrame, &CameraVector);

  if (FAILED(Result)) {
    SET_D3D_ERROR2(Result, "Failed to get the camera position!");
    return (FALSE);
   }

	/* Set the origin position for the land builder */
  RelToAbsMapPosition(CameraVector);
  Result = SetOriginPosition (CameraVector.x, CameraVector.y, CameraVector.z);
  if (!Result) return (FALSE);

  HDC	   hDC;
  char     Buffer[64];
  int      BufferSize;

  hDC = GetDC(hMainWindow);

  if (hDC) { 
    BufferSize = sprintf (Buffer, "%03d, %03d (%.0f, %.0f, %.0f)", LandBuilder.GetOriginXPixel(), LandBuilder.GetOriginYPixel(), CameraVector.x, CameraVector.y, CameraVector.z);
    TextOut(hDC, 10, 10, Buffer, BufferSize);
    ReleaseDC(hMainWindow, hDC);
   }

  return (TRUE);
 }
/*===========================================================================
 *		End of Class CDFD3DApp::Render()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDFD3DApp Method - boolean SetOriginPosition (X, Y, Z);
 *
 * Sets the position of the origin in absolute DF coordinates.
 *
 *=========================================================================*/
boolean CDFD3DApp::SetOriginPosition (d3dvalue_t X, d3dvalue_t Y, d3dvalue_t Z) {
  HRESULT Result;

	/* Ensure valid objects */
  if (pCamera == NULL) {
    SET_EXT_ERROR(ERR_NULL);
    return (FALSE);
   }

	/* Set the absolute position */
  OriginPosition.x = X;
  OriginPosition.y = Y;
  OriginPosition.z = Z;
  RelativeOriginPosition = OriginPosition;

	/* Compute the relative position */
  AbsToRelMapPosition(RelativeOriginPosition);

	/* Change the landbuilder origin position */
  LandBuilder.SetOriginPosition (X, Y, Z);

	/* Do we need to update the landscape? */
  if (LandBuilder.HasOriginPixelChanged()) {

		/* Recreate the landscape */
    ResetLastRender();
    Result = LandBuilder.CreateLandscape();
    if (!Result) return (FALSE);

		/* Compute the relative position */
    RelativeOriginPosition = OriginPosition;
    AbsToRelMapPosition(RelativeOriginPosition);

		/* Change the camera postion */
    Result = pCamera->SetPosition (pMainFrame, RelativeOriginPosition.x, RelativeOriginPosition.y, RelativeOriginPosition.z);

    if (FAILED(Result)) {
      SET_D3D_ERROR2(Result, "Failed to set the camera position!");
      return (FALSE);
     }
   }

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFD3DApp::SetOriginPosition()
 *=========================================================================*/