#include "SM_CommonFXPCH.h"
#include "SM_Engine3DPCH.h"
#include "SM_MathPch.h"
#include "MFastFPU.h"

#if 0

#define ROWS       95
#define COLUMNS    95
#define MAXTRIS    20000
#define ROWSPERDP  (min(ROWS, MAXTRIS/(COLUMNS*2)))
#define NVERTICES  ((ROWS+1)*(COLUMNS+1)) 
#define NPRIMITIVES (COLUMNS*ROWS*2)


Vector3D v3dPositions[NVERTICES];
Vector3D v3dNormals[NVERTICES];


Vector3D v3dDataBuffers[3][NVERTICES];

#define SINTABLESIZE 512
float SINTABLE[SINTABLESIZE];
  
__forceinline float SINF(float x)
{
  int index, index2;
  
  float fOffset = (x*SINTABLESIZE*(1.0/(2*PI)));
  int   iOffset = MFPU_ftol(fOffset);
  index = iOffset;
  index&= (SINTABLESIZE-1);
  index2= index+1;
  index2&= (SINTABLESIZE-1);

  float fLerp = fOffset - float(iOffset);

  return SINTABLE[index]+fLerp*(SINTABLE[index2]-SINTABLE[index]);

  /*
  float f;

  __asm fld x
  __asm fsin
  __asm fstp f
  
  return f;
  */
}

__forceinline float COSF(float x)
{
  return SINF(x+PI/2);
}
  
  __forceinline Evaluate1(float u, float v, Vector3D* pOut)
  {
    u *= 2*PI;
    v *= PI; v -= PI/2.0f;
   
    /*
    pOut->x = u*1.0f;
    pOut->y = v*1.0f;
    pOut->z = 5.0f;
    */
    

    /*
    pOut->x = sinf(u) * (1 + cosf(v)) ;
    pOut->y = sinf(u + 2  / 3) *(1 + cosf(v + 2  / 3)) ;
    pOut->z = sinf(u + 4  / 3) *(1 + cosf(v + 4  / 3)) ;
    */
    
    pOut->x = sinf(u)*cosf(v);
    pOut->y = cosf(u)*cosf(v);
    pOut->z = sinf(v);                
  }

  __forceinline Evaluate2(float u, float v, Vector3D* pOut)
  {
    u *= 2*PI ; u-=PI;
    v *= 2*PI ; v-=PI;
   
    pOut->x = 2 * sinf(3 * u) / (2 + cosf(v));
    pOut->y = 2 * (sinf(u) + 2 * sinf(2 * u)) / (2 + cosf(v + 2 * PI / 3));
    pOut->z = (cosf(u) - 2 * cosf(2 * u)) * (2 + cosf(v)) * (2 + cosf(v + 2 * PI / 3)) / 4;          
  }

  __forceinline Evaluate3(float u, float v, Vector3D* pOut)
  {
   u *= 2*PI ; u-=PI;
   v *= 2*PI ; v-=PI;
   float pp = sqrtf(u*u + v*v) / sqrt(2*PI*PI);

   pOut->x = 4.0f*((1-pp)*cosf(u)*cosf(v)+pp*sinf(u)*sinf(v));
   pOut->y = 4.0f*((1-pp)*cosf(u+2*PI/3)*cosf(v+2*PI/3)+pp*sinf(u+2*PI/3)*sinf(v+2*PI/3));
   pOut->z = (1-pp)*cosf(u+4*PI/3)*cosf(v+4*PI/3)+pp*sinf(u+4*PI/3)*sinf(v+4*PI/3);
  }  

  __forceinline Evaluate4(float u, float v, Vector3D* pOut)
  {
    u *= 2.0f*PI;
    v *= 0.5f*PI;

    pOut->x = 6.0f*cosf(u) * sinf(2*v);
    pOut->y = 6.0f*sinf(u) * sinf(2*v);
    pOut->z = 6.0f*cosf(v) * cosf(v) - cosf(u) * cosf(u) * sinf(v) * sinf(v);
  }

  __forceinline Evaluate5(float u, float v, Vector3D* pOut)
  {
    u *= 2.0f*PI;
    v *= 0.8f; v -= 0.4f;

    pOut->x = 4.0f*cosf(u)+v*cosf(u*0.5f)*cosf(u);
    pOut->y = 4.0f*sinf(u)+v*cosf(u*0.5f)*sinf(u);
    pOut->z = 4.0f*v*sinf(u*0.5f);
  }
  

  __forceinline Evaluate6(float u, float v, Vector3D* pOut)
  {
    u *= 2*PI ; u-=PI;
    v *= 2*PI ; v-=PI;
   

    pOut->x = sinf(u) * (1 + cosf(v));
    pOut->y = sinf(u + 2.0f  / 3.0f) * (1.0f + cosf(v + 2.0f  / 3.0f));
    pOut->z = sinf(u + 4.0f  / 3.0f) * (1.0f + cosf(v + 4.0f  / 3.0f));
  }

  
  __forceinline Evaluate7(float u, float v, Vector3D* pOut)
  {
    float a=10.0f, b=5.0f, c=4.0f;
    u *= 2*b*c;
    v *= 4*PI;

    pOut->x = 1.0f*cosf(u/c) * cosf(u/b) * (a + cosf(v)) + sinf(u/b) * sinf(v) * cosf(v);
    pOut->y = 1.0f*sinf(u/c) * cosf(u/b) * (a + cosf(v)) + sinf(u/b) * sinf(v) * cosf(v);
    pOut->z = 10.0f+1.0f*-sinf(u/b) * (a + cosf(v)) + cosf(u/b) * sinf(v) * cosf(v);
  }


  __forceinline Evaluate8(float u, float v, Vector3D* pOut)
  {
     float r = 0;
     float theta = 2*PI*u;
     float phi = v*PI;
  
     static float m[8] = 
     {
        1.0, 10.0f, 4.0f, 29.0f, 1.0f, 11.0f, 1.0f, 13.0f
     };

     r += powf(sinf(m[0]*phi),(double)m[1]);
     r += powf(cosf(m[2]*phi),(double)m[3]);
     r += powf(sinf(m[4]*theta),(double)m[5]);
     r += powf(cosf(m[6]*theta),(double)m[7]);

     pOut->x = r * sinf(phi) * cosf(theta);
     pOut->y = r * cosf(phi);
     pOut->z = r * sinf(phi) * sinf(theta);
  }

  __forceinline Evaluate9(float u, float v, Vector3D* pOut)
  {
     float r = 0;
     float theta = 2*PI*u;
     float phi = v*PI;
  
     static float m[8] = 
     {
        3.0, 10.0f, 2.0f, 6.0f, 7.0f, 11.0f, 4.0f, 13.0f
     };

     r += powf(sinf(m[0]*phi),(double)m[1]);
     r += powf(cosf(m[2]*phi),(double)m[3]);
     r += powf(sinf(m[4]*theta),(double)m[5]);
     r += powf(cosf(m[6]*theta),(double)m[7]);

     pOut->x = r * sinf(phi) * cosf(theta);
     pOut->y = r * cosf(phi);
     pOut->z = r * sinf(phi) * sinf(theta);
  }

  __forceinline Evaluate10(float u, float v, Vector3D* pOut)
  {
     float r = 0;
     float theta = 2*PI*u;
     float phi = v*PI;
  
     static float m[8] = 
     {
        //5.0f, 0.0f, 5.0f, 1.0f, 3.0f, 3.0f, 3.0f
       0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
     };

     /*
     r += powf(sinf(m[0]*phi),(double)m[1]);
     r += powf(cosf(m[2]*phi),(double)m[3]);
     r += powf(sinf(m[4]*theta),(double)m[5]);
     r += powf(cosf(m[6]*theta),(double)m[7]);
     */

     r = 1.0f;

     pOut->x = r * sinf(phi) * cosf(theta);
     pOut->y = r * cosf(phi);
     pOut->z = r * sinf(phi) * sinf(theta);
  }

  __forceinline PosMorph(float fMorph, float u, float v, Vector3D* pOut)
  {
    Vector3D v1, v2;
    Evaluate8(u, v, &v1);
    Evaluate9(u, v, &v2);

    *pOut = v1+fMorph*(v2-v1);        
  }




#define sinf SINF
#define cosf COSF


struct WeirdGeometry
{
  virtual void Evaluate(float u, float v, Vector3D* pOut) = 0;
  virtual void GenerateData(Vector3D* pv3d, unsigned uStart, unsigned uRange)
  {
    unsigned i,j;

    float v  = 0;
    float du = (1.0f)/(COLUMNS);
    float dv = (1.0f)/(ROWS);

    Vector3D* pOut = pv3d;
    
    for (j = 0 ; j <= ROWS ; j++)
    {
      float u  = 0;
    
      for (i = 0 ; i <= COLUMNS ; i++)
      {        
        Evaluate(u, v, pOut);

        pOut++;

        u += du;
      }

      v += dv;
    }
  }
};

struct WeirdGeometry_1 : public WeirdGeometry
{
  virtual void Evaluate(float u, float v, Vector3D* pOut)
  {
    Evaluate4(u, v, pOut);
  }  
};

struct WeirdGeometry_2 : public WeirdGeometry
{
  virtual void Evaluate(float u, float v, Vector3D* pOut)
  {
    Evaluate6(u, v, pOut);
  }  
};



class WeirdGeometryFx : public SM_DemoEffect
{
public:              
  WeirdGeometryFx(char const* pcName) : SM_DemoEffect(pcName)
  {
  }

  virtual          ~WeirdGeometryFx()
  {
  }

  int      Init(const char* pcCommand)
  {
    char* pcCopy =0;
    char* pcToken;
    int   iReturn=-1;
    
    pcCopy=new char[strlen(pcCommand)+1];
    strcpy(pcCopy, pcCommand);

    pcToken=strtok(pcCopy, " \t");

    if (pcToken && strcmp(pcToken, "FILL THIS UP")==0)
    {
    }

    ShaderManager::LoadShader("enviroment");

    iReturn  = 0;

    unsigned i;

    for (i = 0 ; i < SINTABLESIZE ; i++)
    {
      float fNorm = float(2)*PI*float(i)/float(SINTABLESIZE);
      SINTABLE[i] = (float) sin(fNorm);
    }

    /*
    for (float f = 0 ; f < 2*3.14 ; f+=0.05)
    {
      SM_Main::OutputDebug("sin: %f ; SINF: %f\n", sin(f), sinf(f));
      SM_Main::OutputDebug("cos: %f ; COSF: %f\n", cos(f), cosf(f));
    }
    */
    
//FAILED:
    if (pcCopy) { delete[] pcCopy; pcCopy=0; }

    return iReturn;        
  }

  int      Shutdown()
  {
    return (0);
  }

  int      Start(float fTime)
  {
    return (0);
  }

  int      Stop()
  {
    return (0);
  }

  int      Reset()
  {
    return 0;
  }

  /*
  __forceinline PosAndNormalMorph(float fMorph, float u, float v, Vector3D* pOut, Vector3D* pNormal)
  {
    PosMorph(fMorph, u, v, pOut);

    #define DELTAU 0.005f
    #define DELTAV 0.005f
    
    u += DELTAU;
    v += DELTAV;

    Vector3D vdu,vdv;
    PosMorph(fMorph, u+DELTAU, v, &vdu); 
    PosMorph(fMorph, u, v+DELTAV, &vdv); 

    *pNormal = Vector3D::Cross(vdv-*pOut, vdu-*pOut).Normalize();    
  }
  */

  int      Run(float fTime)
  {
    static FVF_PosNormalDiffuseTex1 pVertices[(ROWSPERDP+1)*(COLUMNS+1)];    
    static unsigned short pusIndices[ROWSPERDP*COLUMNS*6];
    static bool bIndices = false;
    static bool bGeometries = false;

    // Fill out index list
    if (!bIndices)
    {
      unsigned i,j;
      unsigned short* pI = pusIndices;
      unsigned uCurrent = 0;
      for (j = 0 ; j<ROWS ; j++, uCurrent+=COLUMNS+1)
      {
        for (i = 0 ; i < COLUMNS ; i++)
        {
          /*
          unsigned uV = uCurrent+i;
          pI[0] = uV;
          pI[1] = uV+1;
          pI[2] = uV+COLUMNS+1;
          pI[3] = uV+COLUMNS+1;
          pI[4] = uV+1;
          pI[5] = uV+COLUMNS+2;
          pI+=6;
          */
          unsigned uV = uCurrent+i;


          
            pI[0] = uCurrent+i;
            pI[1] = uCurrent+((i+1)%COLUMNS);
            pI[2] = uCurrent+i+COLUMNS+1;
            pI[3] = uCurrent+i+COLUMNS+1;
            pI[4] = uCurrent+((i+1)%COLUMNS);
            pI[5] = uCurrent+((i+1)%COLUMNS)+COLUMNS+1;
            pI+=6;
          
          
        }
      }

      bIndices = true;
    }

    if (!bGeometries)
    {
      WeirdGeometry_1 GeoA;
      WeirdGeometry_2 GeoB;

      GeoA.GenerateData(v3dDataBuffers[0], 0, 0);
      GeoB.GenerateData(v3dDataBuffers[1], 0, 0);

      bGeometries =true;
    }


    RenderContext RC;   

    RC.Set(
      Vector3D(0.0f*sinf(Timer::GetTime()), 0.0f*cosf(Timer::GetTime()), 0.0f),
    Quaternion(0.0f, Vector3D(0.0f, 0.0f, 1.0f)),
    90,
    0.75f,
    1.0f,
    200.0f);

    RC.SetViewport(0, 0, 640.0f, 480.0f);  
  
    RC.SyncRasterizer();
    RC.UpdateFrustum();

    assert(ROWSPERDP == ROWS);

    
    // Fill out positions/normals
    unsigned i;

    /*
    
    float v  = 0;
    float du = (1.0f)/(COLUMNS);
    float dv = (1.0f)/(ROWS);

    Vector3D* pOut = v3dData;
    float fLerp = 0.5+sinf(Timer::GetTime())*0.5;
    for (j = 0 ; j <= ROWSPERDP ; j++)
    {
      float u  = 0;
    
      for (i = 0 ; i <= COLUMNS ; i++)
      {        
        PosAndNormalMorph(fLerp, u, v, pOut, pOut+1);

        pOut += 2;

        u += du;
      }

      v += dv;
    }
    */

    float fLerp = 0.5+sinf(Timer::GetTime())*0.5;    
    for (i = 0 ; i < NVERTICES ; i++)
    {
      Vector3D* pA = v3dDataBuffers[0];
      Vector3D* pB = v3dDataBuffers[1];

      v3dPositions[i] = pA[i]+fLerp*(pB[i]-pA[i]);
    }

    Vector3D* pvNormal=new Vector3D[NPRIMITIVES*3];
    int iTri;
    for (i=0, iTri=0 ; i<NPRIMITIVES*3 ; i+=3, iTri++)
    {
      pvNormal[iTri]=
        Vector3D::Cross(
          v3dPositions[pusIndices[i+1]]-v3dPositions[pusIndices[i]],
          v3dPositions[pusIndices[i+2]]-v3dPositions[pusIndices[i]]
          );
    }

    Vector3D* pvVertexNormal=new Vector3D[NVERTICES];
    memset(pvVertexNormal, 0, sizeof(Vector3D)*NVERTICES);
    for (i=0, iTri=0 ; i<NPRIMITIVES*3 ; i+=3, iTri++)
    {
      pvVertexNormal[pusIndices[i]]  +=pvNormal[iTri];
      pvVertexNormal[pusIndices[i+1]]+=pvNormal[iTri];
      pvVertexNormal[pusIndices[i+2]]+=pvNormal[iTri];
    }

    for (i=0 ; i<NVERTICES ; i++)
    {
      v3dNormals[i] = pvVertexNormal[i].Normalize();
    }

    delete[] pvNormal;
    delete[] pvVertexNormal;

    MeshElement me;
        
    me.m_iShader            =ShaderManager::FindShader("enviroment");
    me.m_iVB                =-1;
    me.m_iIB                =-1;
    me.m_pIndices           =pusIndices;
    me.m_pVertices          =pVertices;
    me.m_uStartVertex       =0;
    me.m_uVertices          =NVERTICES;
    me.m_uStartIndex        =0;
    me.m_uPrimitives        =NPRIMITIVES;
    me.m_uActiveLightMask   =-1;  
    me.m_fDepth             =0.0f;

    Vector3D Pos(0.0f*sinf(Timer::GetTime()), 0.0f, 8.0f);
    Quaternion q(Timer::GetTime(), Vector3D(sinf(27.0f), cosf(27.0f), 0.0f));

    ToTransform(&me.m_WorldTransform, &Pos, &q);
    
    Vector3D* pV = v3dPositions;
    Vector3D* pN = v3dNormals;

    /*
    FVF_PosNormalDiffuseTex1* pG = pVertices;
    for (i=0 ; i<NVERTICES ; i++)
    {      
      pG ->x=pV->x; pG ->y=pV->y; pG ->z=pV->z; 
      pV++;
      pG ->nx=pN->x; pG ->ny=pN->y; pG ->nz=pN->z;
      pN++;
      pG ->diffuse=0xFFFFFFFF;
      pG++;                     
    }    
    assert(pG == pVertices + (uRows+1)*(COLUMNS+1));
  
    */

    FVF_PosNormalDiffuseTex1* pDVBVertices;
    unsigned short*           pDIBIndices;
    int                       iVertexOffset, iIndexOffset;        
    
    // Allocate VB and IB
    if ((iVertexOffset=ResourceManager::GiveVBChunk(ResourceManager::m_iPosNormalDiffuseTex1Stream, NVERTICES*sizeof(FVF_PosNormalDiffuseTex1), (void**)&pDVBVertices))==-1)
    {
      assert(0);
    }

    if ((iIndexOffset=ResourceManager::GiveIBChunk(ResourceManager::m_iIndexStream, NPRIMITIVES*3*sizeof(unsigned short), (void**)&pDIBIndices))==-1)
    {
      assert(0);
    }

    FVF_PosNormalDiffuseTex1* pG = pDVBVertices;
    for (i=0 ; i<NVERTICES ; i++)
    {      
      pG ->x=pV->x; pG ->y=pV->y; pG ->z=pV->z; 
      pV++;
      pG ->nx=pN->x; pG ->ny=pN->y; pG ->nz=pN->z;
      pN++;
      pG ->diffuse=0xFFFFFFFF;
      pG++;                     
    }    

    memcpy(pDIBIndices, pusIndices, sizeof(unsigned short)*NPRIMITIVES*3);

    ResourceManager::DoneVBChunk(ResourceManager::m_iPosNormalDiffuseTex1Stream);
    ResourceManager::DoneIBChunk(ResourceManager::m_iIndexStream);

    unsigned uStartIndex =iIndexOffset/sizeof(unsigned short);
    unsigned uStartVertex=iVertexOffset/sizeof(FVF_PosNormalDiffuseTex1);

    me.m_iVB          =ResourceManager::m_iPosNormalDiffuseTex1Stream;
    me.m_iIB          =ResourceManager::m_iIndexStream;
    me.m_pIndices     =0;
    me.m_pVertices    =0;    
    me.m_uStartVertex = uStartVertex;
    me.m_uStartIndex  = uStartIndex;
 
    RenderPipeline::Render(&me);
    RenderPipeline::Flush();
    

    return 1;
  }

  int      Command           (float fTime, const char* pcCommand)
  {
    SM_DemoEffect::Command(fTime, pcCommand);
    char* pcCopy =0;
    char* pcToken;
    int   iReturn=-1;
    
    pcCopy=new char[strlen(pcCommand)+1];
    strcpy(pcCopy, pcCommand);

    pcToken=strtok(pcCopy, " \t");

    if (strcmp(pcToken, "FILL THIS UP")==0)
    {
    }

    iReturn  = 0;
    
    if (pcCopy) { delete[] pcCopy; pcCopy=0; }
    return iReturn;
  }  
};

DEFINE_EFFECT(WeirdGeometryFX)
WeirdGeometryFx Tunel("WEIRDGEM");

/*
    // Paint a poly example!!! 
    RenderContext RC;   

    RC.Set(
    Vector3D(0.0f, 0.0f, 0.0f),
    Quaternion(1.0f, 0.0f, 0.0f, 0.0f),
    90,
    0.75f,
    1.0f,
    200.0f);

    RC.SetViewport(0, 0, 640.0f, 480.0f);  
  
    RC.SyncRasterizer();
    RC.UpdateFrustum();

    D3DVIEWPORT8 Viewport;
    Viewport.X      =0;
    Viewport.Y      =0;
    Viewport.Width  =640;
    Viewport.Height =480;
    Viewport.MinZ   =0.0f;
    Viewport.MaxZ   =1.0f;

    SM_D3d::Device()->SetViewport(&Viewport);  

    MeshElement me;

    FVF_PosNormalDiffuseTex1 pVertices[3];
    unsigned short pusIndices[3]={0,2,1};

    pVertices[0].x=0.0f; pVertices[0].y=0.0f; pVertices[0].z=10.0f; 
    pVertices[0].nx=0.0f; pVertices[0].ny=0.0f; pVertices[0].nz=1.0f;
    pVertices[0].u=0.0f; pVertices[0].v=0.0f; pVertices[0].diffuse=0xFFFFFFFF;

    pVertices[1].x=0.0f; pVertices[1].y=10.0f; pVertices[1].z=10.0f; 
    pVertices[1].nx=0.0f; pVertices[1].ny=0.0f; pVertices[1].nz=1.0f;
    pVertices[1].u=0.0f; pVertices[1].v=0.0f; pVertices[1].diffuse=0xFFFFFFFF;

    pVertices[2].x=10.0f; pVertices[2].y=10.0f; pVertices[2].z=10.0f; 
    pVertices[2].nx=1.0f; pVertices[2].ny=0.0f; pVertices[2].nz=0.0f;
    pVertices[2].u=0.0f; pVertices[2].v=0.0f; pVertices[2].diffuse=0xFFFFFFFF;
    
    me.m_iShader            =ShaderManager::FindShader("NULL");
    me.m_iVB                =-1;
    me.m_iIB                =-1;
    me.m_pVertices          =pVertices; 
    me.m_pIndices           =pusIndices;
    me.m_uStartVertex       =0;
    me.m_uVertices          =3;
    me.m_uStartIndex        =0;
    me.m_uPrimitives        =1;
    me.m_WorldTransform     =Matrix4X4::Identity;
    me.m_uActiveLightMask   =-1;  
    me.m_fDepth             =0.0f;

    RenderPipeline::Render(&me);
    RenderPipeline::Flush();


*/
#endif