#pragma once

#include <Spiel/2dLayer.h>

#include <Interface/IIOStream.h>
#include <Interface/IAnimationManager.h>

#include <Lang/FastDelegate.h>

#include <list>
#include <set>
#include <vector>

#include "PacTypes.h"



class Unit;
class XRenderer;
class PlayState;
class Player;

class Level
{

  public:

    typedef std::list<Unit*>         tListUnits;

    PlayState*                        m_pState;


    GR::Gamebase::TileLayer2D<Pac::eFeldTypes>      m_Field;
    GR::Gamebase::TileLayer2D<int>                  m_ExtraTimer;

    tListUnits                        m_Units;

    typedef fastdelegate::FastDelegate1<const Pac::tGameEvent&> tEventHandlerFunction;

    tEventHandlerFunction             m_GameEventHandler;

    std::vector<GR::tPoint>           m_ExtraStartPositions;
    std::vector<GR::tPoint>           m_FlashStartPositions;

    GR::f32                           m_StartPause;
    GR::f32                           m_DonePause;
    GR::f32                           m_DeathPause;

    GR::u32                           m_NastiesAlive;

    GR::tFPoint                       m_Offset;

    GR::f32                           m_FixedTime;

    int                               m_ExtraStartDelay;
    int                               m_FlashStartDelay;

    GR::tPoint                        m_BubbleTargetPos;

    int                               m_EnemySpawnDelay;

    int                               m_NumDots;
    int                               m_TotalStartDots;

    tAnimationPos                     m_TileAnimPos;



    Level();
    ~Level();


    void                              Display( XRenderer& Renderer, int XOffset, int YOffset );
    void                              Update( const GR::f32 ElapsedTime );
    void                              UpdateFixed();

    void                              UpdateUnits( GR::f32 ElapsedTime );
    void                              UpdateUnitsFixed();

    void                              Clear();

    bool                              IsFeldBlocking( Unit* pUnit, const Pac::eFeldTypes Feld, Pac::eDir Dir );
    bool                              CanMove( Unit* pUnit, const GR::tPoint& ptCurPos, Pac::eDir Dir );

    bool                              Load( IIOStream& StreamIn );
    bool                              Save( IIOStream& StreamIn );

    GR::u32                           OnCollision( Unit* pUnit1, Unit* pUnit2 );

    bool                              PrepareForStart();
    bool                              IsCompleted();

    Unit*                             SpawnUnit( int X, int Y, Pac::eUnitTypes UnitType, int PlayerIndex = -1 );
    Unit*                             SpawnUnitAtTile( int X, int Y, Pac::eUnitTypes UnitType, int PlayerIndex = -1 );
    Unit*                             LateSpawnUnitAtTile( int X, int Y, Pac::eUnitTypes UnitType, int PlayerIndex = -1 );

    void                              CreateBorders();

    void                              DetectExits( Unit& Unit, std::set<Pac::eDir>& Directions );

    Pac::eDir                         OppositeDirection( Pac::eDir Dir );

    Player*                           FindPlayer( int PlayerIndex );

    std::list<Unit*>                  EnumerateUnits( Pac::eUnitTypes Type );
    int                               NumEnemiesAlive();

    void                              Generate();
    GR::tPoint                        FindPotentialPos();
    GR::tPoint                        FindPotentialEmptyPos();

    bool                              IsWall( Pac::eFeldTypes Feld );
    void                              AdjustBorders( int X1, int Y1, int X2, int Y2 );

};


