#include "SM_Engine3DPCH.h"
#include <direct.h>
#include "SM_Screen.h"
#include "MBStaticList.h"
#include "MSystemFunctions.h"

const unsigned TEXWIDTH=256,
               TEXHEIGHT=256;


namespace ScreenManager
{  
MBStaticList<SMImage*> m_slImageList;


SMImage::SMImage  ()
{
  m_uWidth      =0;
  m_uHeight     =0;
  m_uTilesX     =0;
  m_uTilesY     =0;
  m_bAlpha      =false;
  m_piTextures  =0;
}

SMImage::~SMImage ()
{
  Shutdown();
}

int SMImage::Init()
{
  Shutdown();
  return 0;
}

int SMImage::Shutdown()
{
  unsigned i;

  for (i=0 ; i<m_uTilesX*m_uTilesY ; i++)
  {
    ResourceManager::ReleaseTexture(m_piTextures[i]);
  }

  if (m_piTextures)
  {
    delete[] m_piTextures;
    m_piTextures=0;
  }

  return 0;
}


int     Init        ()
{
  m_slImageList.Init();
  return 0;
}

int     Shutdown    ()
{
  int iIterator, iNext;


  for (iIterator=m_slImageList.First() ; iIterator!=-1; iIterator=iNext)
  {
    SMImage* pImage;

    iNext=m_slImageList.Next(iIterator);

    m_slImageList.Get(iIterator, pImage);
    delete pImage;
  }

  m_slImageList.Shutdown();

  return 0;
}

int     FindImage   (char* pcImageName)
{
  SMImage* pImage;
  int iIterator;

  for (iIterator=m_slImageList.First() ; iIterator!=-1 ; iIterator=m_slImageList.Next(iIterator))
  {
    m_slImageList.Get(iIterator, pImage);
    if (stricmp(pcImageName, pImage->m_pcName)==0)
    {
      return iIterator;
    }
  }

  return -1;
}

int     LoadImagen   (char* pcImageName, MSurface* pSurface)
{
  char     pcBasename[256];
  MSurface s;
  int      iID;

  getcwd(pcBasename, 256);
  SM_Main::OutputConsole("Working dir is %s\n", pcBasename);
   

  MExtractBasename(pcBasename, pcImageName);

  if ((iID=FindImage(pcBasename))!=-1)
  {
    return (iID);
  }
  
  SMImage* pImage=new SMImage;

  if (pSurface)
  {
  }
  else
  {
    pSurface=&s;

    if (pSurface->Load(pcImageName, true)!=0)
    {
      return -1;
    }
  }

  pImage->m_uWidth =pSurface->GetPSurfaceDescriptor()->m_uiWidth;
  pImage->m_uHeight=pSurface->GetPSurfaceDescriptor()->m_uiHeight;
  pImage->m_uTilesX=pImage->m_uWidth /TEXWIDTH+((pImage->m_uWidth %TEXWIDTH)?1:0);
  pImage->m_uTilesY=pImage->m_uHeight/TEXWIDTH+((pImage->m_uHeight%TEXWIDTH)?1:0);;
  
  if (pSurface->GetPSurfaceDescriptor()->m_pfPixelFormat.m_uAMask)
  {
    pImage->m_bAlpha=true;
  }
  else
  {
    pImage->m_bAlpha=false;
  }

  MSurfaceDescriptor sd;
  sd.m_pfPixelFormat=CommonPixelDescriptors[pImage->m_bAlpha?E_ARGB32_8888:E__RGB32__888];
  sd.m_uiWidth =pImage->m_uWidth;
  sd.m_uiHeight=pImage->m_uHeight;

  MSurface Source;
  Source.Init(sd, 0, 0);
  Source.ScaleAndConvertFrom(*pSurface);

  sd.m_pfPixelFormat=CommonPixelDescriptors[pImage->m_bAlpha?E_ARGB32_8888:E__RGB32__888];
  sd.m_uiWidth =TEXWIDTH;
  sd.m_uiHeight=TEXWIDTH;
  MSurface Target;

  Target.Init(sd, 0, 0);

  unsigned i,j;

  int iCounter=0;
  pImage->m_piTextures=new int[pImage->m_uTilesX*pImage->m_uTilesY];
  if (!pImage->m_piTextures)
  {
    return -1;
  }

  for (j=0 ; j<pImage->m_uTilesY ; j++)
  {
    for (i=0 ; i<pImage->m_uTilesX ; i++)
    {
      Target.SurfaceBlt(Source, i*TEXWIDTH, j*TEXWIDTH, TEXWIDTH, TEXWIDTH, 0, 0);      


      bool b32BitTexture=
            FAILED( SM_D3d::D3D()->CheckDeviceFormat(SM_D3d::GetCurrentMode()->uDevice, 
                                   D3DDEVTYPE_HAL,
                                   SM_D3d::GetCurrentMode()->d3dFormat,
                                   0,
                                   D3DRTYPE_TEXTURE,
                                   D3DFMT_A8R8G8B8))?false:true;

      int iTextureID=ResourceManager::CreateTextureFromSurface(&Target, pImage->m_bAlpha?(b32BitTexture?D3DFMT_A8R8G8B8:D3DFMT_A4R4G4B4):D3DFMT_R5G6B5);
      if (iTextureID==-1)
      {
        return (-1);
      }

      pImage->m_piTextures[iCounter++]=iTextureID;
    }
  }

  strcpy(pImage->m_pcName, pcBasename);

  iID=m_slImageList.InsertTail(pImage);
  if (iID==-1)
  {
    delete pImage;
    return -1;
  }

  return iID;
}

int     RemoveImage (int iID)
{
  return 0;
}

SMImage*  GetImage    (int iID)
{
  SMImage* pImage=0;

  m_slImageList.Get(iID, pImage);

  return pImage;
}  

void Render(int iImage, float x, float y, int iSegmentWidth, int iSegmentHeight, eBlend Blend, unsigned ColorBlend, float fZoom)
{      
  SMImage* pImage=GetImage(iImage);
  assert(pImage);

  unsigned uWidth=pImage->m_uWidth;
  unsigned uHeight=pImage->m_uHeight;

  x = x+(pImage->m_uWidth/2)*(1.0f - fZoom);
  y = y+(pImage->m_uHeight/2)*(1.0f - fZoom);

 

  SM_D3d::SetRenderState(D3DRS_TEXTUREFACTOR, ColorBlend);

  SM_D3d::SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);  
  
  SM_D3d::SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE );
  SM_D3d::SetTextureStageState(0, D3DTSS_COLOROP  , D3DTOP_MODULATE);
  SM_D3d::SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
  if (Blend == E_INVERT || Blend == E_SUBSTRACTIVE)
  {
    SM_D3d::SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TFACTOR);
  }
  else
  {
    SM_D3d::SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
  }

  SM_D3d::SetTextureStageState(0, D3DTSS_ALPHAOP  , D3DTOP_MODULATE);
  SM_D3d::SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
  SM_D3d::SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_TFACTOR);
  
  SM_D3d::SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
  SM_D3d::SetTextureStageState(0, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP);
  SM_D3d::SetTextureStageState(0, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP);
  SM_D3d::SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
  SM_D3d::SetTextureStageState(0, D3DTSS_MINFILTER, D3DTEXF_LINEAR);  
  SM_D3d::SetTextureStageState(0, D3DTSS_MIPFILTER, D3DTEXF_NONE);
  SM_D3d::SetRenderState(D3DRS_ZWRITEENABLE, D3DZB_FALSE);
  

  if (!pImage->m_bAlpha && ((ColorBlend&(0xFF000000))==0xFF000000))
  {    
  	switch (Blend)
  	{
	  case E_NORMAL:
		  SM_D3d::SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
		  break;
	  case E_ADDITIVE:
		  SM_D3d::SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
		  SM_D3d::SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
		  SM_D3d::SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);  
		  break;
	  case E_SUBSTRACTIVE:
		  SM_D3d::SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
		  SM_D3d::SetRenderState(D3DRS_SRCBLEND ,D3DBLEND_ZERO);
		  SM_D3d::SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR);
		  break;
    case E_MULTIPLY:
      SM_D3d::SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
		  SM_D3d::SetRenderState(D3DRS_SRCBLEND , D3DBLEND_ZERO);
		  SM_D3d::SetRenderState(D3DRS_DESTBLEND, D3DBLEND_SRCCOLOR);		  
      break;
    case E_INVERT:
      SM_D3d::SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
		  SM_D3d::SetRenderState(D3DRS_SRCBLEND , D3DBLEND_INVDESTCOLOR);
		  SM_D3d::SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO);		  
      break;
  	}
  }
  else
  {
  	switch (Blend)
  	{
    case E_NORMAL:
	    SM_D3d::SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);                
	      SM_D3d::SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
          SM_D3d::SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
	    break;
    case E_ADDITIVE:
	    SM_D3d::SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);            
          SM_D3d::SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
          SM_D3d::SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
	    break;
    case E_SUBSTRACTIVE:
	    SM_D3d::SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);      	
	    SM_D3d::SetRenderState(D3DRS_SRCBLEND ,D3DBLEND_ZERO);
	    SM_D3d::SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR);		
	    break;
    case E_MULTIPLY:
      SM_D3d::SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
		  SM_D3d::SetRenderState(D3DRS_SRCBLEND , D3DBLEND_ZERO);
		  SM_D3d::SetRenderState(D3DRS_DESTBLEND, D3DBLEND_SRCCOLOR);		        
      break;
    case E_INVERT:
      SM_D3d::SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
		  SM_D3d::SetRenderState(D3DRS_SRCBLEND , D3DBLEND_INVDESTCOLOR);
		  SM_D3d::SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR);		  
      break;
  	}  	
  }

  

  unsigned i,j;  

  
  unsigned uVertices=4;
  unsigned uIndices =6;

  FVF_PosRhwDiffuseTex1 pVertices[4];
  unsigned short pIndices[6]={0, 1, 2, 1, 2, 3};

  int iLastSegmentWidth =(uWidth%TEXWIDTH);
  int iLastSegmentHeight=(uHeight%TEXHEIGHT);


  for (j=0 ; j<pImage->m_uTilesY ; j++)
  {
    for (i=0 ; i<pImage->m_uTilesX ; i++)
    {      
      for (int n=0 ; n<4 ; n++)
      {
        pVertices[n].z=1.0f;
        pVertices[n].oow=1.0f ;
        pVertices[n].diffuse=0xFFFFFFFF;      
      }

      float xRight, uRight, yDown, vDown, xTile, yTile;
      if (i==pImage->m_uTilesX-1 && iLastSegmentWidth)
      {
        xRight=(float)iLastSegmentWidth*fZoom;
        uRight=(float)iLastSegmentWidth/float(TEXWIDTH);
      }
      else
      {
        xRight=TEXWIDTH*fZoom;
        uRight=1.0f;
      }

      if (j==pImage->m_uTilesY-1 && iLastSegmentHeight)
      {
        yDown=(float)iLastSegmentHeight*fZoom;
        vDown=(float)iLastSegmentHeight/float(TEXWIDTH);
      }
      else
      {
        yDown=TEXHEIGHT*fZoom;
        vDown=1.0f;
      }

      xTile = TEXWIDTH * fZoom;
      yTile = TEXHEIGHT * fZoom;

      
      pVertices[0].x=x+i*xTile-0.5f; pVertices[0].y=y+j*yTile-0.5f; 
      pVertices[0].u=0.0f;
      pVertices[0].v=0.0f;

      pVertices[1].x=x+i*xTile-0.5f+xRight; pVertices[1].y=y+j*yTile-0.5f; 
      pVertices[1].u=uRight;
      pVertices[1].v=0.0f;      

      pVertices[2].x=x+i*xTile-0.5f; pVertices[2].y=y+j*yTile-0.5f+yDown; 
      pVertices[2].u=0.0f;
      pVertices[2].v=vDown;
      
      pVertices[3].x=x+i*xTile-0.5f+xRight; pVertices[3].y=y+j*yTile-0.5f+yDown; 
      pVertices[3].u=uRight;
      pVertices[3].v=vDown;

      int iFixup;
      for (iFixup=0 ; iFixup<4 ; iFixup++)
      {
        pVertices[iFixup].x=Coordinates::GetPhysicX(pVertices[iFixup].x);
        pVertices[iFixup].y=Coordinates::GetPhysicY(pVertices[iFixup].y);
      }

      
      // Draw
      SM_D3d::Device()->SetTexture(0, ResourceManager::GetTextureFromID(pImage->m_piTextures[j*pImage->m_uTilesX+i]));    
      SM_D3d::Device()->SetVertexShader(FVF_POSRHWDIFFUSETEX1);
    
      SM_D3d::Device()->DrawIndexedPrimitiveUP(
        D3DPT_TRIANGLELIST, 
        0,
        4,
        2,
        pIndices,
        D3DFMT_INDEX16,
        pVertices, sizeof(FVF_PosRhwDiffuseTex1));
    }
  }

  /*
  for (l=0 ; l<pImage->m_uTilesY ; l++)
  {
    for (k=0 ; k<pImage->m_uTilesX ; k++)
    {
      unsigned uVWidth =TEXWIDTH/iSegmentWidth;
      unsigned uVHeight=TEXWIDTH/iSegmentHeight;

      // Generate vertices
      for (j=0 ; j<=uVHeight ; j++)
      {
        for (i=0 ; i<=uVWidth ; i++)
        {
          Vector3D v3d(
             -0.5f+float(TEXWIDTH*k)+iSegmentWidth*i,
             -0.5f+float(TEXWIDTH*l)+iSegmentHeight*j,
             0.0f);

          Vector3D v3dOut;

          mFinal.Transform(&v3dOut, &v3d, 1);

          pVertices[j*(uVWidth+1)+i].x=v3dOut.x;
          pVertices[j*(uVWidth+1)+i].y=v3dOut.y;
          pVertices[j*(uVWidth+1)+i].z=1.0f;
          pVertices[j*(uVWidth+1)+i].oow=1.0f ;
          pVertices[j*(uVWidth+1)+i].diffuse=0xFFFFFFFF;
          pVertices[j*(uVWidth+1)+i].u=float(i)/float(uVWidth);
          pVertices[j*(uVWidth+1)+i].v=float(j)/float(uVHeight);

          float x=pVertices[j*(uVWidth+1)+i].x;
          float y=pVertices[j*(uVWidth+1)+i].y;
          //pVertices[j*(uVWidth+1)+i].x+=0.75f*sinf(4.0f*Timer::GetTime()+x+y);
          //pVertices[j*(uVWidth+1)+i].y+=0.75f*cosf(4.0f*Timer::GetTime()+x+y);
        }
      }

      // Generate Indices
      unsigned uIndicesWidth = pImage->m_uTilesX-1==k?iLastSegmentWidth:uVWidth;
      unsigned uIndicesHeight= pImage->m_uTilesY-1==l?iLastSegmentHeight:uVHeight;

      unsigned uOffset=0;
      for (j=0 ; j<uIndicesHeight ; j++)
      {
        for (i=0 ; i<uIndicesWidth ; i++)
        {
          pIndices[uOffset++]=(j+0)*(uVWidth+1)+(i+0);
          pIndices[uOffset++]=(j+0)*(uVWidth+1)+(i+1);
          pIndices[uOffset++]=(j+1)*(uVWidth+1)+(i+1);
          pIndices[uOffset++]=(j+0)*(uVWidth+1)+(i+0);
          pIndices[uOffset++]=(j+1)*(uVWidth+1)+(i+0);
          pIndices[uOffset++]=(j+1)*(uVWidth+1)+(i+1);
        }
      }
      
      // Draw
      SM_D3d::Device()->SetTexture(0, ResourceManager::GetTextureFromID(pImage->m_piTextures[l*pImage->m_uTilesX+k]));    
      SM_D3d::Device()->SetVertexShader(FVF_POSRHWDIFFUSETEX1);
    
      SM_D3d::Device()->DrawIndexedPrimitiveUP(
        D3DPT_TRIANGLELIST, 
        0,
        (uVWidth+1)*(uVHeight+1),
        uOffset/3,
        pIndices,
        D3DFMT_INDEX16,
        pVertices, sizeof(FVF_PosRhwDiffuseTex1));
    }
  }*/

  SM_D3d::SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
}

};

