/*------------------------------------------*/
/*   {<|author : Adler3D(Kashin Ivan) |>   }*/
/*   {<|e-mail : Adler3D@gmail.com    |>   }*/
/*   {<|site   : Adler3d.narod.ru     |>   }*/
/*------------------------------------------*/
#pragma warning(disable:4068)
/*------------------------------------------*/
#include "stdafx.h"
#include "Main.h"
#include "Time.h"
/*------------------------------------------*/
HINSTANCE hInst;
string CmdLine;
int CmdShow;
QapSys Sys("log.txt");
/*------------------------------------------*/
template<class TYPE>
inline void Clean(TYPE&Arr)
{
  int c=Arr.size();
  int j=-1;
  for(int i=0;i<c;i++)
  {
    if(!Arr[i].deaded&&i!=j)Arr[++j]=Arr[i];
  }
  Arr.resize(j+1);
}
/*------------------------------------------*/
#include "TextRender.hpp"
#include "QapAtlas.hpp"
/*------------------------------------------*/
class TGame{
public:
  typedef QapAtlas::TFrame TFrame;
public:
  class ILevel{
  public:
    virtual void Render(QapDev*RD)=0;
    virtual void Update(TGame*Game)=0;
    virtual bool Win()=0;
    virtual bool Fail()=0;
    virtual void AddText(TextRender*TR){}
    virtual void OnPickCell()=0;
    virtual ~ILevel(){}
  };
  class ILevelFactory{
  public:
    virtual ILevel*Build(TGame*Game)=0;
  };
  struct TLevelInfo{
    string Name;
    ILevelFactory&Factory;
    TLevelInfo(const string&Name,ILevelFactory&Factory):Name(Name),Factory(Factory){}
  };
  template<typename TYPE>
  class TLevelFactory:public ILevelFactory{
  public:
    virtual ILevel*Build(TGame*Game){
      auto*Result=new TYPE();
      Result->DoReset();
      Result->Init(Game);
      return Result;
    }
  };/*
  template<typename TYPE>
  class AutoPtr:public std::auto_ptr<TYPE>{
  public:
    TYPE*operator->(){return get();}
    operator bool(){return 
  };*/
public:
  #include "Entity.inl"
  #include "LevelPack.inl"
  #include "GameMenu.inl"
public:
#define FRAMESCOPE(F)\
  F(Head,"xHead",0)\
  F(Body,"xBody",0)\
  F(Point,"Point",0)\
  F(MenuItem,"MenuItem",0)
#define ADDFRAME(NAME,FILE,MODE)TFrame*Frame##NAME;TFrame*Frame##NAME##_s;
  FRAMESCOPE(ADDFRAME)
#undef ADDFRAME
public:
#define PRO_VARIABLE()\
ADDVAR(TCounterIncEx,WaitWin,TCounterIncEx(0,0,Sys.UPS*2))\
ADDVAR(TCounterIncEx,WaitFail,TCounterIncEx(0,0,Sys.UPS*2))\
ADDVAR(TCounterInc,LevelCounter,TCounterInc(0,0,0))
//=====+>>>>>TGame
#include "GenVar.inl"
//<<<<<+=====TGame
public:
  TUserBody User;
  vector<TLevelInfo>LevelsInfo;
  std::auto_ptr<ILevel>Level;
  std::auto_ptr<TMenu>Menu;
public:
  QapAtlas Atlas;
  QapDev RD;
  QapDX::QapFont NormFont;
  QapDX::QapFont BlurFont;
public:
  TGame(){DoReset();}
public:
  typedef QapDX::QapTexMem QapTexMem;
  QapTexMem*AddBorder(QapTexMem*pMem,int dHS=8,const QapColor&Color=0xffffffff)
  {
    int dS=dHS*2;
    QapDX::QapTexMem*sm=new QapDX::QapTexMem("ShadowBot",pMem->W+dS,pMem->H+dS,NULL);
    sm->pBits=new QapColor[sm->W*sm->H];
    sm->Clear(Color);
    sm->FillMem(dHS,dHS,pMem);
    //sm->FillBorder(dHS,dHS,pMem,dHS);
    return sm;
  }
  QapTexMem*GenShadow(QapTexMem*pMem,int dHS=8)
  {
    int dS=dHS*2;
    auto*sm=AddBorder(pMem,dHS,0xffffffff);
    sm->CalcAlpha(0xffffffff);
    sm->FillChannel(0x00ffffff,0x00ffffff);
    QapDX::BlurTexture(sm,dHS);
    return sm;
  }
  TFrame*GenShadowFrame(QapDX::QapTexMem*pMem,int dHS=8)
  {
    QapTexMem*sm=GenShadow(pMem,dHS);
    TFrame*FrameX=Atlas.AddFrame(sm);
    delete sm;
    return FrameX;
  }
  void LoadFrames()
  {
    //auto*ball=QapDX::LoadTexture("GFX\\Ball.png");
    //#define F(NAME)QapDX::LoadTexture("GFX\\"#NAME".png")->CopyAlpha(ball)->SaveToFile("GFX\\"#NAME".png");
    #define F(NAME,FILE,MODE)\
      if(MODE==0){QapDX::LoadTexture("GFX\\"FILE".png")->CalcAlpha(0xffffffff)->SaveToFile("GFX\\"FILE".png");}\
      if(MODE==1){QapDX::LoadTexture("GFX\\"FILE".png")->CopyAlpha(QapDX::LoadTexture("GFX\\"FILE"_a.png")->CalcAlpha())->SaveToFile("GFX\\"FILE".png");}

      FRAMESCOPE(F);
      //QapDX::LoadTexture("GFX\\You.png")->CopyAlpha(QapDX::GenBall(32))->SaveToFile("GFX\\You.png");
    #undef F
    #define F(NAME,FILE,MODE)\
      Frame##NAME=Atlas.AddFrame(tmp=QapDX::LoadTexture("GFX\\"FILE".png"));delete tmp;\
      Frame##NAME##_s=GenShadowFrame(QapDX::LoadTexture("GFX\\"FILE".png"));
      {
        QapDX::QapTexMem*tmp=NULL;
        FRAMESCOPE(F);
      }
    #undef F
    //Atlas.pMem->SaveToFile("Atlas.png");
    Atlas.GenTex();
  }
  void Init()
  {
    DoReset();
    srand(time(NULL));
    {
      QapDX::QapTexMem*pNormMem=NormFont.CreateFontMem("Arial",14,false,512);
      QapDX::QapTexMem*pBlurMem=pNormMem->Clone();
      //pBlurMem->Blur(10);
      QapDX::BlurTexture(pBlurMem,4);
      BlurFont=NormFont;
      BlurFont.Tex=QapDX::GenTextureMipMap(pBlurMem);
      NormFont.Tex=QapDX::GenTextureMipMap(pNormMem);
      //SysFont=QapDX::FontCreate("Arial",16,false,512);
    }
    LoadFrames();
    RD.Init(1024*32,1024*32*2);
    InitLevelsInfo();
    //RestartLevel();
    InitMenuSystem();
    Menu->Up();
  }
  void InitLevelsInfo()
  {
    #define ADDLEVEL(CLASS){static TLevelFactory<CLASS>tmp;LevelsInfo.push_back(TLevelInfo(#CLASS,tmp));}
    LEVEL_LIST(ADDLEVEL);
    #undef ADDLEVEL
    LevelCounter.Maximum=LevelsInfo.size();
  }
  void NewGame(){
    LevelCounter.Value=0;
    RestartLevel();
  }
  void RestartLevel()
  {
    if(!LevelCounter){
      QapAssert(("Поздравляю вы полностью прошли игру!(Это не баг, это фича!).",false));
      return;
    };
    Level.reset(LevelsInfo[LevelCounter.Value].Factory.Build(this));
    WaitWin.Stop();
    WaitFail.Stop();
  }
  void InitMenuSystem(){
    static class TOnResume:public IOnClick{
    public:
      void Call(TMenu*EX){
        EX->Game->Menu->Down();
      }
      virtual bool IsEnabled(TMenu*EX){return EX->Game->Level.get();}
    } OnResume;
    static class TOnRestartLevel:public IOnClick{
    public:
      void Call(TMenu*EX){
        EX->Game->RestartLevel();
        EX->Game->Menu->Down();
      }
      virtual bool IsEnabled(TMenu*EX){return EX->Game->LevelCounter.Value;}
    } OnRestartLevel;
    static class TOnNewGame:public IOnClick{
    public:
      void Call(TMenu*EX){
        EX->Game->NewGame();
        EX->Game->Menu->Down();
      }
    } OnNewGame;
    static class TOnBack:public IOnClick{
    public:
      void Call(TMenu*EX){
        EX->Back();
      }
    } OnBack;
    static class TOnNothing:public IOnClick{
    public:
      void Call(TMenu*EX){}
      virtual bool IsEnabled(TMenu*EX){return false;}
    } OnNothing;
    static class TOnSettings:public IOnClick{
    public:
      void Call(TMenu*EX){
        auto&Menu=EX->Game->Menu;
        auto OldMenu=std::auto_ptr<TMenu>(Menu.release());
        Menu.reset(new TMenu(EX->Game,"Settings"));
        Menu->Add("Under construction",&OnNothing);
        Menu->Add("Back",&OnBack);
        Menu->OldMenu=std::auto_ptr<TMenu>(OldMenu.release());
        Menu->Up();
      }
      //virtual bool IsEnabled(TMenu*EX){return false;}
    } OnSettings;
    static class TOnAbout:public IOnClick{
    public:
      void Call(TMenu*EX){
        auto&Menu=EX->Game->Menu;
        auto OldMenu=std::auto_ptr<TMenu>(Menu.release());
        Menu.reset(new TMenu(EX->Game,"About"));
        Menu->Add("^2Author ^2: ^8Ad^8ler^33D",&OnNothing);
        Menu->Add("^2Code ^2: ^8Ad^8ler^33D",&OnNothing);
        Menu->Add("^2Art ^2: ^8Ad^8ler^33D",&OnNothing);
        Menu->Add("^9Special for ^3IGDC.ru ^2#71",&OnNothing);
        Menu->Add("^B 27.07.2011 - 28.07.2011",&OnNothing);
        Menu->Add("Back",&OnBack);
        Menu->OldMenu=std::auto_ptr<TMenu>(OldMenu.release());
        Menu->Up();
      }
      //virtual bool IsEnabled(TMenu*EX){return false;}
    } OnAbout;
    static class TOnExit:public IOnClick{
    public:
      void Call(TMenu*EX){
        Sys.Quit();
      }
    } OnExit;
    Menu.reset(new TMenu(this,"Main menu"));
    Menu->Add("Resume",&OnResume);
    Menu->Add("Restart level",&OnRestartLevel);
    Menu->Add("New game",&OnNewGame);
    Menu->Add("Settings",&OnSettings);
    Menu->Add("About",&OnAbout);
    Menu->Add("Exit",&OnExit);
  }
  void Resume()
  {

  }
  void Render()
  {
    if(!QapDX::BeginScene())return;
    QapDX::Set2D();
    QapDX::Clear2d(0xffc8c8c8);
    if(0){
      QapDX::SetColor(0xff000000);
      QapDX::DrawCircle(vec2d_zero,32,0,1,8);
    }
    RD.NextFrame();
    if(0){
      RD.BindTex(0,Atlas.pTex);
      RD.SetBlendMode(QapDX::BT_SUB);
      RD.SetColor(0xffffffff);
      RD.DrawQuad(0.5,0.5,Atlas.W,Atlas.H,0);
    }
    if(0){
      RD.BindTex(0,Atlas.pTex);
      RD.SetColor(0xff000000);
      FrameHead_s->Bind(&RD);
      RD.DrawQuad(0.5+1,0.5-1,FrameHead_s->w,FrameHead_s->h,0);
      RD.SetColor(0xffffffff);
      FrameHead->Bind(&RD);
      RD.DrawQuad(0.5,0.5,FrameHead->w,FrameHead->h,0);
    }
    QapAssert(Menu.get());
    if(Menu->InGame())
    {
      if(Level.get()){
        Level->Render(&RD);
      }
    }else{
      TextRender TE(&RD);
      RD.SetColor(0xff000000);
      TE.BeginScope(0,0,&NormFont,&BlurFont);
      Menu->Render(&RD,&TE);
      TE.EndScope();
    };
    {
      RenderText(RD);
    }
    if(!QapDX::EndScene())return;
    QapDX::Present();
  }
  void RenderText(QapDev&RD)
  {
    TextRender TE(&RD);
    vec2d hs=vec2d((Sys.SM.W,1024,Sys.SM.W),Sys.SM.H)*0.5;
    real ident=24.0;
    real Y=0;
    RD.SetColor(0xff000000);
    TE.BeginScope(-hs.x+ident,+hs.y-ident,&NormFont,&BlurFont);
    {
      const string PreesR=" ^7(^3press ^2R^7)";
      const string PreesSpace=" ^7(^3press ^2Space^7)";
      const string PreesEnter=" ^7(^3press ^2Return^7)";
      string BEG="^7";
      string SEP=" ^2: ^8";
      TE.AddText("^7The ^2Game");
      TE.AddText("");
      #define GOO(TEXT,VALUE)TE.AddText(string(BEG)+string(TEXT)+string(SEP)+string(VALUE));
      GOO("Level",string(LevelCounter)+" ["+string(LevelCounter?LevelsInfo[LevelCounter.Value].Name:"noname")+"]");
      #undef GOO
      TE.AddText("");
      if(Level.get())Level->AddText(&TE);
      TE.AddText("");
      if(WaitWin.Runned)TE.AddText("^2You win!"+PreesEnter);
      if(WaitFail.Runned)TE.AddText("^1You loser!"+PreesR);
      if(!WaitFail||!WaitWin){TE.AddText("^7game over!");}
    }
    TE.EndScope();
  }
  void Collide()
  {    
  }
  void NextLevel()
  {
    LevelCounter++;
    RestartLevel();
  }
  void OnWin(){
    //Level.reset();
  }
  void OnFail(){
    //Level.reset();
  }
  void Win(){WaitWin.Start();User.deaded=true;}
  void Fail(){WaitFail.Start();}
  void Update()
  {
    QapAssert(Menu.get());
    if(QapInput::Down[VK_ESCAPE]){if(Menu->InGame()){Menu->Up();}else{Menu->Down();}QapInput::Down[VK_ESCAPE]=false;}
    QapInput::UpdateMouse();
    if(Menu->InGame())
    {
      if(Level.get())
      {
        Level->Update(this);
        {WaitFail++;WaitWin++;}
        if(!WaitWin.Runned&&!WaitFail.Runned)
        {
          bool w=Level->Win();bool f=Level->Fail();
          if(f)Fail();
          if(w&&!f)Win();
        }else{
          if(!WaitFail)OnFail();
          if(!WaitWin)OnWin();
        }
      };
      if(WaitFail.Runned||WaitWin.Runned)
      {
        if(WaitFail.Runned&&QapInput::Down['R']){RestartLevel();}
        if(WaitWin.Runned&&QapInput::Down[VK_RETURN]){NextLevel();}
      }
    }else{
      Menu->Update(this);
    }
  }
  void Free()
  {
    QapDX::FreeFont(NormFont);QapDX::FreeFont(BlurFont);
    QapDX::UnloadTextures();
  }
};
/*------------------------------------------*/
TGame*GetGame(TGame*pValue=NULL)
{
  static TGame*pGame=NULL;
  if(pValue)pGame=pValue;
  return pGame;
}
/*------------------------------------------*/
void Init(){GetGame()->Init();}
void Render(){GetGame()->Render();}
void Update(){GetGame()->Update();}
void Free(){GetGame()->Free();}
/*------------------------------------------*/
#include "../GenInfo/BuildInfo.h"
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
  MACRO_ADD_LOG("App.version : "+IToS(AfterBuildCount)+"/"+IToS(BeforeBuildCount+AfterBuildCount),lml_EVENT);
  TScreenMode SM=GetScreenMode();
  Sys.SetProc(PROC_RENDER,&Render);
  Sys.SetProc(PROC_UPDATE,&Update);
  Sys.SetProc(PROC_INIT,&Init);
  Sys.SetProc(PROC_FREE,&Free);
  SET_DEBUG_VAR(Sys.Debug);
  Sys.WindowCreate("QapEngine(by Adler3D)",false/*!Sys.Debug*/);
  Sys.WindowMode(true,SM.W,SM.H,SM.BPP,SM.Freq);
  Sys.InitD3D();
  {
    TGame Game;
    GetGame(&Game);
    Sys.Run(64);
  }
	return 0;
}
//-------------------------------------------//
//   {<|          27.07.2011           |>}   //
//-------------------------------------------//