// Image.cpp : Implementation of cImage
#include "stdafx.h"
#include "Inject.h"
#include "Image.h"

#include "Manager.h"

/////////////////////////////////////////////////////////////////////////////
// cImage

cImage::~cImage()
{
   cManager::_p->removeImage( this );
}

void cImage::loadFile()
{
   // ::MessageBox( NULL, _T( "cImage::loadFile" ), _T( "Inject.dll" ), MB_OK );

   HANDLE hBitmap = ::CreateFile( m_szFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL );
   if( hBitmap == INVALID_HANDLE_VALUE )
   {
      int nError = ::GetLastError();
      return;
   }

   BITMAPFILEHEADER bfh;
   DWORD dwRead;
   ::ReadFile( hBitmap, &bfh, sizeof( BITMAPFILEHEADER ), &dwRead, NULL );

   BITMAPINFOHEADER bih;
   ::ReadFile( hBitmap, &bih, sizeof( BITMAPINFOHEADER ), &dwRead, NULL );

   RGBQUAD pal[ 256 ];
   ::memset( pal, 0, sizeof( RGBQUAD[ 256 ] ) );
   // Figure out the number of colors used
   int nEntries = 1 << ( bih.biBitCount );

   if( bih.biClrUsed != 0 )
      nEntries = bih.biClrUsed;

   ::ReadFile( hBitmap, pal, sizeof( RGBQUAD ) * nEntries, &dwRead, NULL );

   SIZE szImage = { bih.biWidth, bih.biHeight };
   cManager::_p->CreateCanvas( &szImage, &m_pImage );

   CComPtr< IDirectDrawSurface4 > pSurface;
   m_pImage->GetSurface( IID_IDirectDrawSurface4, reinterpret_cast< void ** >( &pSurface ) );
   
   DDSURFACEDESC2 desc;
   desc.dwSize = sizeof( DDSURFACEDESC2 );

   pSurface->Lock( NULL, &desc, DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR | DDLOCK_WRITEONLY, NULL );

   WORD wDownMixed[ 256 ];
   RGBQUAD *pSrc = pal;
   for( WORD *pDest = wDownMixed; pDest != wDownMixed + ( nEntries - 1 ); ++ pDest, ++ pSrc ) {
	   m_pImage->DownMixRGB(pSrc->rgbRed, pSrc->rgbGreen, pSrc->rgbBlue, pDest);
   }

   *pDest = 0x07FF;

   // Mekle: Added the (bih.biWidth % 4) sections below, as BMP rows MUST end on a 32bit boundry
   BYTE *pbSurf = reinterpret_cast< BYTE * >( desc.lpSurface ) + ( desc.dwHeight - 1 ) * desc.lPitch;
   BYTE *pbRow = new BYTE[ bih.biWidth + (bih.biWidth % 4) ];

   for( int nRow = 0; nRow < bih.biHeight; ++ nRow )
   {
      ::ReadFile( hBitmap, pbRow, bih.biWidth + (bih.biWidth % 4), &dwRead, NULL );

      WORD *pwDest = reinterpret_cast< WORD * >( pbSurf );
      for( BYTE *pbSrc = pbRow; pbSrc != pbRow + bih.biWidth; ++ pbSrc, ++ pwDest )
         // Lookup the color in the downmixed palette
         *pwDest = wDownMixed[ *pbSrc ];

      pbSurf -= desc.lPitch;
   }

   delete[] pbRow;

   pSurface->Unlock( NULL );
   ::CloseHandle( hBitmap );
}

void cImage::loadResource()
{
}

void cImage::loadPortal()
{
   // ::MessageBox( NULL, _T( "cImage::loadPortal" ), _T( "Inject.dll" ), MB_OK );

   struct cColorImage
   {
      DWORD m_dwID,
         m_dwX,
         m_dwY;
   };

   try
   {
      cDatFile::cFile icon = cManager::_p->m_portal.getFile( m_dwID );
      cColorImage header;

      icon.read( reinterpret_cast< BYTE * >( &header ), sizeof( cColorImage ) );

      SIZE szImage = { header.m_dwX, header.m_dwY };

      cManager::_p->CreateCanvas( &szImage, &m_pImage );

      CComPtr< IDirectDrawSurface4 > pSurface;
      m_pImage->GetSurface( IID_IDirectDrawSurface4, reinterpret_cast< void ** >( &pSurface ) );
      
      DDSURFACEDESC2 desc;
      desc.dwSize = sizeof( DDSURFACEDESC2 );

      pSurface->Lock( NULL, &desc, DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR | DDLOCK_WRITEONLY, NULL );

      BYTE *pSrcLine = new BYTE[ 3 * header.m_dwX ],
         *pEndLine = pSrcLine + 3 * header.m_dwX;

      for( int i = 0; i < header.m_dwY; ++ i )
      {
         // Read in a line
         icon.read( pSrcLine, 3 * header.m_dwX );
         WORD *pwDest = reinterpret_cast< WORD * >( reinterpret_cast< BYTE * >( desc.lpSurface ) + desc.lPitch * i );

         for( BYTE *pbSrc = pSrcLine; pbSrc != pEndLine; pbSrc += 3, ++ pwDest )
         {
            // Check for black (transparent)
            if( pbSrc[ 0 ] == 0 && pbSrc[ 1 ] == 0 && pbSrc[ 2 ] == 0 )
               *pwDest = 0x07FF;
            else
            {
               // Downmix the pixel value
               m_pImage->DownMixRGB(pbSrc[0], pbSrc[1], pbSrc[2], pwDest);
            }
         }
      }

      delete[] pSrcLine;

      pSurface->Unlock( NULL );
   }
   catch( ... )
   {
      // Failure, file not found
   }
}

bool cImage::testImage()
{
   bool bReload = false;

   if( m_pImage.p == NULL )
      bReload = true;
   else
   {
      VARIANT_BOOL bLost;
      m_pImage->get_WasLost( &bLost );

      if( bLost )
      {
         m_pImage.Release();
         bReload = true;
      }
   }

   if( bReload )
   {
      // Reload the image
      switch( m_source )
      {
      case eFile:
         loadFile();
         break;

      case eResource:
         loadResource();
         break;

      case ePortal:
         loadPortal();
         break;
      }
   }
   return ( m_pImage.p != NULL );
}

STDMETHODIMP cImage::PatBlt(ICanvas *pDest, LPRECT prcDest, LPPOINT ptOrigin)
{
   // ::MessageBox( NULL, _T( "cImage::PatBlt" ), _T( "Inject.dll" ), MB_OK );

   if( !testImage() )
      return E_FAIL;

   SIZE sz;
   m_pImage->get_Size( &sz );

   POINT ptAdjOrigin = { ptOrigin->x % sz.cx, ptOrigin->y % sz.cy };

   if( ptAdjOrigin.x > 0 )
      ptAdjOrigin.x -= sz.cx;
   if( ptAdjOrigin.y > 0 )
      ptAdjOrigin.y -= sz.cy;

   RECT rcPlacement;
   RECT rcSrc;
   POINT ptSrcOrigin;

   // Scan down
   for( int y = prcDest->top + ptAdjOrigin.y; y < prcDest->bottom; y += sz.cy )
   {
      rcPlacement.top = y;
      rcPlacement.bottom = y + sz.cy;

      if( rcPlacement.top < prcDest->top )
         rcPlacement.top = prcDest->top;

      if( rcPlacement.bottom > prcDest->bottom )
         rcPlacement.bottom = prcDest->bottom;

      ptSrcOrigin.y = ( rcPlacement.top - prcDest->top - ptAdjOrigin.y ) % sz.cy;

      rcSrc.top = ptSrcOrigin.y;
      rcSrc.bottom = rcSrc.top + ( rcPlacement.bottom - rcPlacement.top );

      // Scan across
      for( int x = prcDest->left + ptAdjOrigin.x; x < prcDest->right; x += sz.cx )
      {
         rcPlacement.left = x;
         rcPlacement.right = x + sz.cx;

         if( rcPlacement.left < prcDest->left )
            rcPlacement.left = prcDest->left;
         if( rcPlacement.right > prcDest->right )
            rcPlacement.right = prcDest->right;

         ptSrcOrigin.x = ( rcPlacement.left - prcDest->left - ptAdjOrigin.x ) % sz.cx;
         
         rcSrc.left = ptSrcOrigin.x;
         rcSrc.right = rcSrc.left + ( rcPlacement.right - rcPlacement.left );

         POINT pt = { rcPlacement.left, rcPlacement.top };
         pDest->Blt( &rcSrc, m_pImage, &pt );
      }
   }

   _ASSERTMEM( _CrtCheckMemory( ) );

	return S_OK;
}

STDMETHODIMP cImage::StretchBlt(ICanvas *pDest, LPPOINT pptDest, long nWidth, long nStartStretch, long nEndStretch)
{
   if( !testImage() )
      return E_FAIL;

   // Ok, the image is getting placed in three sections, fixed beginning and end
   // and variable length middle sections (delimited by nStartStretch and nEndStretch)

   // First we place the fixed beginning and end lengths
   SIZE sz;
   m_pImage->get_Size( &sz );

   RECT rcSrcHead = { 0, 0, nStartStretch, sz.cy },
      rcSrcTail = { nEndStretch, 0, sz.cx, sz.cy },
      rcSrcMiddle = { nStartStretch, 0, nEndStretch, sz.cy };

   int nTailPlacement = pptDest->x + nWidth - ( sz.cx - nEndStretch );

   POINT ptHead = { pptDest->x, pptDest->y },
      ptTail = { nTailPlacement, pptDest->y },
      ptStretch = { 0, pptDest->y };

   // Do the first Blts
   pDest->Blt( &rcSrcHead, m_pImage, &ptHead );
   pDest->Blt( &rcSrcTail, m_pImage, &ptTail );

   // Now do the middle sections
   for( ptStretch.x = pptDest->x + nStartStretch; ptStretch.x < nTailPlacement; ptStretch.x += ( nEndStretch - nStartStretch ) )
   {
      if( ( nTailPlacement - ptStretch.x ) < ( rcSrcMiddle.right - rcSrcMiddle.left ) )
         rcSrcMiddle.right = rcSrcMiddle.left + ( nTailPlacement - ptStretch.x );

      pDest->Blt( &rcSrcMiddle, m_pImage, &ptStretch );
   }

	return S_OK;
}

STDMETHODIMP cImage::Blt(LPRECT rcSrc, ICanvas *pDest, LPPOINT pptDest)
{
   if( !testImage() )
      return E_FAIL;

   SIZE sz;
   m_pImage->get_Size( &sz );
   RECT rcImage = { 0, 0, sz.cx, sz.cy };

   pDest->Blt( &rcImage, m_pImage, pptDest );

   return S_OK;
}

STDMETHODIMP cImage::get_Size(SIZE *pVal)
{
   if( !testImage() )
      return E_FAIL;

   m_pImage->get_Size( pVal );

	return S_OK;
}

STDMETHODIMP cImage::StretchBltArea(LPRECT prcSrc, ICanvas *pDest, LPRECT prcDest)
{
	if( !testImage() )
      return E_FAIL;

	SIZE sizeImage;
	get_Size( &sizeImage );

	if( prcSrc->right > sizeImage.cx )
		prcSrc->right = sizeImage.cx ;

	if( prcSrc->bottom > sizeImage.cy )
		prcSrc->bottom = sizeImage.cy;

	if( prcSrc->left < 0 )
		prcSrc->left = 0;
	
	if( prcSrc->top < 0 )
		prcSrc->top = 0;

	if( prcSrc->left >= prcSrc->right )
		prcSrc->left = prcSrc->right - 1;

	if( prcSrc->top >= prcSrc->bottom )
		prcSrc->top = prcSrc->bottom - 1;

	pDest->StretchBlt( prcSrc, m_pImage, prcDest );
	return S_OK;
}