#include "pch.h"
//#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS
//#define BOOST_MPL_LIMIT_VECTOR_SIZE 30 //or whatever you need                       
//#define BOOST_MPL_LIMIT_MAP_SIZE 30 //or whatever you need   

//#include "MainPage.xaml.h"
#include "GameMain.h"
#include "SoundDriver.h"
#include "MenuPage.xaml.h"

#pragma hdrstop

#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>


using namespace Microsoft::WRL;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI::Core;
using namespace Windows::UI::ViewManagement;

using namespace Windows::UI::ApplicationSettings;
using namespace Windows::UI::Popups;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Controls::Primitives;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Interop;
using namespace Windows::UI::Xaml::Navigation;

using namespace Windows::Graphics::Display;
using namespace DirectX;
using namespace D2D1;
using namespace concurrency;


using namespace BasicSprites;

namespace msmf = boost::msm::front;
namespace msmb = boost::msm::back;

#include "SoundEditor.h"

namespace ShootingGame 
{

  namespace State 
  {
    struct Init : msmf::state<>
    {
      template <class Event, class Fsm> 
      void on_entry(Event const& e, Fsm& f )
      {
        gameMain_ = f.GameMain();
      };

    private:
      GameMain^ gameMain_;
    };

    struct Title : msmf::state<> 
    {
      template <class Event, class Fsm> 
      void on_entry(Event const&, Fsm& ) {};
      template <class Event, class Fsm> 
      void on_exit(Event const&, Fsm& ) {};
    };

    struct Demo : msmf::state<>
    {
      template <class Event, class Fsm> 
      void on_entry(Event const&, Fsm& ) {};
      template <class Event, class Fsm> 
      void on_exit(Event const&, Fsm& ) {};
    };


    struct Running_ : msmf::state_machine_def<Running_>
    {

      struct Update 
      {
        template <class Fsm,class Evt,class SourceState,class TargetState> 
        void operator()(Evt const& e, Fsm& fsm, SourceState&,TargetState& ) 
        {

        }
      };

      struct Render
      {
        template <class Fsm,class Evt,class SourceState,class TargetState> 
        void operator()(Evt const& e, Fsm& fsm, SourceState&,TargetState& ) 
        {
          ShootingGame::GameMain^ gameMain = fsm.GameMain();
          BasicSprites::SpriteBatch^ spriteBatch = fsm.GameMain()->SpriteBatch();
          ID3D11Texture2D * test_(fsm.texture());          
          static int frame_count = 0;
          frame_count = (frame_count + 1) & (0x7f);

          gameMain->ClearScreen();

          spriteBatch->Begin();

          // Draw the background.

          spriteBatch->Draw(
            test_,
            float4(160.0f, 120.0f,0.7f,1.0f),
            PositionUnits::Pixels,
            float2(64.0f, 64.0f),
            SizeUnits::Pixels,
            float4(1.0f, 1.0f, 1.0f, 1.0f),
            0.0f,
            BlendMode::Additive,
            float2((float)((frame_count >> 3) % 4) * 64.0f,(float)((frame_count >> 3) / 4) * 64.0f)
            );

          spriteBatch->Draw(
            test_,
            float4(128.0f, 100.0f,0.7f,1.0f),
            PositionUnits::Pixels,
            float2(32.0f, 32.0f),
            SizeUnits::Pixels,
            float4(1.0f, 1.0f, 1.0f, 1.0f),
            0.0f,
            BlendMode::Additive,
            float2((float)((frame_count >> 3) % 4) * 64.0f,(float)((frame_count >> 3) / 4) * 64.0f)
            );

          spriteBatch->Draw(
            test_,
            float4(128.0f, 50.0f,0.7f,1.0f),
            PositionUnits::Pixels,
            float2(96.0f, 96.0f),
            SizeUnits::Pixels,
            float4(1.0f, 1.0f, 1.0f, 1.0f),
            0.0f,
            BlendMode::Additive,
            float2((float)((frame_count >> 3) % 4) * 64.0f,(float)((frame_count >> 3) / 4) * 64.0f)
            );

          spriteBatch->End();
          gameMain->RenderScreen();
        }

      };

      struct Action : msmf::state<>{};

      template <class Event, class Fsm> 
      void on_entry(Event const& evt, Fsm&  fsm) 
      {
        gameMain_ = fsm.GameMain();
        gameMain_->IsBackButtonEnabled(true);
        gameMain_->StopSound();
        InitSound();
        gameMain_->StartSound();
        LoadTexture();
      };

      typedef boost::mpl::vector<Action> initial_state;

      struct Exit : msmf::exit_pseudo_state<msmf::none> {};

      template <class Event, class Fsm> 
      void on_exit(Event const& e, Fsm& fsm)
      { 
        gameMain_->StopSound();
        test_.Reset();

        gameMain_->ClearScreen();

        gameMain_->RenderScreen();
        gameMain_->Present();

        gameMain_->RenderScreen();
      	gameMain_->Present();

      };

      struct transition_table 
        : boost::mpl::vector<
        msmf::Row<Action,Event::Update,msmf::none,Update,msmf::none>,
        msmf::Row<Action,Event::Render,msmf::none,Render,msmf::none>,
        msmf::Row<Action,Event::Back,Exit>
        >
      {};

    // No handled event handler
        template <class Fsm,class Event> 
        void no_transition(Event const& e, Fsm& ,int state) {
            std::cout << "No handled event in Sm1 " << typeid(e).name() << " on State " << state << std::endl;
        }

      ShootingGame::GameMain^ Running_::GameMain(){return gameMain_;} 
      ID3D11Texture2D * texture()
      {
        return test_.Get();
      }; 

    private:
      void InitSound()
      {
        sf::SoundManager& sm(gameMain_->SoundManager());
        sm.Synthesizer().isEnable(false);
        sm.Synthesizer().Clear();
        //        sm.Synthesizer().WaveTables().clear();

        // 鋸波テーブルを作る
        {
          sf::Synthesizer::WaveTable sawtbl;
          sawtbl.sampleRate = 440.0f;
          sawtbl.basePitch = 0.0f;
          sawtbl.stereo = false;

          float v = -1.0f,d = 2.0f / 1024.0f;
          for(int  i = 0;i < 1024;++i)
          {
            //        if(i < 15) v = -1.0f; else v = 1.0f; 
            sawtbl.waveData.push_back(v) ;
            v += d;
          }
          sm.Synthesizer().WaveTables().push_back(std::move(sawtbl));
        }

        // 矩形波テーブルを作る
        {
          sf::Synthesizer::WaveTable squaretbl;
          squaretbl.sampleRate = 440.0f;
          squaretbl.basePitch = 0.0f;
          squaretbl.stereo = false;

          float v = 0.0f;
          for(int  i = 0;i < 32;++i)
          {
            if(i < 15) v = -1.0f; else v = 1.0f; 
            squaretbl.waveData.push_back(v) ;
          }
          sm.Synthesizer().WaveTables().push_back(std::move(squaretbl));
        }

        // 三角波テーブル
        {
          sf::Synthesizer::WaveTable tritbl;
          tritbl.sampleRate = 440.0f;
          tritbl.basePitch = 0.0f;
          tritbl.stereo = false;

          float v = -1.0f,d = 2.0f / 16.0f;
          for(int  i = 0;i < 32;++i)
          {
            if(i < 15) d = -d; 
            tritbl.waveData.push_back(v) ;
            v += d;
          }
          sm.Synthesizer().WaveTables().push_back(std::move(tritbl));
        }

        // sinテーブル
        {
          sf::Synthesizer::WaveTable sintbl;
          sintbl.sampleRate = 440.0f;
          sintbl.basePitch = 0.0f;
          sintbl.stereo = false;

          float v = 0.0f,d = 2.0f * M_PI / 128.0f;
          for(int  i = 0;i < 128;++i)
          {
            sintbl.waveData.push_back(sinf(v));
            v += d;
          }
          sm.Synthesizer().WaveTables().push_back(std::move(sintbl));
        }

        // LFO用 Sinテーブル
        {
          sf::Synthesizer::WaveTable sintbl;
          sintbl.sampleRate = 440.0f;
          sintbl.basePitch = 0.0f;
          sintbl.stereo = false;

          float v = 0.0f,d = 2.0f * M_PI / 128.0f;
          for(int  i = 0;i < 128;++i)
          {
            sintbl.waveData.push_back(sinf(v) / 2.0f + 0.5f);
            v += d;
          }
          sm.Synthesizer().WaveTables().push_back(std::move(sintbl));
        }




        //      ::OutputDebugStringW((boost::wformat(L"waveTable size: %d") % osc_[0].WaveData().size()).str().c_str());

        // timberのセットアップ
        for(int i = 0; i < 4;++i){

          sf::Synthesizer::Timber timber;
          timber.oscillator.reset(new sf::Synthesizer::WaveTableOscillator(&sm.Synthesizer().WaveTables().at(0)));
          timber.amplitude.gain = 1.0f;
          timber.amplitude.envelope.releaseNoteOff = true;
          timber.amplitude.envelope.attackTime = 0.01f;
          timber.amplitude.envelope.decayTime = 0.02f;
          timber.amplitude.envelope.sustainLevel = 0.5f;
          timber.amplitude.envelope.releaseTime = 0.2f;
          timber.amplitude.envelope.gain = 1.0f;
          timber.amplitude.lfo.waveForm = &(sm.Synthesizer().WaveTables()[4]);
          timber.amplitude.lfo.freq = 5.0f;
          timber.amplitude.lfo.gain = 0.0f;

          timber.pitch.lfo.freq = 10.0f;
          timber.pitch.lfo.gain = 0.000f;
          timber.pitch.lfo.waveForm = &(sm.Synthesizer().WaveTables()[4]);
          timber.pitch.lfo.startNoteOn = true;
          timber.pitch.envelope.attackTime = 0.0f;
          timber.pitch.envelope.decayTime = 0.02f;
          timber.pitch.envelope.sustainLevel = 0.5f;
          timber.pitch.envelope.gain =0.0f;
          timber.pitch.pitch = 0.0f;

          timber.pan.lfo.freq = 2.0f;
          timber.pan.lfo.gain = 1.0f;
          timber.pan.lfo.waveForm = &(sm.Synthesizer().WaveTables()[3]);
          timber.pan.lfo.startNoteOn = true;
          timber.pan.envelope.enable = false;
          timber.pan.lfo.envelope.enable = false;
          timber.pan.pan = 0.0f;

          timber.filter.lfo.freq = 5.0f;
          timber.filter.lfo.gain = 1.0f;
          timber.filter.lfo.waveForm = &(sm.Synthesizer().WaveTables()[3]);
          timber.filter.lfo.startNoteOn = true;
          timber.filter.cutoff = 10000.0f;
          timber.filter.resonance = 0.0f;
          timber.filter.envelope.attackTime = 0.01f;
          timber.filter.envelope.decayTime = 0.03f;
          timber.filter.envelope.sustainLevel = 0.05f;
          timber.filter.envelope.releaseTime = 0.2f;
          timber.filter.envelope.gain = 10000.0f;

          sm.Synthesizer().AddProgram(sf::Synthesizer::Program(std::move((boost::wformat(L"Program No.%d") % i).str()),std::move(timber)));   
          //        timbers_.push_back(std::move(timber));
        }

        for(int i = 0,end = sm.Synthesizer().Voices();i < end;++i)
        {
          sm.Synthesizer().AssignProgramToVoice(0,i);
        }

        sm.Synthesizer().isEnable(true);

      }

      void LoadTexture()
      {
        gameMain_->LoadTexture(L"explosion.png",&test_);
        gameMain_->SpriteBatch()->AddTexture(test_.Get(),float2(64.0,64.0));
      }

      ShootingGame::GameMain^ gameMain_;
      Microsoft::WRL::ComPtr<ID3D11Texture2D> test_;
    };

    typedef boost::msm::back::state_machine<Running_> Running;

    // メニュー状態
    struct Menu_ : msmf::state_machine_def<Menu_>
    {
      template <class Event, class Fsm> 
      void on_entry(Event const&, Fsm&  fsm) 
      {
        gameMain_ = fsm.GameMain();
        gameMain_->IsBackButtonEnabled(false);
      };

      template <class Event, class Fsm> 
      void on_exit(Event const&, Fsm& )
      {
      };

      // アイドル状態
      struct Idle : msmf::state<> 
      {
        template <class Event, class Fsm> 
        void on_entry(Event const&, Fsm&  fsm) 
        {
          gameMain_ = fsm.GameMain();
          MenuPage^ menuPage_ = ref new MenuPage(gameMain_->GameStateMachine());
          gameMain_->Subframe(menuPage_);
          gameMain_->IsBackButtonEnabled(false);
        }

        template <class Event, class Fsm> 
        void on_exit(Event const&, Fsm& )
        {
          gameMain_->Subframe(nullptr);
          menuPage_ = nullptr;
        };
      private:
        ShootingGame::GameMain^ gameMain_;
        MenuPage^ menuPage_;
      };
      typedef Idle initial_state;


      // 状態遷移テーブル
      struct transition_table 
        : boost::mpl::vector<
        msmf::Row<Idle,Event::PushStart,State::Running>,
        msmf::Row<State::Running::exit_pt<State::Running_::Exit>,msmf::none,Idle>,
        msmf::Row<Idle,Event::PushSoundEdit,State::SoundEdit>,
        msmf::Row<State::SoundEdit::exit_pt<State::SoundEdit_::Exit>,msmf::none,Idle>
        >
      {};
    // No handled event handler
        template <class Fsm,class Event> 
        void no_transition(Event const& e, Fsm& ,int state) {
 //           std::cout << "No handled event in Sm1 " << typeid(e).name() << " on State " << state << std::endl;
        }

      // プロパティ
      ShootingGame::GameMain^ Menu_::GameMain(){return gameMain_;} 
    private:
      ShootingGame::GameMain^ gameMain_;
    };

    typedef msmb::state_machine<Menu_> Menu;

    struct Exit : boost::msm::front::state<>
    {
      template <class Event, class Fsm> 
      void on_entry(Event const&, Fsm& ) {};
      template <class Event, class Fsm> 
      void on_exit(Event const&, Fsm& ) {};
    };
  }

  struct GameStateMachine_ : boost::msm::front::state_machine_def<GameStateMachine_>
  {
    typedef State::Init initial_state;

    template <class Event, class Fsm> 
    void on_entry(Event const&, Fsm& ) {};

    template <class Event, class Fsm> 
    void on_exit(Event const&, Fsm& ) {};

    struct transition_table 
      : boost::mpl::vector<
      msmf::Row<State::Init,Event::Init,State::Menu>
      //     msmf::Row<State::Running,Event::Update,boost::msm::front::none,State::Running::Update>
      /*      _row<State::Menu,Event::PushStart,State::Running>,
      _row<State::Menu,Event::PushSoundEdit,State::SoundEdit>,
      _row<State::Menu,Event::TimeOut,State::Demo>,
      _row<State::Running,Event::Escape,State::Menu>,
      _row<State::SoundEdit,Event::Escape,State::Menu>*/
      >{};

    template <class Event, class Fsm> 
        void no_transition(Event const& e, Fsm& ,int state) {
 //           std::cout << "No handled event in Sm1 " << typeid(e).name() << " on State " << state << std::endl;
        }

    explicit GameStateMachine_(GameMain^ main) :gameMain_(main)
    {

    }

    ~GameStateMachine_()
    {

    }

    ShootingGame::GameMain^ GameStateMachine_::GameMain(){return gameMain_;} 

  private:
    ShootingGame::GameMain ^gameMain_;
  };

  typedef boost::msm::back::state_machine<GameStateMachine_> GameStateMachineImpl;

  struct GameStateMachine::impl 
  {
    impl(GameMain ^ main) : impl_(main) {};
    ~impl(){impl_.stop();}
    template <class Event> void ProcessEvent(Event const & e) {impl_.process_event(e);}
    void Start() {impl_.start();}
    void Stop() {impl_.stop();}
  private:
    GameStateMachineImpl impl_;
  };

  GameStateMachine::GameStateMachine(GameMain^ main) 
    : impl_(new impl(main))
  {

  }

  GameStateMachine::~GameStateMachine()
  {
    impl_.reset();
  }

  template <class Event> void GameStateMachine::ProcessEvent(Event const& e)
  {
    impl_->ProcessEvent(e);
  }

  void GameStateMachine::Start()
  {
    impl_->Start();

  }

  void GameStateMachine::Stop()
  {
    impl_->Stop();
  }

  template void GameStateMachine::ProcessEvent<Event::Init>(Event::Init const &e);
  template void GameStateMachine::ProcessEvent<Event::Update>(Event::Update const &e);
  template void GameStateMachine::ProcessEvent<Event::Render>(Event::Render const &e);
  template void GameStateMachine::ProcessEvent<Event::PushStart>(Event::PushStart const &e);
  template void GameStateMachine::ProcessEvent<Event::PushSoundEdit>(Event::PushSoundEdit const &e);
  template void GameStateMachine::ProcessEvent<Event::Escape>(Event::Escape const &e);
  template void GameStateMachine::ProcessEvent<Event::Back>(Event::Back const &e);

}
