// IconCache.cpp : Implementation of cIconCache
#include "stdafx.h"
#include "Inject.h"
#include "IconCache.h"

#include "Manager.h"
//Moputu - 05172002: Added long lColor to make icons with different colored borders unique.
bool cIconCache::findIcon( HMODULE hMod, DWORD dwFile, cIconCache::cIconBuffer *&pBuffer, int &nIndex, long lColor )
{
   for( cIconBufferList::iterator i = m_icons.begin(); i != m_icons.end(); )
   {
      VARIANT_BOOL bLost;
      i->m_pSurface->get_WasLost( &bLost );
      if( bLost )
      {
         i = m_icons.erase( i );
         continue;
      }

      // Search inside this buffer
      for( cIDList::iterator j = i->m_icons.begin(); j != i->m_icons.end(); ++ j )
      {
         //if( j->m_hMod == hMod && j->m_dwID == dwFile ) // Original Code
         if( j->m_hMod == hMod && j->m_dwID == dwFile && j->m_lBColor == lColor )
         {
            // We found the icon, return happy
            pBuffer = &( *i );
            nIndex = j - i->m_icons.begin();
            return true;
         }
      }

      ++ i;
   }

   // Icon not found
   return false;
}

void cIconCache::findFreeSlot( cIconBuffer *&pBuffer, int &nIndex )
{
   // First find a buffer with an unused icon slot, note that we test
   // the buffers
   for( cIconBufferList::iterator i = m_icons.begin(); i != m_icons.end(); )
   {
      VARIANT_BOOL bLost;
      i->m_pSurface->get_WasLost( &bLost );
      if( bLost )
      {
         i = m_icons.erase( i );
         continue;
      }

      if( i->m_icons.size() < ( m_nEdge * m_nEdge ) )
         break;

      ++ i;
   }

   if( i == m_icons.end() )
   {
      // There were no surfaces with free slots, make a new one
      cIconBuffer buf;
      buf.m_icons.reserve( m_nEdge * m_nEdge );
      
      SIZE sz = { m_szIcon.cx * m_nEdge, m_szIcon.cy * m_nEdge };

      cManager::_p->CreateCanvas( &sz, &buf.m_pSurface );
      m_icons.push_back( buf );

      // Reset to last entry
      i = -- m_icons.end();
   }

   pBuffer = &( *i );
   nIndex = i->m_icons.size();
}

//Modified by Moputu 05/12/2002 to allow the replacement of icon border colors.
bool cIconCache::loadIcon( DWORD dwFile, cIconCache::cIconBuffer *&pBuffer, int &nIndex, long lColor ) 
{
   findFreeSlot( pBuffer, nIndex );

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

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

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

      int nXScale, nYScale;

      if( header.m_dwX % m_szIcon.cx != 0 )
      {
         // Bad size - only linear filtering here
         _ASSERT( FALSE );
         return false;
      }

      nXScale = header.m_dwX / m_szIcon.cx;

      if( header.m_dwY % m_szIcon.cy != 0 )
      {
         // Bad size - only linear filtering here
         _ASSERT( FALSE );
         return false;
      }

      nYScale = header.m_dwY / m_szIcon.cy;

      // Can't fail now, transfering bits to the buffer
      POINT ptIconOrg = { ( nIndex % m_nEdge ) * m_szIcon.cx, ( nIndex / m_nEdge ) * m_szIcon.cy };
      RECT rcSrc = { ptIconOrg.x, ptIconOrg.y, ptIconOrg.x + m_szIcon.cx, ptIconOrg.y + m_szIcon.cy };

      DDSURFACEDESC2 desc;
      desc.dwSize = sizeof( DDSURFACEDESC2 );

      CComPtr< IDirectDrawSurface4 > pSurface;
      pBuffer->m_pSurface->GetSurface( IID_IDirectDrawSurface4, reinterpret_cast< void ** >( &pSurface ) );

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

      BYTE srcLine[ 3 * 64 ],
         *pEndLine = srcLine + 3 * header.m_dwX;

      {
         for( int i = 0; i < m_szIcon.cy; ++ i )
         {
            // Read in a line
            icon.read( srcLine, 3 * header.m_dwX );
            icon.skip( 3 * header.m_dwX * ( nYScale - 1 ) );
            WORD *pwDest = reinterpret_cast< WORD * >( reinterpret_cast< BYTE * >( desc.lpSurface ) + desc.lPitch * i );

            for( BYTE *pbSrc = srcLine; pbSrc != pEndLine; pbSrc += 3 * nXScale, ++ pwDest )
            {
               // Check for black (transparent)
               if( ( pbSrc[ 0 ] == 0 && pbSrc[ 1 ] == 0 && pbSrc[ 2 ] == 0 ) ||
                  ( pbSrc[ 0 ] == 255 && pbSrc[ 1 ] == 255 && pbSrc[ 2 ] == 255 ) )
				   //  *pwDest = 0x07FF; // Original code
				  if( pbSrc[ 0 ] == 255 && pbSrc[ 1 ] == 255 && pbSrc[ 2 ] == 255 ) //Check for White (border)
				  {
					  if ( lColor == -1 ) // No argument was passed, treat it like the original code would have.
					  {
						*pwDest = 0x07FF; 
			          }
					  else //Replace with specified color.
					  {
						pBuffer->m_pSurface->DownMixRGB((BYTE)lColor, (BYTE)(lColor >> 8), (BYTE)(lColor >> 16), pwDest); 
					  }
				  }
				  else
				  {
					  *pwDest = 0x07FF;
				  }
               else
               {
                  pBuffer->m_pSurface->DownMixRGB(pbSrc[0], pbSrc[1], pbSrc[2], pwDest);
               }
            }
         }
      }

      pSurface->Unlock( &rcSrc );
      cIconID iid = { NULL, dwFile, lColor }; //Moputu - 05172002: Added lColor to make icons with different colored borders unique.
      pBuffer->m_icons.push_back( iid );

      // Survived the encounter
      return true;
   }
   catch( ... )
   {
      // Failure, return false at end
   }

   // File not found, fail
   _ASSERT( FALSE );
   return false;
}

bool cIconCache::loadIconResource( HMODULE hMod, DWORD dwResourceID, cIconBuffer *&pBuffer, int &nIndex, long lColor )
{
   HICON hIcon = reinterpret_cast< HICON >( ::LoadImage( hMod, MAKEINTRESOURCE( dwResourceID ), IMAGE_ICON, m_szIcon.cx, m_szIcon.cy, LR_DEFAULTCOLOR ) );
   if( hIcon == NULL )
      return false;

   findFreeSlot( pBuffer, nIndex );

   // TODO: Write a fast version of this function, use GDI for now

   HDC hdc;
   pBuffer->m_pSurface->GetDC( &hdc );

   HBRUSH br = ::CreateSolidBrush( RGB( 0, 255, 255 ) );
   POINT ptIconOrg = { ( nIndex % m_nEdge ) * m_szIcon.cx, ( nIndex / m_nEdge ) * m_szIcon.cy };
   RECT rcSrc = { ptIconOrg.x, ptIconOrg.y, ptIconOrg.x + m_szIcon.cx, ptIconOrg.y + m_szIcon.cy };

   ::FillRect( hdc, &rcSrc, br );
   ::DrawIconEx( hdc, ptIconOrg.x, ptIconOrg.y, hIcon, m_szIcon.cx, m_szIcon.cy, 0, NULL, DI_NORMAL );

   ::DeleteObject( br );

   pBuffer->m_pSurface->ReleaseDC();

   ::DestroyIcon( hIcon );

   cIconID iid = { hMod, dwResourceID, lColor }; //Moputu - 05172002: Added long lColor to make icons with different colored borders unique.
   pBuffer->m_icons.push_back( iid );

   return true;
}

void cIconCache::drawIcon( LPPOINT pPos, ICanvas *pDest, cIconBuffer *pBuffer, int nIndex )
{
   _ASSERTE( pPos != NULL );
   _ASSERTE( pDest != NULL );
   _ASSERTE( pBuffer != NULL );
   _ASSERTE( nIndex >= 0 && nIndex < ( m_nEdge * m_nEdge ) );

   POINT ptIconOrg = { ( nIndex % m_nEdge ) * m_szIcon.cx, ( nIndex / m_nEdge ) * m_szIcon.cy };
   RECT rcSrc = { ptIconOrg.x, ptIconOrg.y, ptIconOrg.x + m_szIcon.cx, ptIconOrg.y + m_szIcon.cy };

   pDest->Blt( &rcSrc, pBuffer->m_pSurface, pPos );
}

/////////////////////////////////////////////////////////////////////////////
// cIconCache


STDMETHODIMP cIconCache::DrawIcon( LPPOINT ppt, long nFile, long nModule, ICanvas *pCanvas )
{
   _ASSERTE( ppt != NULL );
   _ASSERTE( pCanvas != NULL );

   cIconBuffer *pBuffer;
   int nIndex;

// GKusnick: Handle no-icon case without asserting.
   if (nModule == 0 && nFile == 0) return S_OK;

   if( !findIcon( reinterpret_cast< HMODULE >( nModule ), *reinterpret_cast< DWORD* >( &nFile ), pBuffer, nIndex ) )
   {
      if( nModule == 0 )
      {
         if( !loadIcon( *reinterpret_cast< DWORD* >( &nFile ), pBuffer, nIndex ) )
            return E_FAIL;
      }
      else
      {
         if( !loadIconResource( reinterpret_cast< HMODULE >( nModule ), *reinterpret_cast< DWORD* >( &nFile ), pBuffer, nIndex ) )
            return E_FAIL;
      }
   }

   drawIcon( ppt, pCanvas, pBuffer, nIndex );

	return S_OK;
}

STDMETHODIMP cIconCache::DrawIconEx( LPPOINT ppt, long nFile, long nModule, ICanvas *pCanvas, long lColor )
{
   _ASSERTE( ppt != NULL );
   _ASSERTE( pCanvas != NULL );

   cIconBuffer *pBuffer;
   int nIndex;

   if( !findIcon( reinterpret_cast< HMODULE >( nModule ), *reinterpret_cast< DWORD* >( &nFile ), pBuffer, nIndex, lColor ) ) //Moputu - 05172002: Added long lColor to make icons with different colored borders unique.
   {
      if( nModule == 0 )
      {
         if( !loadIcon( *reinterpret_cast< DWORD* >( &nFile ), pBuffer, nIndex, lColor ) )
            return E_FAIL;
      }
      else
      {
         if( !loadIconResource( reinterpret_cast< HMODULE >( nModule ), *reinterpret_cast< DWORD* >( &nFile ), pBuffer, nIndex, lColor ) ) //Moputu - 05172002: Added long lColor to make icons with different colored borders unique.
            return E_FAIL;
      }
   }

   drawIcon( ppt, pCanvas, pBuffer, nIndex );

	return S_OK;
}
