#ifndef _HPP_KUNGFLOW_STATE_PLAY
#define _HPP_KUNGFLOW_STATE_PLAY

#include "gaia/gaia.hpp"
using namespace gaia;

#include "main.hpp"
#include "defines.hpp"

#include <vector>

#include <boost/circular_buffer.hpp>

#include <fmod.h>

class KungFlowGame;
class Stage;
//class CharacterModelView;
class HumanGamer;
class Music;

enum e_RunState {
  e_RunState_Running,
  e_RunState_Paused
};

enum e_GlobalSound {
  e_GlobalSound_Hit01,
  e_GlobalSound_SoundHit,
  e_GlobalSound_SoundBlast,
  e_GlobalSound_SIZE
};

struct LightQueueEntry {
  Vector2 screenPosition;
  Vector2 radius;
  float r, g, b;
};

enum e_CoordinateSystem {
  e_CoordinateSystem_World,
  e_CoordinateSystem_Screen
};

struct DebugLine {
  Vector2 position1;
  Vector2 position2;
  float r, g, b, a;
  e_CoordinateSystem coordinateSystem;
};

struct DebugText {
  Vector2 position;
  std::string text;
  float r, g, b, a;
  e_CoordinateSystem coordinateSystem;
};

typedef std::vector<LightQueueEntry> LightQueue;

class PlayState : public State {

  public:

    PlayState(KungFlowGame *game);
    virtual ~PlayState();

    virtual void OnActivate();
    virtual void OnDeactivate();

    virtual void OnStepPhysicsTime();
    virtual void OnStepGraphicsTime();

    void DrawLight(const Vector2 &screenPosition, const Vector2 &radius, float r, float g, float b) const;

    // maybe these could move to gaia's State object (or a seperate debug gfx class); seems pretty useful for anyone!
    void FlushDebugGraphics() const;
    void DrawDebugLine(const Vector2 &position1, const Vector2 &position2, e_CoordinateSystem coordinateSystem, float r = 1.0f, float g = 1.0f, float b = 1.0f, float a = 0.5f) const;
    void DrawDebugCoordCross(const Vector2 &center, e_CoordinateSystem coordinateSystem, float intensity = 0.5f) const;
    void DrawDebugText(const std::string &text, const Vector2 &position, e_CoordinateSystem coordinateSystem, float r = 1.0f, float g = 1.0f, float b = 1.0f, float a = 0.5f) const;

    void DrawLine(const Vector2 &position1, const Vector2 &position2, e_CoordinateSystem coordinateSystem, float r, float g, float b, float a) const;

    const SpatialNode &GetWorldNode() const { return worldNode; }
    const Camera &GetCamera() const { return camera; }

    unsigned long GetStateRunTime_ms() const { return stateRunTime_ms; }

    void SetTimeStepMultiplier(float mup = 1.0) const { timeStepMultiplier = mup; }
    unsigned int GetPhysicsTimeStep_ms() const { return int(ceil(game->GetPhysicsTimeStep_ms() * timeStepMultiplier)); }

    //SequenceLibrary *GetSequenceLibrary() const;

    // 'globals'
    float GetFullVelocity() const { return 3.4f * _VELOCITYFACTOR * _PHYSICSSCALE; }
    float GetMediumVelocity() const { return 2.8f * _VELOCITYFACTOR * _PHYSICSSCALE; }
    float GetSlowVelocity() const { return 2.0f * _VELOCITYFACTOR * _PHYSICSSCALE; }
    float GetQuantizationFactor() const { return _DIRECTIONQUANTIZATIONFACTOR; }//0.7f; }

    radian GetViewOrientation() const { return viewOrientation; }

    FMOD_SYSTEM *GetAudioSystem() const { return static_cast<KungFlowGame*>(this->game)->GetAudioSystem(); } // so we don't have to static cast manually every time
    void SoundPlay(e_GlobalSound sound, float volume = 0.5f) const;
    const Music *GetCurrentMusic() const { return testMusic; }

    //float GetPlayerBodyRadius() const { return 0.5f; }
    //const Stage* GetStage() const { return stage; }
    Stage* GetStage() const { return stage; }

  private:

    Music *GetCurrentMusic_rw() const { return testMusic; }

    Camera camera;
    SpatialNode worldNode;

    // full screen texture, to blit over rendered scene with modulated blend mode
    int lightingLayerResolutionDivider; // multiples of 2, please
    SDL_Surface *lightingLayer;
    SDL_Texture *lightingLayerTexture;
    SDL_Surface *lightSurface; // simple white circle, to be blended on the lightingLayer (SDL_Surface version, to blit on lightingLayer)
    Sprite *lightSprite; // simple white circle, to be blended on the lightingLayer (sprite version, to render to screen for highlights)
    mutable LightQueue lightQueue; // save every drawn light for this frame, so we can reuse them to draw highlights as post processing effect
    bool lighting;

    radian viewOrientation;
    float defaultCameraDistance;

    Object *hudObject;
    Sprite *hudSprite;

    Object *hudDarkenObject;
    Sprite *hudDarkenSprite;

    HumanGamer *bazkie;
    Stage *stage;

    unsigned long stateRunTime_ms;
    mutable float timeStepMultiplier;
    e_RunState runState;

    boost::circular_buffer<Spatial> camSpatialHistory;

    FMOD_SOUND *sounds[e_GlobalSound_SIZE];
    Music *testMusic;

    mutable std::vector<DebugLine> debugLines;
    mutable std::vector<DebugText> debugTexts;

};

class Music {

  public:

    Music(const KungFlowGame *game, const PlayState *playState, const std::string &filename, float bpm, signed int syncOffset_ms = 0);
    virtual ~Music();

    void OnStepPhysicsTime();

    void Play(bool well = true);
    bool IsPlaying() const { return isPlaying; }

    const FMOD_SOUND *GetFMODSound() const { return theSoundOfMusic; }

    unsigned long GetCurrentTime_ms() const { return currentTime_ms; }
    unsigned long GetSyncOffset_ms() const { return syncOffset_ms; }
    float GetBPM() const;
    int GetBeatDuration_ms() const { return floor(1000 / (GetBPM() / 60.0f)); }
    int GetBeat() const;
    float GetBeatOffset() const;

  private:

    const KungFlowGame *game;
    const PlayState *playState;

    FMOD_SOUND *theSoundOfMusic; // this is too beautiful an opportunity to let just pass by
    FMOD_CHANNEL *channel;

    float bpm;
    bool isPlaying;
    unsigned long currentTime_ms;
    signed int syncOffset_ms;

};

class HumanGamer {

  public:

    HumanGamer(const KungFlowGame *game);
    virtual ~HumanGamer();

    const KeyInput &GetKeyInput() const;
    const JoyInput &GetJoyInput() const;

  private:

    const KungFlowGame *game;

};

#endif
