#include "SM_Engine3DPCH.h"
#include "SM_KeyFrameSequence.h"
#include "SM_Twister.h"
#include "SM_MathAux.h"
#include "SM_MathPch.h"


#define ROWS      64
#define COLUMNS   48

FVF_PosDiffuseSpecularTex1 pVertices[ROWS*COLUMNS];
unsigned short pusIndices[6*(ROWS-1)*(COLUMNS-1)];


unsigned SM_Twister::ColorFromVector(float x, float y, float z)
{
  unsigned r = int((x*0.5f+0.5f)*255.0f)<<16;
  unsigned g = int((y*0.5f+0.5f)*255.0f)<<8;
  unsigned b = int((z*0.5f+0.5f)*255.0f)<<0;

  return 0xFF000000|r|g|b;
}

SM_Twister::SM_Twister      ()
{
}

SM_Twister::~SM_Twister     ()
{
}

int  SM_Twister::Reset              ()
{
  return 0;
}


int  SM_Twister::Init               (int iShader, IKFAnimable* pAnimable)
{
  InitMathAux();

  if (Heightmap.Load("data/textures/traumaheight.tga") != 0)
  {
    SM_Main::OutputError("missing data/textures/traumaheight.tga (32x64x32bpp)");
    return -1;
  }

  m_iShader = ShaderManager::LoadShader("trauma");
  m_pAnimable = pAnimable;
  return 0;
}

int  SM_Twister::Shutdown           ()
{
  return 0;
}



void SM_Twister::Render             (RenderContext* pRenderContext, int iOutcode, float fTime)
{
  fTime*=2.0f;
    // Paint a poly example!!! 
    RenderContext RC;   

    static bool bInit = false;
    if (!bInit)
    {
      bInit = true;

      unsigned i,j;
      unsigned short* pI = pusIndices;
      unsigned uCurrent = 0;
      for (j = 0 ; j  <ROWS-1 ; j++, uCurrent+=COLUMNS)
      {
        for (i = 0 ; i < COLUMNS-1 ; i++)
        {
          pI[0] = uCurrent+i;
          pI[1] = uCurrent+i+COLUMNS;
          pI[2] = uCurrent+(i+1);          
          pI[3] = uCurrent+i+COLUMNS;
          pI[4] = uCurrent+(i+1)+COLUMNS;
          pI[5] = uCurrent+(i+1);
          
          pI+=6;                   
        }
      }
    }

    RC.Set(
      Vector3D(30.0f*SINF(fTime*0.5f), 0.0f, 30.0f*COSF(fTime*0.5f)),
      Vector3D(0.0f, 0.0f, 0.0f),
      Vector3D(0.0f, 1.0f, 0.0f),
      90,
      0.75f,
      1.0f,
      200.0f);
  /*
  5
    RC.SetViewport(0, 0, 640.0f, 480.0f);  
  
    RC.SyncRasterizer();
    RC.UpdateFrustum();
    

    ShaderManager::SetRenderContext(&RC);
*/
    
    int i,j;

    float x,y,z;

    #define SCALE 0.35f
    #define DY 2.0f*SCALE

    x = 0.0f;
    y = DY*float(-ROWS/2);
    z = 0.0f;

    

    float fOffset = fmodf(fTime+1.0f+0.5f*SINF(fTime), 1.0f);
    for (j = 0 ; j < ROWS ; j++)
    {
      float fDisplace = float(-fTime*1.5f+float(j)*0.075f);
      float sx = SINF(fDisplace)*4.0f;
      float sz = COSF(fDisplace)*3.0f;

      unsigned* puHeight = (unsigned*) Heightmap.GetSurfacePointer(0, j);
        
      FVF_PosDiffuseSpecularTex1* pV = pVertices+j*COLUMNS;    
      for (i = 0 ; i < COLUMNS ; i++)
      {
       
        float u = float(i)/float(COLUMNS-1);
        float v = float(j)/float(ROWS-1);

        //float fh = sinf(float(i)*0.02f);
        float fh = float(puHeight[i]&0xFF)*0.01f;
        
        float fAngle = u*2.0f*PI+sx*0.25;


        pV[i].x = sx + SINF(fAngle)*10.0f*(1.0f+fh*0.1);
        pV[i].y = y;
        pV[i].z = sz + COSF(fAngle)*10.0f*(1.0f+fh*0.1);
        pV[i].u = u*4.0f;
        pV[i].v = v*4.0f;        
        pV[i].diffuse = 0xFFFFFFFF;               
      }
      pV[i-1].x = pV[0].x;
      pV[i-1].y = pV[0].y;
      pV[i-1].z = pV[0].z;

      
      y += DY;
    }

    int iTri;
    Vector3D pvNormal[(ROWS-1)*(COLUMNS-1)*2];
    Vector3D pvUGradient[(ROWS-1)*(COLUMNS-1)*2];


    for (i=0, iTri=0 ; i<(ROWS-1)*(COLUMNS-1)*6 ; i+=3, iTri++)
    {
      FVF_PosDiffuseSpecularTex1* pV1 =  &pVertices[pusIndices[i]];
      FVF_PosDiffuseSpecularTex1* pV2 =  &pVertices[pusIndices[i+1]];
      FVF_PosDiffuseSpecularTex1* pV3 =  &pVertices[pusIndices[i+2]];

      Vector3D v1(pV1->x, pV1->y, pV1->z);
      Vector3D v2(pV2->x, pV2->y, pV2->z);
      Vector3D v3(pV3->x, pV3->y, pV3->z);

      pvNormal[iTri]=
        Vector3D::Cross(
          v3-v2,
          v2-v1).Normalize();

      if (iTri & 1)
      {
        pvUGradient[iTri]=(v2-v1).Normalize();
      }
      else
      {
        pvUGradient[iTri]=(v3-v1).Normalize();
      }
    }
    


    Vector3D pvVertexNormal[ROWS*COLUMNS];
    Vector3D pvUVertexGradient[ROWS*COLUMNS];
    Vector3D pvVVertexGradient[ROWS*COLUMNS];

    memset(pvVertexNormal, 0, sizeof(Vector3D)*ROWS*COLUMNS);
    memset(pvUVertexGradient, 0, sizeof(Vector3D)*ROWS*COLUMNS);
    
    for (i=0, iTri=0 ; i<(ROWS-1)*(COLUMNS-1)*6 ; i+=3, iTri++)
    {
      pvVertexNormal[pusIndices[i]]  +=pvNormal[iTri];
      pvVertexNormal[pusIndices[i+1]]+=pvNormal[iTri];
      pvVertexNormal[pusIndices[i+2]]+=pvNormal[iTri];

      pvUVertexGradient[pusIndices[i]]  +=pvUGradient[iTri];
      pvUVertexGradient[pusIndices[i+1]]+=pvUGradient[iTri];
      pvUVertexGradient[pusIndices[i+2]]+=pvUGradient[iTri];
      
    }

    for (i=0 ; i<ROWS*COLUMNS ; i++)
    {
      pvVertexNormal[i].Normalize();
      //pVertices[i].nx = pvVertexNormal[i].x;
      //pVertices[i].ny = pvVertexNormal[i].y;
      //pVertices[i].nz = pvVertexNormal[i].z;
    }

    Vector3D v3dCamera = pRenderContext->GetViewport()->m_v3dPosition;


    Vector3D Light(5.0f*SINF(fTime*2.3f), 0.0f*COSF(fTime*2.3f), 50.0f*COSF(fTime*0.3f));
    

    Vector3D LightPos = v3dCamera+pRenderContext->m_VRP*15.0f*sinf(fTime)+pRenderContext->m_VUP*15.0f*cosf(fTime)+pRenderContext->m_VPN*15.0f;
    Vector3D vTemp = LightPos;
        
    Light.Normalize();       
    /*Matrix4X4 temp;
    pRenderContext->GetViewport()->ViewMatrix(temp);
    temp.Transform(&LightPos, &vTemp, 1);
    */

   FVF_PosDiffuseSpecularTex1* pV = pVertices;      
   for (i=0 ; i<ROWS*COLUMNS ; i++)
   {
      pvUVertexGradient[i].Normalize();
      pvVVertexGradient[i] = Vector3D::Cross(pvVertexNormal[i], pvUVertexGradient[i]);

      Light = (LightPos-Vector3D(pV[i].x, pV[i].y, pV[i].z)).Normalize();

      Vector3D v3dEye = v3dCamera-Vector3D(pV[i].x, pV[i].y, pV[i].z);
      v3dEye.Normalize();
      Vector3D H = (v3dEye+Light)*0.5f;
      H.Normalize();


      if ((i+1)%COLUMNS == 0)
      {
        pV[i].diffuse = pV[i-COLUMNS+1].diffuse;
        pV[i].specular= pV[i-COLUMNS+1].specular;
      }
      else
      {
        pV[i].diffuse = ColorFromVector( Light.x*pvUVertexGradient[i].x+Light.y*pvUVertexGradient[i].y+ Light.z*pvUVertexGradient[i].z,
                                         Light.x*pvVVertexGradient[i].x+Light.y*pvVVertexGradient[i].y+ Light.z*pvVVertexGradient[i].z,
                                         Light.x*pvVertexNormal[i].x   +Light.y*pvVertexNormal[i].y   + Light.z*pvVertexNormal[i].z);
        pV[i].specular = ColorFromVector( H.x*pvUVertexGradient[i].x+H.y*pvUVertexGradient[i].y+ H.z*pvUVertexGradient[i].z,
                                          H.x*pvVVertexGradient[i].x+H.y*pvVVertexGradient[i].y+ H.z*pvVVertexGradient[i].z,
                                          H.x*pvVertexNormal[i].x   +H.y*pvVertexNormal[i].y   + H.z*pvVertexNormal[i].z);
      }      
    }
       
    ShaderManager::GetShader(m_iShader)->SetShaderState(0);

    SM_D3d::SetRenderState(D3DRS_LIGHTING, FALSE);  
    SM_D3d::Device()->SetTransform(D3DTS_WORLD, (D3DMATRIX*) &Matrix4X4::Identity);
    SM_D3d::Device()->SetVertexShader(FVF_POSDIFFUSESPECULARTEX1);    


    //SM_D3d::SetRenderState(D3DRS_WRAP0, D3DWRAP_U |D3DWRAP_V);
    //SM_D3d::SetRenderState(D3DRS_WRAP1, D3DWRAP_U |D3DWRAP_V);

    SM_D3d::SetRenderState(D3DRS_ALPHATESTENABLE,          FALSE);

    //SM_D3d::SetTextureStageState(0,  D3DTSS_MIPFILTER, D3DTEXF_NONE);
    //SM_D3d::SetTextureStageState(1,  D3DTSS_MIPFILTER, D3DTEXF_NONE);


    
    SM_D3d::Device()->SetTexture(0, ShaderManager::GetD3DTextureFromID(ShaderManager::GetShader(m_iShader)->m_pPasses[1].m_iTextureID));

    SM_D3d::SetRenderState(D3DRS_TEXTUREFACTOR, 0xFF606060);
    SM_D3d::SetTextureStageState(0, D3DTSS_COLOROP  , D3DTOP_MODULATE);
    SM_D3d::SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TFACTOR);
    SM_D3d::SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
    SM_D3d::SetTextureStageState(0, D3DTSS_ALPHAOP  , D3DTOP_SELECTARG1);
    SM_D3d::SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
    SM_D3d::SetTextureStageState(0, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP);
    SM_D3d::SetTextureStageState(0, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP);
  

    
    SM_D3d::SetTextureStageState(1, D3DTSS_COLOROP   , D3DTOP_DISABLE); 
    SM_D3d::SetTextureStageState(1, D3DTSS_ALPHAOP   , D3DTOP_DISABLE); 
    

    SM_D3d::Device()->DrawIndexedPrimitiveUP(
            D3DPT_TRIANGLELIST  ,
            0,
            ROWS*COLUMNS,
            (ROWS-1)*(COLUMNS-1)*2,
            pusIndices,
            D3DFMT_INDEX16 ,
            pVertices,
            sizeof(FVF_PosDiffuseSpecularTex1));   


    SM_D3d::Device()->SetTexture(0, ShaderManager::GetD3DTextureFromID(ShaderManager::GetShader(m_iShader)->m_pPasses[0].m_iTextureID));
    
    SM_D3d::SetTextureStageState(0, D3DTSS_COLOROP  , D3DTOP_DOTPRODUCT3);
    SM_D3d::SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
    SM_D3d::SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
    SM_D3d::SetTextureStageState(0, D3DTSS_ALPHAOP  , D3DTOP_SELECTARG1);
    SM_D3d::SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
    SM_D3d::SetTextureStageState(0, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP);
    SM_D3d::SetTextureStageState(0, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP);
  

    
    SM_D3d::SetTextureStageState(1, D3DTSS_COLOROP   , D3DTOP_MODULATE); 
    SM_D3d::SetTextureStageState(1, D3DTSS_COLORARG1 , D3DTA_CURRENT);
    SM_D3d::SetTextureStageState(1, D3DTSS_COLORARG2 , D3DTA_TEXTURE);
    

    SM_D3d::SetTextureStageState(1, D3DTSS_ALPHAOP  , D3DTOP_SELECTARG1);
    SM_D3d::SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);

    SM_D3d::SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);  
    SM_D3d::SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);  
    SM_D3d::SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);

    
    SM_D3d::Device()->SetTexture(1, 0);
    SM_D3d::Device()->SetTexture(1, ShaderManager::GetD3DTextureFromID(ShaderManager::GetShader(m_iShader)->m_pPasses[1].m_iTextureID));
    
    SM_D3d::Device()->DrawIndexedPrimitiveUP(
            D3DPT_TRIANGLELIST  ,
            0,
            ROWS*COLUMNS,
            (ROWS-1)*(COLUMNS-1)*2,
            pusIndices,
            D3DFMT_INDEX16 ,
            pVertices,
            sizeof(FVF_PosDiffuseSpecularTex1));   

    SM_D3d::Device()->SetTexture(0, ShaderManager::GetD3DTextureFromID(ShaderManager::GetShader(m_iShader)->m_pPasses[0].m_iTextureID));
    SM_D3d::SetTextureStageState(0, D3DTSS_COLOROP  , D3DTOP_DOTPRODUCT3);
    SM_D3d::SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_SPECULAR);
    SM_D3d::SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE);

    SM_D3d::SetTextureStageState(1, D3DTSS_COLOROP  , D3DTOP_MODULATE);
    SM_D3d::SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT);
    SM_D3d::SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);

    SM_D3d::SetTextureStageState(1, D3DTSS_ALPHAOP  , D3DTOP_MODULATE);
    SM_D3d::SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
    SM_D3d::SetTextureStageState(1, D3DTSS_ALPHAARG2, D3DTA_CURRENT);
    SM_D3d::SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);

    SM_D3d::SetRenderState(D3DRS_SRCBLEND , D3DBLEND_SRCALPHA);
    SM_D3d::SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);  
    SM_D3d::SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);

        
    SM_D3d::Device()->DrawIndexedPrimitiveUP(
            D3DPT_TRIANGLELIST  ,
            0,
            ROWS*COLUMNS,
            (ROWS-1)*(COLUMNS-1)*2,
            pusIndices,
            D3DFMT_INDEX16 ,
            pVertices,
            sizeof(FVF_PosDiffuseSpecularTex1));   
    
    SM_D3d::SetTextureStageState(1, D3DTSS_COLOROP  , D3DTOP_DISABLE);    
    SM_D3d::SetTextureStageState(1, D3DTSS_ALPHAOP  , D3DTOP_DISABLE);
    SM_D3d::Device()->SetTexture(0, 0);
    SM_D3d::Device()->SetTexture(1, 0);    


    
    RenderPipeline::Flush();
            
    return;
}
