/* Autor: Leandro Bernsmuller
Implementa funcoes para manipular bitmaps (usa apenas DD).
- armazena e exibe varios bitmaps numa janela */



#include <windows.h>
#include <windowsx.h>
#include <ddraw.h>
#include "..\princip\defs.h"
#include "..\extra\ddutil.h"
#include "..\extra\ddmm.h"
#include "gr.h"



/* construtor
**********************************************************/
CGr::CGr ()
{
   m_dwNumBmps = 0;

   for ( int ncB = 0; ncB < MAX_BMPS; ncB ++ )
      m_astBmps [ ncB ].lpDDSBmp = NULL;
}


  
/* Cria objeto direct draw.
Para recuperar bitmaps anteriormente definidos chame, apos,
CGr::mRecarregaBmps
retorna TRUE - certo / FALSE - erro
**********************************************************/
BOOL CGr::mCria ( HWND hWnd )
{
   // destroi anterior
   mDestroi ( TRUE );

   // define handler da janela
   m_hWnd = hWnd;
   
   // cria dispositivo de saida configurado para janela
   m_nDisp = DirectDrawDeviceFromWindow ( m_hWnd,
                                          m_szNomeDisp,
                                          &m_stRectDisp );
   // define nome, se nao houver
   if ( m_szNomeDisp [0] == 0 )
      lstrcpy ( m_szNomeDisp, STR_NENHUMDISP );

   // tenta criar objeto DD principal, se impossivel cancela
   if ( ( m_lpDD = DirectDrawCreateFromWindow ( m_hWnd ) ) == NULL )
      return mAborta ();

   m_lpDD->SetCooperativeLevel ( m_hWnd, DDSCL_NORMAL );

   // cria superficie primaria (exibicao)
   DDSURFACEDESC
      stDdsd;          // guarda definicoes do que eh para ser criado
   stDdsd.dwSize = sizeof ( stDdsd );
   stDdsd.dwFlags = DDSD_CAPS;
   stDdsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
   // tenta criar
   if ( m_lpDD->CreateSurface ( &stDdsd, &m_lpDDSOnde, NULL ) != DD_OK )
      return mAborta ();

   // cria objeto que faz recorte para superficie primaria
   if ( m_lpDD->CreateClipper ( 0, &m_lpClipper, NULL ) != DD_OK )
      return mAborta ();

   // tenta configurar recorte
   if ( m_lpClipper->SetHWnd( 0, m_hWnd ) != DD_OK )
     return mAborta ();
   if ( m_lpDDSOnde->SetClipper ( m_lpClipper ) != DD_OK )
     return mAborta ();

   // carrega palette
   m_lpDDPal = ::DDLoadPalette ( m_lpDD, STR_BMP_PAL );

   // configura palette
   if ( m_lpDDPal )
      m_lpDDSOnde->SetPalette ( m_lpDDPal );

   return TRUE;
}



/* Destroi objetos do DD
**********************************************************/
void CGr::mDestroi ( BOOL bFimDD )
{
   // testa se os objetos estao instanciados, se sim destroi
   if ( m_lpDDSOnde != NULL )  {
     m_lpDDSOnde->Release ();
     m_lpDDSOnde = NULL;
   }

   // desaloca cada bmp
   for ( int ncB = 0; ncB < (int) m_dwNumBmps; ncB ++ )
      if ( m_astBmps [ ncB ].lpDDSBmp != NULL ) {
        m_astBmps [ ncB ].lpDDSBmp->Release ();
        m_astBmps [ ncB ].lpDDSBmp = NULL;
      }

   if ( m_lpDDPal != NULL ) {
     m_lpDDPal->Release ();
     m_lpDDPal = NULL;
   }

   if ( bFimDD && m_lpDD != NULL ) {
     m_lpDD->Release ();
     m_lpDD = NULL;
   }
}



/* Reinicia objetos que podem ter sido destruidos pelo sistema.
retorna TRUE - certo / FALSE - erro
**********************************************************/
BOOL CGr::mReCria ()
{
   HRESULT 
      hCod = m_lpDDSOnde->Restore();
      
   if ( hCod != DD_OK )
      return FALSE;

   // realoca cada bmp
   for ( int ncB = 0; ncB < (int) m_dwNumBmps; ncB ++ )
      if ( m_astBmps [ ncB ].lpDDSBmp->Restore() != DD_OK )
         return FALSE;

   mRecarregaBmps ();

   return TRUE;
}



/* Carrega novamente cada bitmap
(o nome dos bitmaps nunca eh apagado e por isso pode ser
reusado)
**********************************************************/
void CGr::mRecarregaBmps ()
{
   // cada bmp
   for ( int ncB = 0; ncB < (int) m_dwNumBmps; ncB ++ )
      ::DDReLoadBitmap ( m_astBmps [ ncB ].lpDDSBmp, m_astBmps [ ncB ].szNome );
}


  
/* Se falhou no incio do DD, aborta inicio
**********************************************************/
BOOL CGr::mAborta ()
{
   mDestroi ( TRUE );

   // avisa
   MSGSTR ( STR_ERRO_DD );

   ::DestroyWindow ( m_hWnd );
   return FALSE;
}



/* Adiciona um novo bitmap para desenho.
szBmp - nome do bitmap (conforme arquivo de recursos)
fXIni, fYIni, fXFim, fYFim - coordenadas normalizadas
do bitmap dentro da area de cliente da janela.
fXIni, fYIni indicam a posicao do canto superior esquerdo e
fXFim, fYFim indicam a posicao do canto inferiror direito.

(fXIni, fYIni) *--------*
               |        |
               *--------* (fXFim, fYFim)

fXFim e/ou fYFim podem ser -1, significa que bmp nao deve
ser escalado - serah exibido na sua dimensao natural.
O canto superior esquerdo da area de cliente eh (0,0) e
o canto inferior direito eh (1,1).
retorna TRUE - certo / FALSE - erro
**********************************************************/
BOOL CGr::mIncluiBmp ( char* szNome, float fXIni, float fYIni, float fXFim, float fYFim )
{
   if ( m_dwNumBmps >= MAX_BMPS )
      return mAborta ();

   // tenta carregar bitmap
   if ( ( m_astBmps [ m_dwNumBmps ].lpDDSBmp = ::DDLoadBitmap ( m_lpDD, szNome, 0, 0 ) ) == NULL ) {
      char
         szMsg [ 1000 ];
      sprintf ( szMsg, STR_ERRO_ARQUIVO_NAO_ENCONTRADO, szNome );
      MSGSTR ( szMsg );
      return mAborta ();
   }

   // nome do bmp
   strcpy ( m_astBmps [ m_dwNumBmps ].szNome, szNome );

   // posicao para desenhar bmp na janela
   m_astBmps [ m_dwNumBmps ].fPropXIni = fXIni;
   m_astBmps [ m_dwNumBmps ].fPropYIni = fYIni;
   m_astBmps [ m_dwNumBmps ].fPropXFim = fXFim;
   m_astBmps [ m_dwNumBmps ].fPropYFim = fYFim;
   
   m_dwNumBmps ++;

   return TRUE;
}

 
 
/* Transforma coordenadas nao normalizadas da area de cliente
para coordenadas de tela (nao normalizadas).
stRect - possui a coordenada origem que serah transformada
**********************************************************/
void CGr::mTransformaCoordDest ( RECT& stRect )
{
   // translada retangulo conforme configuracao dos monitores
   POINT
      stPt;
   stPt.x = stPt.y = 0;
   ClientToScreen ( m_hWnd, &stPt );
   stPt.x -= m_stRectDisp.left;
   stPt.y -= m_stRectDisp.top;
   ::OffsetRect ( &stRect, stPt.x, stPt.y );
}



/* Copia para a tela uma parte de uma superficie
nNumBmp - qual bmp exibir, se for -1 exibe todos (default -1)
**********************************************************/
void CGr::mExibe ( int nNdxBmp )
{
   int
      nIni = 0,            // quais bmps exibir
      nFim = (int) m_dwNumBmps;
   // se deve exibir apenas um especifico
   if ( nNdxBmp != -1 ) {
      nIni = nFim = nNdxBmp;
      nFim ++;
   }

   // verifica se bitmap nao estah definido
   if ( nNdxBmp >= (int) m_dwNumBmps )
      return;

   // tenta copiar imagens
   for ( int ncB = nIni; ncB < nFim; ncB ++ )

      while ( 1 ) {

         // dimensoes do bmp
         // pega informacoes sobre a superficie
         DDSURFACEDESC
            stDesc;
         memset ( &stDesc, 0, sizeof ( DDSURFACEDESC ) );
         stDesc.dwSize = sizeof ( DDSURFACEDESC );
         stDesc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH;
         m_astBmps [ ncB ].lpDDSBmp->GetSurfaceDesc ( &stDesc );
         // usa dimensoes
         RECT
            stDim;
         stDim.left   = 0;
         stDim.top    = 0;
         stDim.right  = stDesc.dwWidth;
         stDim.bottom = stDesc.dwHeight;

         // coordenadas para onde desenhar (na area de cliente)
         RECT
            stRect;
         ::GetClientRect ( m_hWnd, &stRect );
         RECT
            stRectDest;
         // canto superior esquerdo
         stRectDest.left   = (int) ( stRect.right  * m_astBmps [ ncB ].fPropXIni );
         stRectDest.top    = (int) ( stRect.bottom * m_astBmps [ ncB ].fPropYIni );
         // canto direito
         // se nao deve escalar (-1)
         if ( m_astBmps [ ncB ].fPropXFim == -1 )
            stRectDest.right  = stRectDest.left + stDim.right;
         else
            stRectDest.right  = (int) ( stRect.right  * m_astBmps [ ncB ].fPropXFim );
         // canto inferior
         // se nao deve escalar (-1)
         if ( m_astBmps [ ncB ].fPropYFim == -1 )
            stRectDest.bottom = stRectDest.top + stDim.bottom;
         else
            stRectDest.bottom = (int) ( stRect.bottom * m_astBmps [ ncB ].fPropYFim );
         mTransformaCoordDest ( stRectDest );

         // faz uma tentativa
         HRESULT
            hValRet = m_lpDDSOnde->Blt ( &stRectDest, m_astBmps [ ncB ].lpDDSBmp, &stDim, 0, NULL );

         // analisa resposta
         // copiou
         if ( hValRet == DD_OK )
            break;
         // perdeu configuracao
         if ( hValRet == DDERR_SURFACELOST )
            if ( ! mReCria () )
               return;
         // sistema nao terminou de desenhar anterior (aguarda retraco?)
         if ( hValRet != DDERR_WASSTILLDRAWING )
            break;
         // outro erro
         if ( hValRet != DD_OK )
            break;
      }
}



/* Verifica se um certo ponto estah sobre o bmp.
nNdxBmp - qual bitmap testar
fNX e fNY - sao as coordenadas NORMALIZADAS a serem testadas.
retorna TRUE - sim / FALSE - nao
**********************************************************/
BOOL CGr::mTestaPontoSobreBmp ( int nNdxBmp, float fNX, float fNY )
{
   // testa se ponto estah entre faixa de valores do bitmap
   if ( m_astBmps [ nNdxBmp ].fPropXIni <= fNX &&
        m_astBmps [ nNdxBmp ].fPropXFim >= fNX &&
        m_astBmps [ nNdxBmp ].fPropYIni <= fNY &&
        m_astBmps [ nNdxBmp ].fPropYFim >= fNY )
      return TRUE;

   return FALSE;
}



/* Altera o bmp por outro (apenas muda o nome do arquivo,
nao recarrega superficie e nao altera proporcoes do bmp).
Para recarregar chamar CGr::mRecarregaBmps.
**********************************************************/
void CGr::mAlteraNomeBmp ( int nNdxBmp, char* szNome )
{
   // nome do bmp
   strcpy ( m_astBmps [ nNdxBmp ].szNome, szNome );
}
