#include "SM_ddrawPCH.h"
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include "SM_ddraw.h"
#include "SM_Main.h"


namespace SM_DDraw
{
  // Variables
  bool                  m_bInit          =false;
  LPDIRECTDRAW7         m_lpDD           =0;
  LPDIRECTDRAWSURFACE7  m_pddsFrontBuffer=0;
  LPDIRECTDRAWSURFACE7  m_pddsBackBuffer =0;
  LPDIRECTDRAWPALETTE   m_pddpPalette    =0;
  TDisplay              m_Display;
  bool                  m_bDisplayInit   =false;
  bool                  m_bNeedRestore   =false;
  long                  m_lWinLong;


  // Auxiliares
  void GetFromMask(unsigned int& uiShiftLeft, unsigned int& uiShiftRight, unsigned uiMask);
  int  Restore    ();
  void NeedRestore();
  

  // Inicializacion
  int Init()
  {
    m_lWinLong=GetWindowLong(SM_Main::Hwnd(), GWL_STYLE);    
    m_bInit=true;
    return (0);
  }

  int Shutdown()
  {
    if (m_bDisplayInit)
    {
      ShutdownDisplay();
    }
    
    m_bInit=false;

    return (0);
  }

  int InitDisplay(const TDisplay& Display)
  {
    if (!m_bInit)
    {
      assert(0);
      return(-1);
    }
    if (DirectDrawCreateEx(NULL, (void**) &m_lpDD, IID_IDirectDraw7, NULL)!=DD_OK)
    {
      return (-1);
    }

    // Modo cooperativo
    DWORD dwCoopFlags;
    if (Display.m_bFullScreen)
    {
      dwCoopFlags= DDSCL_ALLOWREBOOT|DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN;
    }
    else
    {
      dwCoopFlags= DDSCL_NORMAL;
    }

    if( FAILED( m_lpDD->SetCooperativeLevel( SM_Main::Hwnd(), dwCoopFlags ) ) )
    {
      return (-1);
    }

    // Inicializamos superficies
    DDSURFACEDESC2 ddsd;
    memset(&ddsd, 0, sizeof(ddsd));
    ddsd.dwSize=sizeof(ddsd);

    if (Display.m_bFullScreen)
    {
      long lWinLong=m_lWinLong;
      lWinLong &= ~(WS_BORDER | WS_CAPTION);
		  SetWindowLong(SM_Main::Hwnd(), GWL_STYLE, lWinLong);
      SetWindowPos( SM_Main::Hwnd(), HWND_TOPMOST,
                    0,0,GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CYSCREEN),
                    SWP_NOZORDER|SWP_NOACTIVATE );
    }
    else
    {
      RECT rectWindow;
		  RECT rectClient;
		  long lCoorxWin, lCooryWin;

      
		  GetWindowRect(SM_Main::Hwnd(), &rectWindow);
		  GetClientRect(SM_Main::Hwnd(), &rectClient);

		  rectClient.right -= Display.m_uWidth;
		  rectClient.bottom-= Display.m_uHeight;
		  rectWindow.right -= (rectClient.right - rectClient.left);
		  rectWindow.bottom -= (rectClient.bottom - rectClient.top);

		  
		  // intenta centrar la ventana
		  lCoorxWin = (GetSystemMetrics(SM_CXSCREEN) - Display.m_uWidth) /2;
		  lCooryWin = (GetSystemMetrics(SM_CYSCREEN) - Display.m_uHeight) /2;
		  
		  GetWindowRect(SM_Main::Hwnd(), &rectClient);
		  lCoorxWin = rectClient.left;
		  lCooryWin = rectClient.top;

      LONG lWinLong = GetWindowLong(SM_Main::Hwnd(), GWL_STYLE);
      lWinLong |= (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU);
      SetWindowLong(SM_Main::Hwnd(), GWL_STYLE, lWinLong);
      SetWindowPos( SM_Main::Hwnd(), HWND_NOTOPMOST, lCoorxWin, lCooryWin, rectWindow.right-rectWindow.left, rectWindow.bottom-rectWindow.top, SWP_NOZORDER);			                      		  
    }
    RedrawWindow( SM_Main::Hwnd(), 0,0,RDW_FRAME|RDW_INVALIDATE );
            

    if (Display.m_bFullScreen)
    {
      // Cambiamos de resolucion s
      if (FAILED(m_lpDD->SetDisplayMode( Display.m_uWidth, Display.m_uHeight, Display.m_uBPP, 0, 0)))
      {
        return (-1);
      }
			
      ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
      if (Display.m_bDoubleBuffer)
      {
        ddsd.dwFlags          |= DDSD_BACKBUFFERCOUNT;
			  ddsd.ddsCaps.dwCaps   |= DDSCAPS_FLIP | DDSCAPS_COMPLEX;
			  ddsd.dwBackBufferCount = 2; // Triple buffer
      }

      if (FAILED(m_lpDD->CreateSurface( &ddsd, &m_pddsFrontBuffer, NULL)))
      {
        return (-1);
      }

      // Pedimos puntero a backbuffer
      if (Display.m_bDoubleBuffer)
		  {
			  DDSCAPS2 ddscaps;
        memset(&ddscaps, 0, sizeof(DDSCAPS2));
			  ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
			  if(FAILED(m_pddsFrontBuffer->GetAttachedSurface(&ddscaps, &m_pddsBackBuffer)))
        {
          return (-1);
        }      
      }
    }
    else
    {
      // Front buffer
		  ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
      if (FAILED(m_lpDD->CreateSurface( &ddsd, &m_pddsFrontBuffer, NULL )))
      {
         return (-1);
      }

      // Creamos clipper
      LPDIRECTDRAWCLIPPER pClipper;
		  if(FAILED(m_lpDD->CreateClipper( 0, &pClipper, NULL)))
		  {
			  return (-1);
		  }
		  
      // Asociamos clipper a ventana y buffer primario
		  pClipper->SetHWnd( 0, SM_Main::Hwnd());
		  m_pddsFrontBuffer->SetClipper(pClipper);
      pClipper->Release();
      
      // Back buffer
      if (Display.m_bDoubleBuffer)
      {
        ddsd.dwFlags        = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS;
			  ddsd.dwWidth        = Display.m_uWidth;
			  ddsd.dwHeight       = Display.m_uHeight;
			  ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
        if (FAILED(m_lpDD->CreateSurface( &ddsd, &m_pddsBackBuffer, NULL )))
        {
          return (-1);
        }
      }
    }    

    
    if (Display.m_uBPP==8)
    {
      DDPIXELFORMAT ddpf;
      memset(&ddpf, 0, sizeof(ddpf));
      ddpf.dwSize=sizeof(ddpf);

      if (FAILED(m_pddsFrontBuffer->GetPixelFormat(&ddpf)))
      {
        return (-1);
      }

      if (ddpf.dwFlags&DDPF_PALETTEINDEXED8)
      {
        PALETTEENTRY pPaletteEntry[256];
        int i;
        for (i=0 ; i<256 ; i++)
        {
          pPaletteEntry[i].peRed=pPaletteEntry[i].peGreen=pPaletteEntry[i].peBlue=0xFF;
          pPaletteEntry[i].peFlags=PC_NOCOLLAPSE | PC_RESERVED;
        }

        if (FAILED(m_lpDD->CreatePalette(DDPCAPS_8BIT, pPaletteEntry, &m_pddpPalette, 0)))
        {
          return (-1);
        }

        if (FAILED(m_pddsFrontBuffer->SetPalette(m_pddpPalette)))
        {
          return (-1);
        }        
      }        
    }
    
        
    memcpy(&m_Display, &Display, sizeof(Display));

    m_bDisplayInit =true;
    m_bNeedRestore=false;

    DDPIXELFORMAT ddpf;
    memset(&ddpf, 0, sizeof(ddpf));
    ddpf.dwSize=sizeof(ddpf);

    if (FAILED(m_pddsFrontBuffer->GetPixelFormat(&ddpf)))
    {
      ShutdownDisplay();
      return (-1);
    }


    if (ddpf.dwRGBBitCount!=Display.m_uBPP)
    {
      return (-1);
    }
    return (0);
  }

  int ShutdownDisplay()
  {
    if (m_pddpPalette)
    {
      m_pddpPalette->Release();
      m_pddpPalette=NULL;
    }

    if(m_Display.m_bDoubleBuffer)
	  {
      // Liberamos back buffer
		  if (m_pddsBackBuffer) { m_pddsBackBuffer->Release(); m_pddsBackBuffer=NULL; }
	  }
	  
    // Liberamos front buffer
	  if (m_pddsFrontBuffer)	{ m_pddsFrontBuffer->Release()	; m_pddsFrontBuffer =NULL; }
    
    // Liberamos direct draw
    if( m_lpDD )
	  {
	    m_lpDD->SetCooperativeLevel( SM_Main::Hwnd(), DDSCL_NORMAL |DDSCL_FPUSETUP);
      m_lpDD->Release();
	  }
	  m_lpDD = NULL;

    m_bDisplayInit=false;

    return (0);
  }

  // Runtime
  int InitFrame(bool& bRestored)
  {
    if (m_bNeedRestore || m_pddsFrontBuffer->IsLost() ||  m_pddsBackBuffer->IsLost())
    {
      m_bNeedRestore=true;      
    }

    if (m_bNeedRestore)
    {
      if (Restore()!=0)
      {
        return (-1);
      }
      m_bNeedRestore=false;
      bRestored=true;
      return (0);
    }
    bRestored=false;
    return (0);
  }

  int EndFrame()
  {
    LPDIRECTDRAWSURFACE7 pddsRenderSurface=m_pddsBackBuffer;
    
    /*
    static char str[50]="";
    HDC hDC;
    if( SUCCEEDED( pddsRenderSurface->GetDC(&hDC) ) )
    {
        SetTextColor( hDC, RGB(255,255,0) );
        SetBkMode( hDC, TRANSPARENT );
        ExtTextOut( hDC, 10, 10, 0, NULL, str, strlen(str), NULL );
    
        pddsRenderSurface->ReleaseDC(hDC);
    } 
    */


    if (m_Display.m_bDoubleBuffer)
    {
       if (m_Display.m_bFullScreen)
      {
        // Fullscreen: flip
        m_pddsFrontBuffer->Flip( NULL, DDFLIP_NOVSYNC | DDFLIP_WAIT  );
        //m_pddsFrontBuffer->Flip( NULL, 0);
      }
      else
      {
        // Window: blt
        RECT rect;
      	POINT point;

        GetClientRect(SM_Main::Hwnd(), &rect);

		    point.x = rect.left; 
        point.y = rect.top;
		    ClientToScreen(SM_Main::Hwnd(), &point);

		    rect.left		= point.x;
		    rect.top		= point.y;
		    rect.right	= point.x+m_Display.m_uWidth;
		    rect.bottom	= point.y+m_Display.m_uHeight;
		
		    m_pddsFrontBuffer->Blt( &rect, m_pddsBackBuffer, NULL,  DDBLT_WAIT | DDBLT_ASYNC , NULL);
      }      
    }    

    return (0);
  }

  void NeedRestore()
  {
    m_bNeedRestore=true;
  }

  int Restore()
  {
    TDisplay Display;
    memcpy(&Display, &m_Display, sizeof(Display));
   
    if (m_bDisplayInit)
    {
      if (Display.m_bFullScreen)
      {
        switch (m_lpDD->TestCooperativeLevel())
        {
        case DDERR_NOEXCLUSIVEMODE:
          Sleep(0);
          return (-1);
        case DD_OK:
          break;
        default:
          Sleep(0);
          return (-1);
        }
      }
      else
      {
        switch (m_lpDD->TestCooperativeLevel())
        {
        case DDERR_EXCLUSIVEMODEALREADYSET:
          Sleep(0);
          return (-1);
        case DDERR_WRONGMODE:
          ShutdownDisplay();
          break;
        case DD_OK:
          break;
        default:
          Sleep(0);
          return (-1);      
        }
      }
    }


    if (!m_bDisplayInit)
    {
      if (InitDisplay(Display))
      {
        return (-1);
      }      
    }
    else
    {
      m_lpDD->RestoreAllSurfaces();      
    }

    return (0);
  }

  void ToggleFullscreen()
  {
    m_Display.m_bFullScreen=!m_Display.m_bFullScreen;
    ShutdownDisplay();
    NeedRestore();
  }

  int PixelFormat(bool bBackBuffer, TPixelFormat& PixelFormat)
  {
    LPDIRECTDRAWSURFACE7 pSurface=bBackBuffer?m_pddsBackBuffer:m_pddsFrontBuffer;

    DDPIXELFORMAT ddpf;
    memset(&ddpf, 0, sizeof(ddpf));
    ddpf.dwSize=sizeof(ddpf);

    if (FAILED(pSurface->GetPixelFormat(&ddpf)))
    {
      return (-1);
    }

    if (ddpf.dwFlags&DDPF_RGB)
    {
      PixelFormat.m_bPalettized=false;
      PixelFormat.m_uRBitMask  =ddpf.dwRBitMask;
      PixelFormat.m_uGBitMask  =ddpf.dwGBitMask;
      PixelFormat.m_uBBitMask  =ddpf.dwBBitMask;
    }
    else if (ddpf.dwFlags&DDPF_PALETTEINDEXED8)
    {
      PixelFormat.m_bPalettized=true;      
    }
    else
    {
      return (-1);
    }

    PixelFormat.m_uRBitMask  =ddpf.dwRBitMask;
    PixelFormat.m_uGBitMask  =ddpf.dwGBitMask;
    PixelFormat.m_uBBitMask  =ddpf.dwBBitMask;
    PixelFormat.m_uBPP       =ddpf.dwRGBBitCount;

    GetFromMask(PixelFormat.m_uRShiftLeft, PixelFormat.m_uRShiftRight, PixelFormat.m_uRBitMask);
    GetFromMask(PixelFormat.m_uGShiftLeft, PixelFormat.m_uGShiftRight, PixelFormat.m_uGBitMask);
    GetFromMask(PixelFormat.m_uBShiftLeft, PixelFormat.m_uBShiftRight, PixelFormat.m_uBBitMask);

    return (0);
  }

  int Lock(bool bBackBuffer, TLockData& LockData)
  {
    LPDIRECTDRAWSURFACE7 pSurface=bBackBuffer?m_pddsBackBuffer:m_pddsFrontBuffer;

    DDSURFACEDESC2 ddesc;
    memset(&ddesc, 0, sizeof(ddesc));
	  ddesc.dwSize=sizeof(DDSURFACEDESC2);


    if (FAILED(pSurface->Lock(NULL, &ddesc, DDLOCK_WAIT, NULL)))
    {
      return (-1);
    }
    
    LockData.m_pSurfaceData=ddesc.lpSurface;
    LockData.m_uStride     =ddesc.lPitch;
    LockData.m_uWidth      =m_Display.m_uWidth;
    LockData.m_uHeight     =m_Display.m_uHeight;

    return (0);
  }

  int Unlock(bool bBackBuffer)
  {
    LPDIRECTDRAWSURFACE7 pSurface=bBackBuffer?m_pddsBackBuffer:m_pddsFrontBuffer;

    pSurface->Unlock(NULL);

    return (0);
  }

  int SetPalette(unsigned uFirstEntry, unsigned uEntries, unsigned* puEntries)
  {    
    if (m_pddpPalette)
    {
      HRESULT hr;
      hr=m_pddpPalette->SetEntries(0, uFirstEntry, uEntries, (LPPALETTEENTRY) puEntries);
      return (0);
    }
    else
    {
      return (-1);
    }             
  }

  void GetFromMask(unsigned int& uiShiftLeft, unsigned int& uiShiftRight, unsigned uiMask)
  {
  	  unsigned i;
	  int iState=0;

	  unsigned uFirst=0;
	  unsigned uLength=0;


	  for (i=0 ; i<32 ; i++)
	  {
		  if (uiMask&(1<<i))
		  {
			  if (!iState)
			  {
				  uFirst=i;
				  iState=1;
				  uLength=1;
			  }
			  else
			  {
				  uLength++;
			  }
		  }
	  }

	  uiShiftLeft=uFirst;
	  uiShiftRight=8-uLength;
  }
  
  int _cdecl Printf(unsigned x, unsigned y, unsigned uColor, char* pcFormat, ...)
  {
    HDC hDC;
    LPDIRECTDRAWSURFACE7 pddsRenderSurface=m_Display.m_bDoubleBuffer?m_pddsBackBuffer:m_pddsFrontBuffer;

    char pcText[4096];

    va_list lst;	
	  va_start (lst, pcFormat);
	  vsprintf( pcText, pcFormat, lst);
	  va_end (lst);

    if( SUCCEEDED( pddsRenderSurface->GetDC(&hDC) ) )
    {
      SetTextColor( hDC, RGB( (uColor&0xFF0000)>>16, (uColor&0xFF00)>>8, (uColor&0xFF) ));
      SetBkMode( hDC, OPAQUE );
      SetBkColor ( hDC, 0 );
      ExtTextOut( hDC, x, y, 0, NULL, pcText, strlen(pcText), NULL );
    
      pddsRenderSurface->ReleaseDC(hDC);
      return (0);
    } 
    else
    {
      return (-1);
    }    
  }
}



