#include "SM_CommonFXPCH.h"
#include "SM_DemoEffect.h"
#include "SM_MaxScene.h"
#include "SM_Main.h"
#include "MSystemFunctions.h"
#include "SM_Engine3DPCH.h"
#include "FX3DFont.h"
#include "FXEscena3D.h"
#include "MBinArray.h"


#define BUFFEREDGLYPHS 32


char* g_pcLanguage=".txt";


namespace TextManager
{  

  void SetLanguage(char* pcLanguage)
  {
    g_pcLanguage=pcLanguage;
  }

  
  struct TextEntry
  {
    

    struct Command
    {
      enum eType
      {
        E_TEXT,
        E_BR,
        E_COLOR,
      };

      Command()
      {
        m_eType=E_COLOR;
      }

      ~Command()
      {
        if (m_eType==E_TEXT && m_pcText)
        {
          delete[] m_pcText;
        }
      }

      eType m_eType;
      union
      {
          unsigned uColor;
          char*    m_pcText;
      };
    };

    TextEntry()
    {      
    }

    ~TextEntry()
    {
      Shutdown();
    }

    int Init()
    {
      return 0;
    }

    int Shutdown()
    {
      Command** pCommands;

      pCommands=m_baCommands.Get();

      unsigned i;
      for (i=0 ; i<m_baCommands.Used() ; i++)
      {
        delete pCommands[i];        
      }   
      
      if (m_pcId) { delete[] m_pcId; m_pcId=0; }
      return 0;
    }


    char*               m_pcId;
    MBinArray<Command*> m_baCommands;
    unsigned            m_uChars;
  };

  MBStaticList<TextEntry*> m_slEntries;

  int Init      (const char* pcFilename)
  {
    MetaFile ml;

    char pcFinalName[MAX_PATH];
    MExtractPathBasename(pcFinalName, (char*)pcFilename);
    strcat(pcFinalName, g_pcLanguage);

    if (ml.Load(pcFinalName)!=0)
    {
      return -1;
    }

    m_slEntries.Init();

    MetaFileNode* pRoot=ml.Root();
    unsigned i,j;
    for (i=0 ; i<pRoot->m_Sons.size() ; i++)
    {      
      if (pRoot->m_Sons[i]->IsTag("TEXT"))
      {
        MetaFileNode* pTextNode=pRoot->m_Sons[i];


        TextEntry* pTextEntry=new TextEntry;
        if (!pTextEntry)
        {
          return -1;
        }        

        pTextEntry->m_uChars=0;
        
        MetaFileNode* pID=pTextNode->GetSon("ID");
        if (!pID)
        {
          SM_Main::OutputError("El texto tiene que tener <ID>\"nombretexto\"</ID>");
        }

        char* pcName=pID->m_Sons[0]->m_Token.m_pszString;
        pTextEntry->m_pcId=new char[strlen(pcName)+1];
        if (!pTextEntry->m_pcId)
        {
          return -1;
        }
        strcpy(pTextEntry->m_pcId, pcName);

        MetaFileNode* pData=pTextNode->GetSon("DATA");
        if (pData)
        {
          for (j=0 ; j<pData->m_Sons.size() ; j++)
          {
            TextEntry::Command* pCommand=new TextEntry::Command;
            if (!pCommand)
            {
              return -1;
            }

            if (pData->m_Sons[j]->IsTag("COLOR"))
            {
              pCommand->m_eType =TextEntry::Command::E_COLOR;
              float r=pData->m_Sons[j]->m_Sons[0]->m_Token.m_fValue;
              float g=pData->m_Sons[j]->m_Sons[1]->m_Token.m_fValue;
              float b=pData->m_Sons[j]->m_Sons[2]->m_Token.m_fValue;

              pCommand->uColor=
                (int(r*255.0f)<<16)|
                (int(g*255.0f)<< 8)|
                (int(b*255.0f)<< 0);
            }
            else if (pData->m_Sons[j]->IsTag("BR"))
            {
              pCommand->m_eType =TextEntry::Command::E_BR;
            }
            else
            {
              pCommand->m_eType =TextEntry::Command::E_TEXT;
              pCommand->m_pcText=new char[strlen(pData->m_Sons[j]->m_Token.m_pszString)+1];
              strcpy(pCommand->m_pcText, pData->m_Sons[j]->m_Token.m_pszString);
              pTextEntry->m_uChars+=strlen(pCommand->m_pcText);
            }
            pTextEntry->m_baCommands.Add(pCommand);
          }

          
        }

        m_slEntries.InsertTail(pTextEntry);
      }
    }

    return 0;
  }

  int Shutdown  ()
  {
    int iIterator;
    TextEntry* pEntry;

    int iNext;
    for (iIterator=m_slEntries.First() ; iIterator!=-1 ; iIterator=iNext)
    {
      iNext=m_slEntries.Next(iIterator);
      m_slEntries.Get(iIterator, pEntry);
      delete pEntry;
      m_slEntries.Delete(iIterator);
    }
    return 0;
  }


  TextEntry* GetEntry(char* pcName)
  {
    int iIterator;
    TextEntry* pEntry;

    for (iIterator=m_slEntries.First() ; iIterator!=-1 ; iIterator=m_slEntries.Next(iIterator))
    {
      m_slEntries.Get(iIterator, pEntry);

      if (strcmp(pcName, pEntry->m_pcId)==0)
      {
        return pEntry;
      }
    }

    return 0;
  }

  
}



class Text3DFx : public SM_DemoEffect
{
public:              
  struct Glyph
  {
    D3DGeometry* m_pGeometry;
    AABox        m_AABB;
  };

  Text3DFx(char const* pcName) : SM_DemoEffect(pcName)
  {
    m_bLoaded        =false;
    m_uBufferedOffset=0;
    m_pFont          =0;
    m_pEscenaTarget  =0;
    m_fScale         =1.0f;
  
  }

  virtual          ~Text3DFx()
  {
  }

  void             Flush();

  void RenderGlyph(char iGlyph, Vector3D* v3d, Quaternion* q, unsigned uAlphaColor)
  {
    unsigned i;

    D3DGeometry* pGeometry;

    pGeometry=m_pFont->m_Glyphs[(unsigned char)iGlyph].m_pGeometry;
    if (!pGeometry)
    {
      return;
    }

   
    for (i=0 ; i<pGeometry->m_uMeshElements ; i++)
    {            
      pGeometry->m_pMeshElements[i].m_fDepth=0.0f;
        
      ToTransform(&pGeometry->m_pMeshElements->m_WorldTransform, v3d, q, m_fScale);
      ShaderManager::SetTFactorForShader(pGeometry->m_pMeshElements->m_iShader, uAlphaColor);
      RenderPipeline::Render(&(pGeometry->m_pMeshElements[i]));
    }  

    RenderPipeline::Flush();
  }

  
  int      Init(const char* pcCommand)
  {
    return (0);
  }

  int      Shutdown()
  {
    return (0);
  }

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

  int      Stop()
  {
    return (0);
  }

  int      Reset()
  {
    return (0);
  }

  int      Run(float fTime)
  {
    if (!m_pFont || !m_pFont->m_bLoaded)
    {
      SM_Main::OutputError("%s esta usando una font que no existe", m_pcInstanceName);
      return -1;
    }

    if (!m_pFont->m_bLoaded)
    {
      SM_Main::OutputError("%s esta usando una font que no esta cargada", m_pcInstanceName);
      return -1;
    }

    if (!m_pEscenaTarget)
    {
      SM_Main::OutputError("%s no tiene asignada una escena", m_pcInstanceName);
      return -1;
    }

    if (!m_pTextEntry)
    {
      SM_Main::OutputError("%s no tiene asignado ningun texto", m_pcInstanceName);
      return -1;
    }

    RenderContext RC;   
    m_pEscenaTarget->GetScene()->UpdateRC(&RC, fTime);


    D3DMesh* pMesh=m_pEscenaTarget->GetScene()->GetMesh(m_pcTargetName);
    if (!pMesh)
    {
      SM_Main::OutputError("la escena asignada no tiene un objeto que se llame %s", m_pcTargetName);
      return -1;
    }

    
    Vector3D    v;
    Quaternion  q;
    pMesh->GetKeyFrame(fTime, &v, &q);
    ToTransform(&m_Transform, &v, &q);            
    /*
   
    RC.Set(
    //  			         Vector3D(-118.322632       ,    -56.037056 ,         -655.182373),
		//	              Quaternion(-0.697936         ,    0.005628 ,            0.005485,             0.716117),
    Vector3D(00.0f, 0.0f, -80.0f+70.0*sinf(fTime)),
    Quaternion(1.0f, 0.0f, 0.0f, 0.0f),
    90,
    0.75f,
    1.0f,
    200.0f);
  
    RC.SetViewport(0, 0, 640, 480);
    RC.SyncRasterizer();
    RC.UpdateFrustum();
    */           

    float fOffsetX=0.0f;
    float fOffsetY=0.0f;

    Vector3D v3dRight(m_Transform.m_11, m_Transform.m_21, m_Transform.m_31);
    Vector3D v3dUp   (m_Transform.m_13, m_Transform.m_23, m_Transform.m_33);

    
    TextManager::TextEntry::Command** pCommand;

    pCommand=m_pTextEntry->m_baCommands.Get();

    
    float fAlpha=0.0f;
    float fDelta=0.0f;
    if (m_bFadeIn)
    {
      float fOffset;
      m_FadeSlader.GetAbsValue(fTime, &fOffset);

      
      fAlpha=fOffset;
      fDelta=(-1.0f/255.0f);;


      //
      //fAlpha=(m_pTextEntry->m_uChars+255.0f)*fOffset+255.0f*fDelta;            

      //fAlpha=fOffset;
      //fDelta=0.0f;      
    }
    else if (m_bFadeOut)
    {
      float fOffset;
      m_FadeSlader.GetAbsValue(fTime, &fOffset);

      
      fAlpha=1.0f-fOffset;
      fDelta=1.0f/255.0f;
    }



    /*
      m_bFadeOut=false;  
      m_FadeSlader.Init(atof(pcToken));
      m_FadeSlader.Start(fTime);
      */

    unsigned uColor=0xFFFFFF;
    unsigned j;
    for (j=0 ; j<m_pTextEntry->m_baCommands.Used() ; j++)
    {
      switch (pCommand[j]->m_eType)
      {
        case TextManager::TextEntry::Command::E_TEXT:
        {
          int iLen=strlen(pCommand[j]->m_pcText);

          unsigned uAlphaColor;

          
          for (int i=0 ; i<iLen ; i++)
          {      
            uAlphaColor=(int(max(0.0f, min(1.0f, fAlpha))*255.0f)<<24)|uColor;

            if (pCommand[j]->m_pcText[i]==' ')
            {
              fOffsetX+=6.5f*m_fScale;
            }
            else
            {
              RenderGlyph((unsigned char)pCommand[j]->m_pcText[i], 
                &(v+v3dRight*fOffsetX+v3dUp*fOffsetY), &Quaternion::IDENTITY, uAlphaColor);
              

              unsigned uGlyph=(unsigned char)(pCommand[j]->m_pcText[i]);
              fOffsetX+=(m_pFont->m_Glyphs[uGlyph].m_AABB.m_v3dMax.x+0.5f)*m_fScale;
            }

            fAlpha+=fDelta;
          }


          break;
        }
        case TextManager::TextEntry::Command::E_BR:
        {
          fOffsetX=0;
          fOffsetY+=m_pFont->m_fLineSeparation*m_fScale;
          break;
        }
        case TextManager::TextEntry::Command::E_COLOR:
        {
          uColor=pCommand[j]->uColor;
          break;
        }
      }
    }

    /*
    int iLen=strlen(m_pcName);
    for (int i=0 ; i<iLen ; i++)
    //for (int i='A' ; i<'Z' ; i++)
    {
      
      if (m_pcName[i]==' ')
      {
        fOffset+=6.5f*m_fScale;
      }
      else
      {
        RenderGlyph(m_pcName[i], 
          &(v+v3dRight*fOffset), &Quaternion::IDENTITY);

        fOffset+=(m_pFont->m_Glyphs[m_pcName[i]].m_AABB.m_v3dMax.x+0.5f)*m_fScale;
      }
    }
    */

    
    return (1);
  }

  int      Command           (float fTime, const char* pcCommand)
  {
    SM_DemoEffect::Command(fTime, pcCommand);

    char* pcToken;

    char* pcAux=new char[strlen(pcCommand)+1];
    strcpy(pcAux, pcCommand);

    pcToken=strtok(pcAux, " \t");
    if (strcmp(pcToken, "SETFONT")==0)
    {
      pcToken=strtok(0, " \t");

      m_pFont=(Font3DFx*) GetEffect(pcToken);
    }
    else if (strcmp(pcToken, "SETTARGET")==0)
    {
      pcToken=strtok(0, " \t");

      m_pEscenaTarget=(Escena3D*) GetEffect(pcToken);
      if (!m_pEscenaTarget)
      {
        SM_Main::OutputError("Escena %s no existe!", pcToken);
        return -1;
      }

      pcToken=strtok(0, " \t");
      strcpy(m_pcTargetName, pcToken);
    }
    else if (strcmp(pcToken, "SETTEXT")==0)
    {
      m_pTextEntry= TextManager::GetEntry(pcToken+strlen(pcToken)+1);

      if (!m_pTextEntry)
      {
        SM_Main::OutputError("Texto con id %s no existe!", pcToken+strlen(pcToken)+1);
        return -1;
      }
    }
    else if (strcmp(pcToken, "FADEIN")==0)
    {
      pcToken=strtok(0, " \t");

      m_bFadeIn =true;  
      m_bFadeOut=false;  
      m_FadeSlader.Init((float)atof(pcToken));
      m_FadeSlader.Start(fTime);
    }
    else if (strcmp(pcToken, "FADEOUT")==0)
    {
      pcToken=strtok(0, " \t");

      m_bFadeIn =false;  
      m_bFadeOut=true;        
      m_FadeSlader.Init((float)atof(pcToken));
      m_FadeSlader.Start(fTime);
    }
    else if (strcmp(pcToken, "SETSCALE")==0)
    {
      pcToken=strtok(0, " \t");
      m_fScale=(float)atof(pcToken);
    }

    delete[] pcAux;

    return 0;
  } 
  
  
  bool                    m_bLoaded;
  Font3DFx*               m_pFont;
  Escena3D*               m_pEscenaTarget;
  char                    m_pcTargetName[256];
  TextManager::TextEntry* m_pTextEntry;
  LinearSlider            m_FadeSlader;
  bool                    m_bFadeIn;
  bool                    m_bFadeOut;

  
  float   m_fScale;

  MeshElement m_Buffered[BUFFEREDGLYPHS];
  int         m_uBufferedOffset;
  Matrix4X4   m_Transform;

};

DEFINE_EFFECT(Text3DFx)

Text3DFx Text00("TEXT_00");
Text3DFx Text01("TEXT_01");
Text3DFx Text02("TEXT_02");
Text3DFx Text03("TEXT_03");
Text3DFx Text04("TEXT_04");
Text3DFx Text05("TEXT_05");
Text3DFx Text06("TEXT_06");
