#include <assert.h>
#include "SM_D3D.h"
#include "SM_Leaks.h"
#include "SM_Main.h"

//#define DEVICETYPE D3DDEVTYPE_REF 
#define DEVICETYPE D3DDEVTYPE_HAL


namespace SM_D3d
{
  IDirect3D8*           g_pD3D=0;  
  IDirect3DDevice8*     g_pd3dDevice=0;
  D3DDISPLAYMODE        g_dmDisplayMode;
  D3DPRESENT_PARAMETERS g_dppPrensentParameters;
  DWORD                 g_dwWindowStyle;
  int                   g_iCurrentMode;
  HWND                  g_hwnd;

  enum DepthStencilFlags
  {
    DZ_D16_LOCKABLE=1,
    DZ_D32         =2,
    DZ_D15S1       =4,
    DZ_D24S8       =8,
    DZ_D16         =16,
    DZ_D24X8       =32,
    DZ_D24X4S4     =64,
    DZ_ALL         =DZ_D16_LOCKABLE|DZ_D32|DZ_D15S1|DZ_D24S8|DZ_D16|DZ_D24X8|DZ_D24X4S4,
    DZ_STENCIL     =DZ_D24S8,
    DZ_MODES       =7,
  };

  struct   IRestorableNode
  {
    IRestorable*     m_pRestorable;
    IRestorableNode* m_pNext;
  };
    

  // Modes Info
  D3DModeDescriptor*        g_pAvailableModes=0;
  unsigned                  g_uModes=0;

  // Display
  int InitDisplay(unsigned uMode, bool bStencil, bool bFullscreen, bool bAntialias, bool bWaitRetrace, bool bUpdateWindow, bool     bLockable);

  IRestorableNode*          g_pRestorableList;


  int               AttemptRestore();        
  void              AdjustWindow(bool bFullscreen);
  int               InitCache();

IDirect3D8*        D3D()
{
  return (g_pD3D);
}

IDirect3DDevice8* Device()
{
  return (g_pd3dDevice);
}


int Init()
{
  g_pD3D=Direct3DCreate8(D3D_SDK_VERSION);

  if (!g_pD3D)
  {
    return (-1);
  }
 
  EnumerateModes();

  assert(g_pD3D);

  return (0);
}

int Shutdown()
{
  assert(!g_pd3dDevice);
  assert(!g_pRestorableList);

  if (g_pAvailableModes)
  {
    delete[] g_pAvailableModes;
    g_pAvailableModes=0;
    g_uModes=0;
  }

  if (g_pD3D)
  {
    g_pD3D->Release();
    g_pD3D=0;
  }

  return (0);
}

int InitDisplay(HWND hwnd, unsigned uWidth, unsigned uHeight, unsigned uBPP, bool bStencil, bool bFullscreen, int (*Requirements)(int iMode), char* pcRequirement, bool bAntialias, bool bWaitRetrace, bool bUpdateWindow, bool     bLockable)
{
  // Search candidate in available modes
  bool     bFound=false;
  unsigned uBestCandidate;
  
  
  g_hwnd = hwnd;

  D3DModeDescriptor* pMode=GetModes();  
  for (unsigned i=0 ; i<AvailableModes() ; i++)
  {
    if (pMode[i].uWidth ==uWidth  &&
        pMode[i].uHeight==uHeight &&
        pMode[i].uBPP   ==uBPP)
    {
      // Has HAL in selected mode?
      if ( (bFullscreen==false && pMode[i].bWindowed)   ||
           (bFullscreen==true && pMode[i].bFullscreen) )
      {
        // Has Depth Buffer and stencil
        if (pMode[i].iDepthStencilFlags & (bStencil?DZ_STENCIL:DZ_ALL))
        {
          if (Requirements(i))
          {
            if (bFound)
            {
              // Has more Hz than previous mode?
              if (pMode[i].uRefresh>pMode[uBestCandidate].uRefresh)
              {
                uBestCandidate=i;
              }
            }
            else
            {
              // Mark as candidate
              bFound=true;
              uBestCandidate=i;
            }
          }
        }
      }
    }
  }

  if (!bFound)
  {
    SM_Main::OutputError("Requested mode %ix%ix%i (Fullscreen=%s) (%s) not available", uWidth, uHeight, uBPP, bFullscreen?"TRUE":"FALSE", pcRequirement);
    return (-1);
  }
  else
  {
    return (InitDisplay(uBestCandidate, bStencil, bFullscreen, bAntialias, bWaitRetrace, bUpdateWindow, bLockable));
  }
}

int FindBestDepthStencil(int iAdapter, D3DDEVTYPE DeviceType, D3DFORMAT TargetFormat, bool bStencil, D3DFORMAT* pDepthStencilFormat)
{
  #define CANUSE(Z_FORMAT, DEVICE)\
  if (D3D()->CheckDeviceFormat(iAdapter, DEVICE, TargetFormat, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, Z_FORMAT)==D3D_OK)\
  {\
    if (D3D()->CheckDepthStencilMatch(iAdapter, DEVICE, TargetFormat, TargetFormat, Z_FORMAT)==D3D_OK)\
    {\
      *pDepthStencilFormat=Z_FORMAT;\
      return (0);\
    }\
  }                    

  if (bStencil)
  {
    CANUSE(D3DFMT_D24S8, DeviceType);
  }
  else
  {
    CANUSE(D3DFMT_D32, DeviceType);
    CANUSE(D3DFMT_D24X8, DeviceType);
    CANUSE(D3DFMT_D16, DeviceType);
  }
  
  return (-1);
}



int InitDisplay(unsigned uMode, 
                bool     bStencil,                 
                bool     bFullscreen,
                bool     bAntialias,
                bool     bWaitRetrace,
                bool     bUpdateWindow,
                bool     bLockable)
{
  g_dwWindowStyle       = GetWindowLong( g_hwnd, GWL_STYLE );

  g_dmDisplayMode.Width =GetModes()[uMode].uWidth;
  g_dmDisplayMode.Height=GetModes()[uMode].uHeight;
  g_dmDisplayMode.RefreshRate=GetModes()[uMode].uRefresh;

  

  // Initialize display
  memset(&g_dppPrensentParameters, 0, sizeof(D3DPRESENT_PARAMETERS));

  if (!bFullscreen)
  {
    // Pick pixel format of current display mode
    D3DDISPLAYMODE d3ddm;
    if( FAILED( g_pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm ) ) )
    {
      SM_Main::OutputError("Failed to get adapter display/mode");
      return -1;
    }

    g_dmDisplayMode.Format=d3ddm.Format;    

    if (FindBestDepthStencil(0, DEVICETYPE, g_dmDisplayMode.Format, bStencil, &g_dppPrensentParameters.AutoDepthStencilFormat)==-1)
    {
      SM_Main::OutputError("Failed to to find adequate depth stencil");
      return (-1);
    }    
  }
  else
  {
    // Use user's desired pixel format
    g_dmDisplayMode.Format=GetModes()[uMode].d3dFormat;

    if (FindBestDepthStencil(GetModes()[uMode].uDevice, DEVICETYPE, g_dmDisplayMode.Format, bStencil, &g_dppPrensentParameters.AutoDepthStencilFormat)==-1)
    {
      SM_Main::OutputError("Failed to to find adequate depth/stencil");
      return (-1);
    }    
  }

  if(bAntialias && SUCCEEDED(g_pD3D->CheckDeviceMultiSampleType( D3DADAPTER_DEFAULT, 
                                                DEVICETYPE , g_dmDisplayMode.Format, 
                                                FALSE, D3DMULTISAMPLE_4_SAMPLES ) ) )
  {
    bAntialias=true;
  }
  else
  {
    bAntialias=false;
  }
    

  g_dppPrensentParameters.Windowed   = bFullscreen?FALSE:TRUE;
  g_dppPrensentParameters.hDeviceWindow=g_hwnd;
  g_dppPrensentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
  g_dppPrensentParameters.BackBufferFormat = g_dmDisplayMode.Format;
  g_dppPrensentParameters.BackBufferCount  = 1;
  g_dppPrensentParameters.EnableAutoDepthStencil=true;

  if (bAntialias)
  {
    g_dppPrensentParameters.MultiSampleType = D3DMULTISAMPLE_4_SAMPLES;
  }

  if (bFullscreen)
  {
    g_dppPrensentParameters.BackBufferWidth=g_dmDisplayMode.Width;
    g_dppPrensentParameters.BackBufferHeight=g_dmDisplayMode.Height;
  }

  g_dppPrensentParameters.EnableAutoDepthStencil=TRUE;
  g_dppPrensentParameters.Flags = bLockable?D3DPRESENTFLAG_LOCKABLE_BACKBUFFER:0; 
  
  if (!bFullscreen)
  {
    g_dppPrensentParameters.FullScreen_RefreshRateInHz=0;
    g_dppPrensentParameters.FullScreen_PresentationInterval=0;
  }
  else
  {
    g_dppPrensentParameters.FullScreen_RefreshRateInHz=GetModes()[uMode].uRefresh;

    if (bWaitRetrace)
    {
      g_dppPrensentParameters.FullScreen_PresentationInterval=D3DPRESENT_INTERVAL_ONE;
    }
    else
    {
      g_dppPrensentParameters.FullScreen_PresentationInterval=D3DPRESENT_INTERVAL_IMMEDIATE;
    }
    
  }
  

  assert(g_pD3D);
 
  if (bUpdateWindow)
  {
    AdjustWindow(bFullscreen);
  }

   
  bool bTNLDevice=GetModes()[uMode].dcDeviceCaps.DevCaps&D3DDEVCAPS_HWTRANSFORMANDLIGHT?true:false;
  //bTNLDevice=false;
  if (FAILED( g_pD3D->CreateDevice( GetModes()[uMode].uDevice, 
                                    DEVICETYPE, 
                                    g_hwnd,
                                    bTNLDevice?
                                      D3DCREATE_MIXED_VERTEXPROCESSING:
                                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                    &g_dppPrensentParameters, &g_pd3dDevice ) ) )
  {
    return (-1);
  }

  InitCache();

  g_iCurrentMode=uMode;

  g_pAvailableModes[g_iCurrentMode].bStencil = bStencil?1:0;


  return (0);
}

D3DCAPS8* const DeviceCaps()
{
  if (g_pd3dDevice)
  {
    return (&GetModes()[g_iCurrentMode].dcDeviceCaps);
  }
  else
  {
    return (0);
  }
}

int ShutdownDisplay()
{
  if (g_pd3dDevice)
  {
    g_pd3dDevice->Release();
    g_pd3dDevice=0;
  }

  g_iCurrentMode=-1;

  return (0);
}


int StartFrame()
{
  assert(g_pd3dDevice);

  HRESULT hr;
  hr=g_pd3dDevice->TestCooperativeLevel();
  switch (hr)
  {
  case D3D_OK:
    break;
  case D3DERR_DEVICELOST:
    return (-1);
    break;
  case D3DERR_DEVICENOTRESET:
    if (AttemptRestore()!=0)
    {
      return (-1);
    }
    break;
  default:
    assert(0);
  }    
  return (0);
}

int EndFrame()
{
  assert(g_pd3dDevice);
  return (0);
}

int AttemptRestore()
{
  IRestorableNode* pNode;
  
  for (pNode=g_pRestorableList ; pNode ; pNode=pNode->m_pNext)
  {
    if (pNode->m_pRestorable->RestoreShut()!=0)
    {
      return (-1);
    }
  }

  D3DDISPLAYMODE dmDisplayMode;
  memcpy(&dmDisplayMode, &g_dmDisplayMode, sizeof(dmDisplayMode));
  
  if (FAILED(g_pd3dDevice->Reset(&g_dppPrensentParameters)))
  {
    return (-1);
  }
  
  for (pNode=g_pRestorableList ; pNode ; pNode=pNode->m_pNext)
  {
    if (pNode->m_pRestorable->RestoreInit()!=0)
    {
      return (-1);
    }
  }
  
  InitCache();
  return (0);
}

int ToggleFullScreen()
{
  g_dppPrensentParameters.Windowed=!g_dppPrensentParameters.Windowed;
  AdjustWindow(g_dppPrensentParameters.Windowed?true:false);
  if (!g_dppPrensentParameters.Windowed)
  {
    g_dppPrensentParameters.BackBufferWidth=g_dmDisplayMode.Width;
    g_dppPrensentParameters.BackBufferHeight=g_dmDisplayMode.Height;
  }
  else
  {
    g_dppPrensentParameters.BackBufferWidth=g_dmDisplayMode.Width;
    g_dppPrensentParameters.BackBufferHeight=g_dmDisplayMode.Height;
  }

  if (AttemptRestore()!=0)
  {
    return (-1);
  }  

  return (0);
}

void AdjustWindow(bool bFullscreen)
{
  if (!bFullscreen)
  {
    RECT rectWindow;
	  RECT rectClient;
	  long lCoorxWin, lCooryWin;

    
	  GetWindowRect(g_hwnd, &rectWindow);
	  GetClientRect(g_hwnd, &rectClient);

	  rectClient.right -= g_dmDisplayMode.Width;
	  rectClient.bottom-= g_dmDisplayMode.Height;
	  rectWindow.right -= (rectClient.right - rectClient.left);
	  rectWindow.bottom -= (rectClient.bottom - rectClient.top);

	  
	  // intenta centrar la ventana
	  lCoorxWin = (GetSystemMetrics(SM_CXSCREEN) - g_dmDisplayMode.Width) /2;
	  lCooryWin = (GetSystemMetrics(SM_CYSCREEN) - g_dmDisplayMode.Height) /2;
	  
	  GetWindowRect(g_hwnd, &rectClient);
	  lCoorxWin = rectClient.left;
	  lCooryWin = rectClient.top;

    LONG lWinLong = GetWindowLong(g_hwnd, GWL_STYLE);
    lWinLong |= (WS_BORDER |WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU);
    //lWinLong =g_dwWindowStyle;

    SetWindowLong(g_hwnd, GWL_STYLE, lWinLong);
    BOOL b=SetWindowPos( g_hwnd, HWND_NOTOPMOST, 
      lCoorxWin, lCooryWin, 
      rectWindow.right-rectWindow.left, rectWindow.bottom-rectWindow.top, 
      SWP_SHOWWINDOW|SWP_FRAMECHANGED );			                      		      
  }
  else
  {
    long lWinLong=GetWindowLong(g_hwnd, GWL_STYLE);;
    lWinLong &= ~(WS_BORDER | WS_CAPTION);
	  SetWindowLong(g_hwnd, GWL_STYLE, lWinLong);
    SetWindowPos( g_hwnd, HWND_TOPMOST,
                  0,0,GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CYSCREEN),
                  SWP_NOZORDER|SWP_NOACTIVATE );
  }  
  RedrawWindow( g_hwnd, 0,0,RDW_FRAME|RDW_INVALIDATE );
}

#define MAXRENDERSSTATE       D3DRS_BLENDOP+1
#define MAXTEXTURESTAGESTATE  D3DTSS_RESULTARG+1             
  

  DWORD dwCachedRenderState      [MAXRENDERSSTATE];
  DWORD dwCachedTextureStageState[8][MAXTEXTURESTAGESTATE];

  HRESULT UpdateRenderState(D3DRENDERSTATETYPE State, DWORD Value)
  {
    dwCachedRenderState[State]=Value;
    return (SM_D3d::Device()->SetRenderState(State, Value));
  }

  HRESULT UpdateTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE State, DWORD Value)
  {
    dwCachedTextureStageState[Stage][State]=Value;
    return (SM_D3d::Device()->SetTextureStageState(Stage, State, Value));
  }
  
  int InitCache()
  {
    assert(SM_D3d::Device());

    float fZero=0.0f;
    float fOne =1.0f;
   

    UpdateRenderState(D3DRS_ZENABLE,                  D3DZB_TRUE);  
    UpdateRenderState(D3DRS_FILLMODE,                 D3DFILL_SOLID);
    UpdateRenderState(D3DRS_SHADEMODE,                D3DSHADE_GOURAUD);
    UpdateRenderState(D3DRS_LINEPATTERN,              0);     
    UpdateRenderState(D3DRS_ZWRITEENABLE,             TRUE);
    UpdateRenderState(D3DRS_ALPHATESTENABLE,          FALSE);
    UpdateRenderState(D3DRS_LASTPIXEL,                TRUE);         
    UpdateRenderState(D3DRS_SRCBLEND,                 D3DBLEND_ONE);        
    UpdateRenderState(D3DRS_DESTBLEND,                D3DBLEND_ZERO);
    UpdateRenderState(D3DRS_CULLMODE,                 D3DCULL_CCW);
    UpdateRenderState(D3DRS_ZFUNC,                    D3DCMP_LESSEQUAL);
    UpdateRenderState(D3DRS_ALPHAREF,                 0);
    UpdateRenderState(D3DRS_ALPHAFUNC,                D3DCMP_ALWAYS);
    UpdateRenderState(D3DRS_DITHERENABLE,             FALSE);
    UpdateRenderState(D3DRS_ALPHABLENDENABLE,         FALSE);
    UpdateRenderState(D3DRS_FOGENABLE,                FALSE);
    UpdateRenderState(D3DRS_SPECULARENABLE,           FALSE);
    UpdateRenderState(D3DRS_ZVISIBLE,                 0);
    UpdateRenderState(D3DRS_FOGCOLOR,                 0);
    UpdateRenderState(D3DRS_FOGTABLEMODE,             D3DFOG_NONE);
    UpdateRenderState(D3DRS_FOGSTART,                 *((DWORD*) &fZero));
    UpdateRenderState(D3DRS_FOGEND,                   *((DWORD*) &fOne));
    UpdateRenderState(D3DRS_FOGDENSITY,               0);
    UpdateRenderState(D3DRS_EDGEANTIALIAS,            FALSE);
    UpdateRenderState(D3DRS_ZBIAS,                    0);
    UpdateRenderState(D3DRS_RANGEFOGENABLE,           FALSE);
    UpdateRenderState(D3DRS_STENCILENABLE,            FALSE);
    UpdateRenderState(D3DRS_STENCILFAIL,              D3DSTENCILOP_KEEP);
    UpdateRenderState(D3DRS_STENCILZFAIL,             D3DSTENCILOP_KEEP);
    UpdateRenderState(D3DRS_STENCILPASS,              D3DSTENCILOP_KEEP);
    UpdateRenderState(D3DRS_STENCILFUNC,              D3DCMP_ALWAYS);
    UpdateRenderState(D3DRS_STENCILREF,               0);
    UpdateRenderState(D3DRS_STENCILMASK,              0xFFFFFFFF);
    UpdateRenderState(D3DRS_STENCILWRITEMASK,         0xFFFFFFFF);
    UpdateRenderState(D3DRS_TEXTUREFACTOR,            0xFFFFFFFF);
    UpdateRenderState(D3DRS_WRAP0,                    0);
    UpdateRenderState(D3DRS_WRAP1,                    0);
    UpdateRenderState(D3DRS_WRAP2,                    0);
    UpdateRenderState(D3DRS_WRAP3,                    0);
    UpdateRenderState(D3DRS_WRAP4,                    0);
    UpdateRenderState(D3DRS_WRAP5,                    0);
    UpdateRenderState(D3DRS_WRAP6,                    0);
    UpdateRenderState(D3DRS_WRAP7,                    0);
    UpdateRenderState(D3DRS_CLIPPING,                 TRUE);
    UpdateRenderState(D3DRS_LIGHTING,                 FALSE);    
    UpdateRenderState(D3DRS_AMBIENT,                  0);     
    UpdateRenderState(D3DRS_FOGVERTEXMODE,            D3DFOG_NONE);
    UpdateRenderState(D3DRS_COLORVERTEX,              FALSE);               
    UpdateRenderState(D3DRS_LOCALVIEWER,              FALSE);
    UpdateRenderState(D3DRS_NORMALIZENORMALS,         FALSE);
    UpdateRenderState(D3DRS_DIFFUSEMATERIALSOURCE,    D3DMCS_COLOR1);
    UpdateRenderState(D3DRS_SPECULARMATERIALSOURCE,   D3DMCS_COLOR2);
    UpdateRenderState(D3DRS_AMBIENTMATERIALSOURCE,    D3DMCS_COLOR2);
    UpdateRenderState(D3DRS_EMISSIVEMATERIALSOURCE,   D3DMCS_MATERIAL);
    UpdateRenderState(D3DRS_VERTEXBLEND,              D3DVBF_DISABLE);
    UpdateRenderState(D3DRS_CLIPPLANEENABLE,          0);
    UpdateRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, FALSE);
    UpdateRenderState(D3DRS_POINTSIZE,                *((DWORD*) &fOne));
    UpdateRenderState(D3DRS_POINTSIZE_MIN,            *((DWORD*) &fOne));
    UpdateRenderState(D3DRS_POINTSPRITEENABLE,        FALSE);
    UpdateRenderState(D3DRS_POINTSCALEENABLE,         FALSE);
    UpdateRenderState(D3DRS_POINTSCALE_A,             *((DWORD*) &fOne));
    UpdateRenderState(D3DRS_POINTSCALE_B,             *((DWORD*) &fOne));
    UpdateRenderState(D3DRS_POINTSCALE_C,             *((DWORD*) &fOne));
    UpdateRenderState(D3DRS_MULTISAMPLEANTIALIAS,     FALSE);
    UpdateRenderState(D3DRS_MULTISAMPLEMASK,          0xFFFFFFFF);
    UpdateRenderState(D3DRS_PATCHEDGESTYLE,           D3DPATCHEDGE_DISCRETE);
    UpdateRenderState(D3DRS_PATCHSEGMENTS,            *((DWORD*) &fOne));
    UpdateRenderState(D3DRS_DEBUGMONITORTOKEN,        D3DDMT_DISABLE);
    UpdateRenderState(D3DRS_POINTSIZE_MAX,            *((DWORD*) &fOne));
    UpdateRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE);
    UpdateRenderState(D3DRS_COLORWRITEENABLE,         0x0000000F);
    UpdateRenderState(D3DRS_TWEENFACTOR,              *((DWORD*) &fZero));
    UpdateRenderState(D3DRS_BLENDOP,                  D3DBLENDOP_ADD);

    int i=0;
    for (i=0 ; i<8 ; i++)  
    {
      UpdateTextureStageState(i,D3DTSS_COLOROP               , D3DTOP_DISABLE);
      UpdateTextureStageState(i,D3DTSS_COLORARG1             , D3DTA_TEXTURE);
      UpdateTextureStageState(i,D3DTSS_COLORARG2             , D3DTA_TEXTURE);
      UpdateTextureStageState(i,D3DTSS_ALPHAOP               , D3DTOP_DISABLE);
      UpdateTextureStageState(i,D3DTSS_ALPHAARG1             , D3DTA_TEXTURE);
      UpdateTextureStageState(i,D3DTSS_ALPHAARG2             , D3DTA_CURRENT);
      UpdateTextureStageState(i,D3DTSS_BUMPENVMAT00          , *((DWORD*) &fZero));
      UpdateTextureStageState(i,D3DTSS_BUMPENVMAT01          , *((DWORD*) &fZero));
      UpdateTextureStageState(i,D3DTSS_BUMPENVMAT10          , *((DWORD*) &fZero));
      UpdateTextureStageState(i,D3DTSS_BUMPENVMAT11          , *((DWORD*) &fZero));
      UpdateTextureStageState(i,D3DTSS_TEXCOORDINDEX         , D3DTSS_TCI_PASSTHRU);
      UpdateTextureStageState(i,D3DTSS_ADDRESSU              , D3DTADDRESS_WRAP);
      UpdateTextureStageState(i,D3DTSS_ADDRESSV              , D3DTADDRESS_WRAP);
      UpdateTextureStageState(i,D3DTSS_BORDERCOLOR           , 0);
      UpdateTextureStageState(i,D3DTSS_MAGFILTER             , D3DTEXF_LINEAR);
      UpdateTextureStageState(i,D3DTSS_MINFILTER             , D3DTEXF_LINEAR); 
      UpdateTextureStageState(i,D3DTSS_MIPFILTER             , D3DTEXF_NONE);
      UpdateTextureStageState(i,D3DTSS_MIPMAPLODBIAS         , *((DWORD*) &fZero));
      UpdateTextureStageState(i,D3DTSS_MAXMIPLEVEL           , 0);
      UpdateTextureStageState(i,D3DTSS_MAXANISOTROPY         , 1);
      UpdateTextureStageState(i,D3DTSS_BUMPENVLSCALE         , *((DWORD*) &fZero));
      UpdateTextureStageState(i,D3DTSS_BUMPENVLOFFSET        , *((DWORD*) &fZero));
      UpdateTextureStageState(i,D3DTSS_TEXTURETRANSFORMFLAGS , D3DTTFF_DISABLE);
      UpdateTextureStageState(i,D3DTSS_ADDRESSW              , D3DTADDRESS_WRAP);
      UpdateTextureStageState(i,D3DTSS_COLORARG0             , D3DTA_TEMP);
      UpdateTextureStageState(i,D3DTSS_ALPHAARG0             , D3DTA_TEMP);
      UpdateTextureStageState(i,D3DTSS_RESULTARG             , D3DTA_CURRENT);

    }

    UpdateTextureStageState(0,D3DTSS_COLOROP               , D3DTOP_MODULATE);
    UpdateTextureStageState(0,D3DTSS_ALPHAOP               , D3DTOP_SELECTARG1);
      

    return (0);
  }

  HRESULT SetRenderState(D3DRENDERSTATETYPE State, DWORD Value)
  {
    if (dwCachedRenderState[State]!=Value)
    {
      return UpdateRenderState(State,Value);
    }
    else
    {
      return (D3D_OK);
    }
  }

  HRESULT SetTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE State, DWORD Value)
  {
    if (dwCachedTextureStageState[Stage][State]!=Value)
    {
      return UpdateTextureStageState(Stage, State, Value);
    }
    else
    {
      return (D3D_OK);
    }
  }

  unsigned AvailableModes()
  {
    assert(g_pAvailableModes);
    return (g_uModes);
  }

  D3DModeDescriptor* const  GetCurrentMode()
  {
    return (&g_pAvailableModes[g_iCurrentMode]);
  }

  D3DModeDescriptor*  const GetModes()
  {
    assert(g_pAvailableModes);
    return (g_pAvailableModes);
  }

  int EnumerateModes()
  {
    assert(SM_D3d::D3D());

    unsigned uAdapters;

    uAdapters=SM_D3d::D3D()->GetAdapterCount();

    unsigned i,j;

    
    g_uModes=0;
    for (i=0 ; i<uAdapters ; i++)
    {
      if (i==D3DADAPTER_DEFAULT)
      {
        g_uModes+=SM_D3d::D3D()->GetAdapterModeCount(i);  
      }
    }

    assert(!g_pAvailableModes);
    g_pAvailableModes=new D3DModeDescriptor[g_uModes];
    if (!g_pAvailableModes)
    {
      return (-1);
    }

    SM_Main::OutputConsole("D3D Enumeration...\n");

    unsigned uCurrentMode=0;
    for (i=0 ; i<uAdapters ; i++)
    {
      if (i==D3DADAPTER_DEFAULT)
      {
        D3DADAPTER_IDENTIFIER8 AdapterIdentifier;

        if (FAILED(SM_D3d::D3D()->GetAdapterIdentifier(i, D3DENUM_NO_WHQL_LEVEL, &AdapterIdentifier)))
        {
          return (-1);
        }

        SM_Main::OutputConsole("Adapter %u is %s\n", 
          i, AdapterIdentifier.Description);
      
  
      
        unsigned uModes=SM_D3d::D3D()->GetAdapterModeCount(i);
        for (j=0 ; j<uModes ; j++, uCurrentMode++)
        {
          D3DDISPLAYMODE Mode;
          if (FAILED(SM_D3d::D3D()->EnumAdapterModes(i, j, &Mode)))
          {
            return (-1);
          }

          unsigned uBPP;

          #define SMD3DFORMAT(FORMAT, BPP) case FORMAT: uBPP=BPP; break;
          switch (Mode.Format)
          {
            #include "SM_D3DFormat.h"
          default:
            assert(0);
          }        

          g_pAvailableModes[uCurrentMode].uDevice  =i;
          g_pAvailableModes[uCurrentMode].uMode    =j;
          g_pAvailableModes[uCurrentMode].uWidth   =Mode.Width;
          g_pAvailableModes[uCurrentMode].uHeight  =Mode.Height;
          g_pAvailableModes[uCurrentMode].uRefresh =Mode.RefreshRate;
          g_pAvailableModes[uCurrentMode].uBPP     =uBPP;        
          g_pAvailableModes[uCurrentMode].d3dFormat=Mode.Format;
          D3D()->GetDeviceCaps(i, DEVICETYPE , &g_pAvailableModes[uCurrentMode].dcDeviceCaps);
          
          // HAL in fullscreen
          g_pAvailableModes[uCurrentMode].bFullscreen=
            FAILED(D3D()->CheckDeviceType(i, DEVICETYPE, Mode.Format, Mode.Format, FALSE))?0:1;

          // HAL in window
          g_pAvailableModes[uCurrentMode].bWindowed=
            FAILED(D3D()->CheckDeviceType(i, DEVICETYPE, Mode.Format, Mode.Format, TRUE))?0:1;

          // Multisample Fullscreen
          g_pAvailableModes[uCurrentMode].bMultisamplingFullscreen=  
            FAILED(D3D()->CheckDeviceMultiSampleType(i, DEVICETYPE, Mode.Format, FALSE, D3DMULTISAMPLE_4_SAMPLES))?0:1;
          
          // Multisample Windowed
          g_pAvailableModes[uCurrentMode].bMultisamplingWindowed=  
            FAILED(D3D()->CheckDeviceMultiSampleType(i, DEVICETYPE, Mode.Format, TRUE, D3DMULTISAMPLE_4_SAMPLES))?0:1;


          // Depth/Stencil formats
          D3DFORMAT DepthBufferTable[DZ_MODES]=
          {
            D3DFMT_D16_LOCKABLE,
            D3DFMT_D32         ,
            D3DFMT_D15S1       ,
            D3DFMT_D24S8       ,
            D3DFMT_D16         ,
            D3DFMT_D24X8       ,
            D3DFMT_D24X4S4     ,         
          };
            
          g_pAvailableModes[uCurrentMode].iDepthStencilFlags=0;
          for (unsigned uZModes=0 ; uZModes<DZ_MODES ; uZModes++)
          {
            if (D3D()->CheckDeviceFormat(i, DEVICETYPE, Mode.Format, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, DepthBufferTable[uZModes])==D3D_OK)
            {
              if (D3D()->CheckDepthStencilMatch(i, DEVICETYPE, Mode.Format, Mode.Format, DepthBufferTable[uZModes])==D3D_OK)
              {
                g_pAvailableModes[uCurrentMode].iDepthStencilFlags|=1<<uZModes;
              }
            }                  
          }

          SM_Main::OutputConsole("%ux%ux%u %3u Hz DEPTH/STENCIL: %08x\n", 
            g_pAvailableModes[uCurrentMode].uWidth,
            g_pAvailableModes[uCurrentMode].uHeight,
            g_pAvailableModes[uCurrentMode].uBPP,
            g_pAvailableModes[uCurrentMode].uRefresh,
            g_pAvailableModes[uCurrentMode].iDepthStencilFlags);      
        }
      }
    }

    return (0);
  }

}


