#pragma once
//-------------------------------------------//
//   {<| author : Adler3D(Kashin Ivan) |>}   //
//   {<| e-mail : Adler3D@gmail.com    |>}   //
//   {<| site   : Adler3d.narod.ru     |>}   //
//-------------------------------------------//
#include <Box2D/box2d.h>
#include "QapSys.h"
#include "QapVec.h"
#include "QapMatrix.h"
#include "QapDX.h"
//-------------------------------------------//
class QapDev
{
public:
  class TDynamicResource:public QapDX::IResource
  {
  public:
    TDynamicResource():Owner(NULL),flag(false){QapDX::IResource::Reg(this);};
    ~TDynamicResource(){QapDX::IResource::UnReg(this);};
  public:
    QapDev *Owner;
    bool flag;
    void OnLost(){flag=Owner->VB;if(Owner)Owner->Free();}
    void OnReset(){if(Owner&&flag)Owner->ReInit();}
  };
  TDynamicResource DynRes;
private:
  #define ADDVAR(TYPE,NAME,VALUE)TYPE NAME;
    ADDVAR(QapColor,color,0);
  #undef ADDVAR
public:
  union Ver{
    struct{
      float x,y,z;
      QapColor color;
      float tu,tv;
    };
    struct{
      b2Vec2 b2pos;
      float b2z;
      QapColor color;
      b2Vec2 b2tpos;
    };
    struct{
      vec3f pos;
      QapColor color;
      float tu,tv;
    };
    Ver(){}
    Ver(const float&x,const float&y,const QapColor&color,const float&u=0.0,const float&v=0.0):x(x),y(y),b2z(0),color(color),tu(u),tv(v){}
    Ver(const b2Vec2&pos,const QapColor&color,const float&u=0.0,const float&v=0.0):b2pos(pos),b2z(0),color(color),tu(u),tv(v){}
    Ver(const b2Vec2&pos,const QapColor&color,const b2Vec2&texcoord):b2pos(pos),b2z(0),color(color),b2tpos(texcoord){}
  };
  struct BatchScope{
    QapDev&RD;
    bool flag;
    BatchScope(QapDev&RD):RD(RD){flag=!RD.Batching;if(flag)RD.BeginBatch();}
    ~BatchScope(){if(flag)RD.EndBatch();}
  };
protected:
  IDirect3DVertexBuffer9 *VB;
  IDirect3DIndexBuffer9 *IB;
  Ver *VBA;
  int *IBA;
  int VPos;
  int IPos;
  int MaxVPos;
  int MaxIPos;
  int DIPs;
  int Verts;
  int Tris;
  QapDX::BlendType BlendMode;
  QapDX::AlphaMode AlphaMode;
  bool Batching;
  bool Textured;
  b2Transform xf,txf;
  static const DWORD FVF=D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1;
public:
  QapDev():color(0xFFFFFFFF),VB(NULL),IB(NULL),VBA(NULL),IBA(NULL),VPos(0),IPos(0),MaxVPos(0),MaxIPos(0),Batching(false),BlendMode(QapDX::BT_SUB),AlphaMode(QapDX::AM_NONE){DynRes.Owner=this;}
  ~QapDev(){Free();}
public:
  void ReInit(){Init(MaxVPos,MaxIPos);}
  void Init(int VCount,int ICount)
  {
    if(!VB)
    {
      MaxVPos=VCount; MaxIPos=ICount;
      Sys.pDev->CreateVertexBuffer(VCount*sizeof(Ver),D3DUSAGE_DYNAMIC,FVF,D3DPOOL_DEFAULT,&VB,NULL);
      Sys.pDev->CreateIndexBuffer(ICount*sizeof(int),D3DUSAGE_DYNAMIC,D3DFMT_INDEX32,D3DPOOL_DEFAULT,&IB,NULL);
      VBA=0; IBA=0; VPos=0; IPos=0; DIPs=0; Verts=0; Tris=0;
      xf.SetIdentity();
      txf.SetIdentity();
    };
  }
  void Free(){
    if(VB)VB->Release();VB=NULL;if(IB)IB->Release();IB=NULL;VPos=0;IPos=0;Batching=false;BlendMode=QapDX::BT_SUB;AlphaMode=QapDX::AM_NONE;
  };
public:
  void QapDev::BeginBatch()
  {
    Batching=true;Textured=true;
    VBA=0;IBA=0;VPos=0;IPos=0;//Test it
    if(!IB||!VB)return;
    IB->Lock(0,sizeof(int)*MaxIPos,(void **)&IBA,0);
    VB->Lock(0,sizeof(Ver)*MaxVPos,(void **)&VBA,0);
  };
  void EndBatch()
  {
    Batching=false;
    if(!IB||!VB)return;
    IB->Unlock(); VB->Unlock();  
    DIP();
    VBA=0;IBA=0;
  }
  void DIP()
  {
    Sys.pDev->SetFVF(FVF);
    Sys.pDev->SetStreamSource(0,VB,0,sizeof(Ver));
    Sys.pDev->SetIndices(IB);
    Sys.pDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,0,VPos,0,IPos/3);
    DIPs++;Verts+=VPos;Tris+=IPos/3;
  }
  bool IsBatching(){return Batching;}
  int GetIPos(){return IPos;}
  int GetVPos(){return VPos;}
  int GetDIPs(){return DIPs;}
  int GetVerts(){return Verts;}
  int GetTris(){return Tris;}
  const QapColor&GetColor(){return color;}
  void NextFrame(){DIPs=0;Verts=0;Tris=0;SetBlendMode(BlendMode);SetAlphaMode(AlphaMode);}
public:
  void HackMode(bool Textured){this->Textured=Textured;}
  virtual void BindTex(int Stage,QapDX::QapTex*Tex){Sys.pDev->SetTexture(Stage,Tex?Tex->Tex:NULL);txf.SetIdentity();}
public:
  inline Ver&AddVertexRaw(){return VBA[VPos++];}
  inline int AddVertex(const Ver&Source)
  {
    Ver&Dest=VBA[VPos];
    Dest=Source;
    {b2Vec2&v=Dest.b2pos;v=b2Mul(xf,v);}
    if(Textured){b2Vec2&v=Dest.b2tpos;v=b2Mul(txf,v);}
    return VPos++;
  }
  inline void AddTris(int A,int B,int C)
  {
    IBA[IPos++]=A;
    IBA[IPos++]=B;
    IBA[IPos++]=C;
  };
public:
  inline void SetColor(const QapColor&C){color=C;QapDX::SetColor(C);}
  inline void SetTransform(b2Transform const&val){xf=val;}
  inline b2Transform GetTransform(){return xf;}
  inline void SetTextureTransform(b2Transform const&val){txf=val;}
  inline b2Transform GetTextureTransform(){return txf;}
  inline real GetZoom(){return vec2d(xf.R.col1).Mag();}
public:
  QapDX::BlendType GetBlendMode(){return BlendMode;}
  QapDX::AlphaMode GetAlphaMode(){return AlphaMode;}
  void SetBlendMode(QapDX::BlendType Mode){QapDX::Blend(BlendMode=Mode);}
  void SetAlphaMode(QapDX::AlphaMode Mode){QapDX::Alpha(AlphaMode=Mode);}
public:
  void DrawQuad(float x,float y,float w, float h)
  {  
    BatchScope Scope(*this);
    {
      #define F(X,Y,U,V){AddVertex(Ver(x+(X)*w,y+(Y)*h,color,U,V));}
      vec2d O(x,y);
      int n=VPos;
      F(-0.5f,-0.5f,0.0f,1.0f);
      F(+0.5f,-0.5f,1.0f,1.0f);
      F(+0.5f,+0.5f,1.0f,0.0f);
      F(-0.5f,+0.5f,0.0f,0.0f); 
      AddTris(n+1,n+0,n+3);
      AddTris(n+3,n+2,n+1);
      #undef F
    }
  }
  void DrawQuad(float x,float y,float w, float h, float a)
  {
    BatchScope Scope(*this);
    {
      #define F(X,Y,U,V){AddVertex(Ver(O+vec2d(X*w,Y*h).UnRot(OZ),color,U,V));}
      vec2d OZ=Vec2dEx(a,1.0);
      vec2d O(x,y);
      int n=VPos;
      F(-0.5f,-0.5f,0.0f,1.0f);
      F(+0.5f,-0.5f,1.0f,1.0f);
      F(+0.5f,+0.5f,1.0f,0.0f);
      F(-0.5f,+0.5f,0.0f,0.0f); 
      AddTris(n+1,n+0,n+3);
      AddTris(n+3,n+2,n+1);
      #undef F
    }
  }
  void DrawTrigon(const vec2d&A,const vec2d&B,const vec2d&C)
  {
    BatchScope Scope(*this);
    {
      AddTris(
        AddVertex(Ver(A,color)),
        AddVertex(Ver(B,color)),
        AddVertex(Ver(C,color))
      );
    }
  }
  void DrawConvex(const vector<vec2d>&Points)
  {
    BatchScope Scope(*this);
    {
      if(Points.empty())return;
      int c=Points.size();
      int n=VPos;
      for(int i=0;i<c;i++)AddVertex(Ver(Points[i],color,0.5,0.5));
      for(int i=2;i<c;i++)AddTris(n,n+i-1,n+i-0);
    }
  }
public:
  template<typename TYPE>
  void DrawPolyLine(const vector<TYPE>&PA,const real&LineSize,const bool&Loop)
  {
    if(PA.empty())return;
    BatchScope Scope(*this);
    {
      int Count=PA.size();
      int c=Loop?Count:Count-1;
      for(int i=0;i<c;i++)
      {
        TYPE const&a=PA[(i+0)%Count];
        TYPE const&b=PA[(i+1)%Count];
        TYPE n=vec2d(b-a).Ort().SetMag(LineSize);
        int A[4]={
        #define F(pos)AddVertex(Ver(pos,color,0.5f,0.5f))
          F(a+n),
          F(b-n),
          F(a-n),
          F(b+n),
        #undef F
        };
        AddTris(A[0],A[1],A[2]);
        AddTris(A[0],A[1],A[3]);
      }
    }
  }
  template<typename TYPE>
  void DrawLineList(const vector<TYPE>&PA,const real&LineSize)
  {
    if(PA.empty())return;
    BatchScope Scope(*this);
    {
      int Count=PA.size();
      for(int i=0;i<Count;i+=2)
      {
        TYPE const&a=PA[i+0];
        TYPE const&b=PA[i+1];
        TYPE n=vec2d(b-a).Ort().SetMag(LineSize);
        int A[4]={
        #define F(A)AddVertex(MakeVer(A,color,0.5,0.5))
          F(a+n),
          F(b-n),
          F(a-n),
          F(b+n),
        #undef F
        };
        AddTris(A[0],A[1],A[2]);
        AddTris(A[0],A[1],A[3]);
      }
    }
  }
  template<typename TYPE>
  void DrawMesh(const vector<TYPE>&VA,const vector<int>&IA)
  {
    if(VA.empty())return;
    BatchScope Scope(*this);
    {
      int base=GetVPos();
      static IntArray VID;VID.resize(VA.size());
      for(int i=0;i<VA.size();i++)VID[i]=AddVertex(MakeVer(VA[i],color,p.x,p.y));
      for(int i=0;i<IA.size();i+=3)AddTris(VID[IA[i+0]],VID[IA[i+1]],VID[IA[i+2]]);
    }
  }
};
//-------------------------------------------//
//   {<|          01.07.2011           |>}   //
//-------------------------------------------//