#ifndef __SMSHADER

#define __SMSHADER

struct MeshElement;
class  RenderContext;

#include "SM_VertexShader.h"

struct FVF_PosNormalDiffuseTex1;


namespace ShaderManager
{
  // Shader priority
  enum eShaderPriority
  {
    PR_FIRST       = 0,
    PR_OPAQUE      = 3,
    PR_ADDITIVE    = 6,
    PR_ADDITIVE2   = 7,
    PR_ADDITIVE3   = 8,
    PR_TRANSPARENT = 12,
    PR_TRANSPARENT2= 13,
    PR_TRANSPARENT3= 14,
    PR_LAST        = 15,
    PR_LEVELS      = 16
  };

  enum eWaveType
  {
    WF_CONSTANT,
    WF_SIN,
    WF_TRIANGLE,
    WF_SQUARE,
    WF_SAWTOOTH,
    WF_INVSAWTOOTH
  };

  enum ePositionGen
  {
    PG_PASSTHRU,
    PG_AUTOSPRITE
  };

  enum eNormalGen
  {
    NG_PASSTHRU,
    NG_DISTORT
  };

  enum eShaderTexGen
  {
    TG_PASSTHRU,
    TG_SPHEREREFLECTION,
    TG_CAMERAREFLECTION,
    TG_CUBEREFLECTION
  };

  enum eVertexColorGen
  {
    CG_PASSTHRU,
    CG_LIGHTING
  };

  enum eShaderType
  {
    ST_OPAQUE,
    ST_ADDITIVE,
    ST_TRANSPARENT
  };

  enum eFillMode
  {
    RM_SOLID              =D3DFILL_SOLID,
    RM_WIREFRAME          =D3DFILL_WIREFRAME        
  };

  enum eShadingMode
  {
    SM_GOURAUD            = D3DSHADE_GOURAUD,
    SM_FLAT               = D3DSHADE_FLAT
  };

  enum eZComp
  {
    ZC_ALWAYS              =D3DCMP_ALWAYS ,
    ZC_LESSEQUAL           =D3DCMP_LESSEQUAL ,
    ZC_LESS                =D3DCMP_LESS 

  };


  enum eBlend
  {
    BL_ZERO               =D3DBLEND_ZERO            ,
    BL_ONE                =D3DBLEND_ONE             ,
    BL_SRCCOLOR           =D3DBLEND_SRCCOLOR        ,  
    BL_INVSRCCOLOR        =D3DBLEND_INVSRCCOLOR     ,
    BL_SRCALPHA           =D3DBLEND_SRCALPHA        ,  
    BL_INVSRCALPHA        =D3DBLEND_INVSRCALPHA     ,
    BL_DESTALPHA          =D3DBLEND_DESTALPHA       ,  
    BL_INVDESTALPHA       =D3DBLEND_INVDESTALPHA    ,
    BL_DESTCOLOR          =D3DBLEND_DESTCOLOR       ,
    BL_INVDESTCOLOR       =D3DBLEND_INVDESTCOLOR    ,
    BL_SRCALPHASAT        =D3DBLEND_SRCALPHASAT     ,
    BL_BOTHSRCALPHA       =D3DBLEND_BOTHSRCALPHA    ,
    BL_BOTHINVSRCALPHA    =D3DBLEND_BOTHINVSRCALPHA 
  };

  enum eMipFilter
  {
    MIP_NONE  = D3DTEXF_NONE,
    MIP_POINT = D3DTEXF_POINT,
    MIP_LINEAR= D3DTEXF_LINEAR
  };

  enum eCull
  {
    CULL_NONE             =D3DCULL_NONE,
    CULL_CW               =D3DCULL_CW,
    CULL_CCW              =D3DCULL_CCW
  };

  enum ePipeArgument
  {
    PA_CURRENT   = D3DTA_CURRENT,
    PA_DIFFUSE   = D3DTA_DIFFUSE, 
    PA_SELECTMASK= D3DTA_SELECTMASK,
    PA_SPECULAR  = D3DTA_SPECULAR, 
    PA_TEMP      = D3DTA_TEMP, 
    PA_TEXTURE   = D3DTA_TEXTURE,
    PA_TFACTOR   = D3DTA_TFACTOR
  };

  enum ePipeOp
  {
    PO_SELECTARG1                =D3DTOP_SELECTARG1                ,
    PO_SELECTARG2                =D3DTOP_SELECTARG2                ,
    PO_MODULATE                  =D3DTOP_MODULATE                  ,
    PO_ADD                       =D3DTOP_ADD                       ,  
    PO_DOT3                      =D3DTOP_DOTPRODUCT3               
  };


  enum eCommonShaderType
  {
    ST_NULL,
    ST_STANDARDTEXTURE,
    ST_STANDARDREFLECTION,
    ST_STANDARDTEXTUREWITHREFLECTION,
    ST_PLAINCOLOR
  };  

  enum eShaderFlags
  {
    SH_DYNAMIC_GEOMETRY = 1,
    SH_VOXELIZE         = 2,
    SH_VOXELIZE_NOCULL  = 4,
    SH_VOXELIZE_STATIC  = 8
  };

  enum eShaderPassFlags
  {
    E_COLORWAVE       =1,
    E_ALPHAWAVE       =2,
    E_DYNAMICGEOMETRY =4,
    E_SCALE           =8,
    E_NOZWRITE        =16,
    E_MATRIXSCALE     =32,
    E_SPECULARADD     =64,
  };

  struct WaveForm
  {
    eWaveType m_Type;
    float     m_fBase;
    float     m_fAmplitude;
    float     m_fFrequency;
    float     m_fOffset;

    float     Evaluate(float fTime);
  };

  // @CONSIDER Encapsulate all this?
  struct ShaderPass
  {
    ShaderPass();

    // Helpers
    bool              UsesTextures();
    bool              UsesTexGen();



    // Data
    
    eShadingMode      m_ShadeMode;
    eFillMode         m_FillMode;
    int               m_Flags;
    eShaderTexGen     m_TexGen;
    eVertexColorGen   m_ColorGen;
    char*             m_pcTextureName;
    int               m_iTextureID;  
    eBlend            m_SrcBlend; 
    eBlend            m_DstBlend;
    ePipeArgument     m_ColorArg1;
    ePipeArgument     m_ColorArg2;
    ePipeOp           m_ColorOp;
    ePipeArgument     m_AlphaArg1;
    ePipeArgument     m_AlphaArg2;
    ePipeOp           m_AlphaOp;  
    eCull             m_CullMode;
    eMipFilter        m_MipFilter;
    eZComp            m_ZComp;
    unsigned          m_uTFactor;
                    
    float             m_fScrollU;
    float             m_fScrollV;
  
    WaveForm          m_ScaleWave;
    WaveForm          m_ColorWave;
    WaveForm          m_AlphaWave;    
    D3DMATERIAL8      m_Material;  
  };


  class Shader
  {
  public:  
                    Shader            ();                  
                    ~Shader           ();
    
    int             Init              ();
    
    int             Shutdown          ();
    
    int             LoadResources     ();
    int             ReleaseResources  ();
    int             ReloadResources();
    int             Release           ();
    int             AddRef            ();
    void            Render            (MeshElement** pMeshElements, unsigned uElements, bool bDoBatch);
    void            PreCache          (MeshElement* pMeshElement);
    int             SetShaderState    (int iPass);


    // Helpers
    bool            UsesTextures      ();
    bool            UsesTexGen        ();


    //
    unsigned        m_uPriority;
    unsigned        m_uPasses;
    ShaderPass*     m_pPasses;
    ePositionGen    m_PosGen;   
    eNormalGen      m_NormalGen;
    char*           m_pcShaderName;
    int             m_iReferences;
    VertexShader*   m_pVertexShader;
    int             m_Flags;
    bool            m_bReceiveShadows;
    bool            m_bProjectShadows;
    bool            m_bProjectBeams;

  };




  int     Init             ();
  int     Shutdown         ();
  int     LoadShader       (const char* pcShaderName);
  int     LoadCommonShader (const char* pcShaderName, eCommonShaderType Type, const char* pcTextureA, const char* pcTextureB, unsigned uColor=0);
  int     ReleaseShader    (int iShader);
  int     ReloadShader         (int iShader);
  int     GetShaderNameByID(int iID, const char*& pcShaderName);
  int     FindShader       (const char* pcShaderName);  

  Shader* GetShader        (const char* pcShaderName);
  Shader* GetShader        (int iID);  

  int     LoadTexture      (const char* pcTexture);
  int     ReleaseTexture   (int iID);

  int     ParseShadersDir  (const char* pcShaderDir);
  int     ParseShadersFile (const char* pcShaderFileName);

  void    Render           (MeshElement** pMeshElements, unsigned uElements);
  void    SetRenderContext (RenderContext* pRC);

  int     GetPolygons      ();
  int     AddPolygons      (int iPolygons);


  void    OverrideBlend    (bool bBlend);
  void    ToggleWireframe  ();

  void    OverrideTFactor  (bool bOverride, unsigned uColor);

  // Hack
  int    SetTFactorForShader(int iShader, unsigned uColor);
  extern bool g_b32BitTextures;

  IDirect3DBaseTexture8* GetD3DTextureFromID(int iID);
}

namespace RenderPipeline
{
  int     Init       ();
  int     Shutdown   (); 

  void    Flush      ();
  void    Render     (MeshElement* pMeshElement);

  int     GetPolygons();
}








#endif
