#include <Xtreme/X2dRenderer.h>

#include <MasterFrame/XAct.h>

#include <IO/FileChunk.h>

#include ".\level.h"
#include ".\Unit.h"
#include ".\GetEmGood.h"
#include "Spawn.h"
#include "Player.h"
#include "PlayState.h"
#include "GameLogic.h"



Level::Level() :
  m_GameEventHandler( NULL ),
  m_FixedTime( 0.0f ),
  m_ExtraStartDelay( 0 ),
  m_FlashStartDelay( 0 ),
  m_EnemySpawnDelay( 0 ),
  m_NumDots( 0 ),
  m_TotalStartDots( 0 ),
  m_pState( NULL )
{
  m_Field.InitFeld( 50, 35 );
  m_ExtraTimer.InitFeld( 50, 35 );

  m_TileAnimPos = theApp.AnimationPos( "Tile.1" );
}



Level::~Level()
{
  Clear();
}



void Level::Display( XRenderer& Renderer, int XOffset, int YOffset )
{
  GR::tPoint    ptOffset( (int)m_Offset.x, (int)m_Offset.y );

  int     iX1 = ptOffset.x / 16;
  int     iY1 = ptOffset.y / 16;

  int     iX2 = iX1 + 50;
  int     iY2 = iY1 + 35;

  for ( int i = 0; i < 50; ++i )
  {
    for ( int j = 0; j < 35; ++j )
    {
      Pac::eFeldTypes Feld = m_Field.Field( i, j );
      if ( Feld != Pac::FT_EMPTY )
      {
        if ( Feld == Pac::FT_DOT )
        {
          Renderer.RenderTextureSection2d( ( i - iX1 ) * 16 - ( ptOffset.x % 16 ) + XOffset,
                                           ( j - iY1 ) * 16 - ( ptOffset.y % 16 ) + YOffset,
                                           theApp.AnimationFrame( m_TileAnimPos ) );
        }
        else
        {
          Renderer.RenderTextureSection2d( ( i - iX1 ) * 16 - ( ptOffset.x % 16 ) + XOffset, 
                                           ( j - iY1 ) * 16 - ( ptOffset.y % 16 ) + YOffset, 
                                           theApp.m_Sections[Feld] );
        }
      }
    }
  }

  tListUnits::iterator    it( m_Units.begin() );
  while ( it != m_Units.end() )
  {
    Unit*    pUnit( *it );

    pUnit->Display( ptOffset - GR::tPoint( XOffset, YOffset ), Renderer );

    ++it;
  }
}



void Level::UpdateUnits( GR::f32 ElapsedTime )
{
  tListUnits::iterator    it( m_Units.begin() );
  while ( it != m_Units.end() )
  {
    Unit*    pUnit( *it );

    if ( pUnit->m_RemoveMe )
    {
      pUnit->ProcessEvent( *m_pState, Unit::ET_DIE );
      m_GameEventHandler( Pac::tGameEvent( Pac::tGameEvent::GE_UNIT_DELETED, pUnit ) );

      delete pUnit;
      it = m_Units.erase( it );
    }
    else
    {
      if ( ( m_StartPause > 0.0f )
      &&   ( pUnit->m_IsNasty ) )
      {
        // Monster haben Startpause
      }
      else
      {
        pUnit->Update( *m_pState, ElapsedTime );
      }
      ++it;
    }
  }

  it = m_Units.begin();
  while ( it != m_Units.end() )
  {
    next_object:;

    Unit*    pUnit( *it );

    if ( pUnit->m_RemoveMe )
    {
      pUnit->ProcessEvent( *m_pState, Unit::ET_DIE );
      m_GameEventHandler( Pac::tGameEvent( Pac::tGameEvent::GE_UNIT_DELETED, pUnit ) );

      delete pUnit;
      it = m_Units.erase( it );
      continue;
    }
    if ( pUnit->m_Invincibility == 0 )
    {
      GR::tRect   rcBounds( pUnit->CollisionBounds() );

      tListUnits::iterator    it2( m_Units.begin() );
      while ( it2 != m_Units.end() )
      {
        Unit*    pUnit2( *it2 );

        if ( ( pUnit != pUnit2 )
        &&   ( !pUnit2->m_RemoveMe )
        &&   ( pUnit2->m_Invincibility == 0 )
        &&   ( rcBounds.intersects( pUnit2->CollisionBounds() ) ) )
        {
          GR::u32   dwResult = OnCollision( pUnit, pUnit2 );
          if ( dwResult == 2 )
          {
            it2 = m_Units.erase( it2 );
            continue;
          }
          dwResult = OnCollision( pUnit2, pUnit );
          if ( dwResult == 2 )
          {
            it = m_Units.erase( it );
            goto next_object;
          }
        }

        ++it2;
      }
    }
    ++it;
  }
}



bool Level::IsCompleted()
{
  return m_NumDots == 0;
}



void Level::UpdateUnitsFixed()
{
  tListUnits::iterator    it( m_Units.begin() );
  while ( it != m_Units.end() )
  {
    Unit*    pUnit( *it );

    if ( pUnit->m_RemoveMe )
    {
      pUnit->ProcessEvent( *m_pState, Unit::ET_DIE );
      m_GameEventHandler( Pac::tGameEvent( Pac::tGameEvent::GE_UNIT_DELETED, pUnit ) );

      delete pUnit;
      it = m_Units.erase( it );
    }
    else
    {
      if ( ( m_StartPause > 0.0f )
      &&   ( pUnit->m_IsNasty ) )
      {
        // Monster haben Startpause
      }
      else
      {
        pUnit->UpdateFixed( *m_pState );
      }
      ++it;
    }
  }

  it = m_Units.begin();
  while ( it != m_Units.end() )
  {
    next_object:;

    Unit*    pUnit( *it );

    if ( pUnit->m_RemoveMe )
    {
      pUnit->ProcessEvent( *m_pState, Unit::ET_DIE );
      m_GameEventHandler( Pac::tGameEvent( Pac::tGameEvent::GE_UNIT_DELETED, pUnit ) );

      delete pUnit;
      it = m_Units.erase( it );
      continue;
    }
    if ( pUnit->m_Invincibility == 0 )
    {
      GR::tRect   rcBounds( pUnit->CollisionBounds() );

      tListUnits::iterator    it2( m_Units.begin() );
      while ( it2 != m_Units.end() )
      {
        Unit*    pUnit2( *it2 );

        if ( ( pUnit != pUnit2 )
        &&   ( !pUnit2->m_RemoveMe )
        &&   ( pUnit2->m_Invincibility == 0 )
        &&   ( rcBounds.intersects( pUnit2->CollisionBounds() ) ) )
        {
          GR::u32   dwResult = OnCollision( pUnit, pUnit2 );
          if ( dwResult == 2 )
          {
            it2 = m_Units.erase( it2 );
            continue;
          }
          dwResult = OnCollision( pUnit2, pUnit );
          if ( dwResult == 2 )
          {
            it = m_Units.erase( it );
            goto next_object;
          }
        }

        ++it2;
      }
    }
    ++it;
  }

  if ( m_DonePause == 0.0f )
  {
    if ( IsCompleted() )
    {
      m_NastiesAlive = 0;
      m_DonePause = 5.0f;
    }
  }
}



Unit* Level::LateSpawnUnitAtTile( int X, int Y, Pac::eUnitTypes UnitType, int PlayerIndex )
{
  Spawn*    pSpawn = (Spawn*)SpawnUnitAtTile( X, Y, Pac::UT_SPAWN, PlayerIndex );

  pSpawn->m_SpawnUnit = UnitType;

  return pSpawn;
}



Unit* Level::SpawnUnitAtTile( int X, int Y, Pac::eUnitTypes UnitType, int PlayerIndex )
{
  Unit* pUnit = SpawnUnit( X * 16, Y * 16, UnitType, PlayerIndex );
  pUnit->OnSpawned( *m_pState );

  return pUnit;
}



Unit* Level::SpawnUnit( int X, int Y, Pac::eUnitTypes UnitType, int PlayerIndex )
{
  Unit*   pUnit = Unit::FromType( UnitType );
  if ( pUnit == NULL )
  {
    return false;
  }

  pUnit->m_PlayerIndex = PlayerIndex;
  pUnit->m_TilePos.set( X / 16, Y / 16 );

  pUnit->m_Position.set( X, Y );
  pUnit->m_StartPosition.set( X, Y );


  pUnit->ProcessEvent( *m_pState, Unit::eUnitEvent::ET_INIT );

  m_Units.push_back( pUnit );

  if ( GameLogic::IsEnemy( UnitType ) )
  {
    // +1 HP per level
    pUnit->m_HP += m_pState->LevelNumber - 1;
    // +1 movespeed per every 2 levels
    pUnit->m_MoveSpeed += ( m_pState->LevelNumber - 1 ) / 2;
  }


  theApp.SoundClass()->Play( theApp.Sound( "Spawn" ) );

  return pUnit;
}



void Level::Update( const GR::f32 ElapsedTime )
{
  theApp.m_AnimationManager.AdvanceAnimFrame( m_TileAnimPos, ElapsedTime * 1000.0f );

  if ( m_StartPause > 0.0f )
  {
    m_StartPause -= ElapsedTime;
    if ( m_StartPause < 0.0f )
    {
      m_StartPause = 0.0f;
    }
  }

  if ( m_DeathPause > 0.0f )
  {
    m_DeathPause -= ElapsedTime;
    if ( m_DeathPause <= 0.0f )
    {
      m_GameEventHandler( Pac::tGameEvent( Pac::tGameEvent::GE_PLAYER_DIED ) );
      return;
    }
  }

  if ( m_DonePause > 0.0f )
  {
    m_DonePause -= ElapsedTime;
    if ( m_DonePause <= 0.0f )
    {
      m_GameEventHandler( Pac::tGameEvent( Pac::tGameEvent::GE_LEVEL_COMPLETED ) );
      return;
    }
  }

  GR::f32     timeLeft = ElapsedTime;

  while ( timeLeft > 0.0f )
  {
    if ( m_FixedTime + timeLeft >= 0.02f )
    {
      GR::f32     timeDelta = 0.02f - m_FixedTime;

      timeLeft -= timeDelta;
      m_FixedTime = 0.0f;

      UpdateUnits( timeDelta );
      UpdateFixed();
    }
    else
    {
      m_FixedTime += timeLeft;
      UpdateUnits( timeLeft );
      timeLeft = 0.0f;
    }
  }
}



int Level::NumEnemiesAlive()
{
  int     numEnemies = 0;

  tListUnits::iterator    it( m_Units.begin() );
  while ( it != m_Units.end() )
  {
    Unit*    pUnit( *it );

    if ( GameLogic::IsEnemy( pUnit->m_Type ) )
    {
      ++numEnemies;
    }
    ++it;
  }
  return numEnemies;
}



void Level::UpdateFixed()
{
  if ( m_EnemySpawnDelay > 0 )
  {
    --m_EnemySpawnDelay;
  }
  else
  {
    m_EnemySpawnDelay = 200;

    // only spawn if there are still dots and the level is not "full"
    if ( ( m_NumDots > 0 )
    &&   ( NumEnemiesAlive() < m_TotalStartDots / 16 ) )
    {
      GR::tPoint      enemyPos = FindPotentialPos();

      // determine next enemy
      if ( m_pState->NumEnemiesSpawned % 2 )
      {
        LateSpawnUnitAtTile( enemyPos.x, enemyPos.y, Pac::UT_BOMB );
      }
      else
      {
        if ( !m_pState->SpawnList.empty() )
        {
          LateSpawnUnitAtTile( enemyPos.x, enemyPos.y, m_pState->SpawnList.front() );
          m_pState->SpawnList.pop_front();
        }
      }
      ++m_pState->NumEnemiesSpawned;
    }
  }
  UpdateUnitsFixed();

  for ( int i = 0; i < m_ExtraTimer.Width(); ++i )
  {
    for ( int j = 0; j < m_ExtraTimer.Height(); ++j )
    {
      int     timer = m_ExtraTimer.Field( i, j ); 
      if ( timer > 0 )
      {
        m_ExtraTimer.SetField( i, j, timer - 1 );
        if ( timer == 1 )
        {
          // extra gone
          if ( ( ( m_Field.Field( i, j ) >= Pac::FT_FIRST_EXTRA )
          &&     ( m_Field.Field( i, j ) <= Pac::FT_LAST_EXTRA ) )
          ||   ( m_Field.Field( i, j ) == Pac::FT_CATERPILLAR_BODY ) )
          {
            m_Field.SetField( i, j, Pac::FT_EMPTY );
          }
        }
      }
    }
  }
}



void Level::Clear()
{
  tListUnits::iterator    it( m_Units.begin() );
  while ( it != m_Units.end() )
  {
    Unit*    pUnit( *it );

    delete pUnit;

    ++it;
  }
  m_Units.clear();
  m_ExtraStartPositions.clear();
  m_ExtraStartDelay = 0;
  m_FlashStartPositions.clear();
  m_FlashStartDelay = 0;
  m_BubbleTargetPos.set( 380, 20 );

  m_ExtraTimer.ResetField( 0 );
}



bool Level::IsFeldBlocking( Unit* pUnit, const Pac::eFeldTypes Feld, Pac::eDir Dir )
{
  if ( IsWall( Feld ) )
  {
    return true;
  }
  if ( Feld == Pac::FT_CATERPILLAR_BODY )
  {
    if ( ( pUnit == NULL )
    ||   ( ( pUnit->m_Type != Pac::UT_PLAYER_SHOT )
    &&     ( pUnit->m_Type != Pac::UT_CATERPILLAR ) ) )
    {
      return true;
    }
   }
  return false;
}



bool Level::CanMove( Unit* pUnit, const GR::tPoint& ptCurPos, Pac::eDir Dir )
{
  GR::tPoint       targetPos( pUnit->m_Position );

  switch ( Dir )
  {
    case Pac::DIR_W:
      --targetPos.x;
      break;
    case Pac::DIR_E:
      ++targetPos.x;
      break;
    case Pac::DIR_N:
      --targetPos.y;
      break;
    case Pac::DIR_S:
      ++targetPos.y;
      break;
  }

  int     x1 = targetPos.x / 16;
  int     x2 = ( targetPos.x + 15 ) / 16;
  int     y1 = targetPos.y / 16;
  int     y2 = ( targetPos.y + 15 ) / 16;

  for ( int x = x1; x <= x2; ++x )
  {
    for ( int y = y1; y <= y2; ++y )
    {
      if ( IsFeldBlocking( pUnit, m_Field.Field( x, y ), Dir ) )
      {
        return false;
      }
    }
  }
  return true;
}



bool Level::Load( IIOStream& StreamIn )
{
  Clear();

  m_Field.InitFeld( 50, 35 );
  m_ExtraTimer.InitFeld( 50, 35 );

  GR::IO::FileChunk    ChunkLevel;

  while ( ChunkLevel.Read( StreamIn ) )
  {
    MemoryStream   MemIn( ChunkLevel.GetMemoryStream() );

    switch ( ChunkLevel.Type() )
    {
      case 0x0001:
        {
          GR::u32     dwVersion = MemIn.ReadU32();
          std::string strName = MemIn.ReadString();
        }
        break;
      case 0x0002:
        {
          GR::u32     dwWidth = MemIn.ReadU32();
          GR::u32     dwHeight = MemIn.ReadU32();

          m_Field.InitFeld( dwWidth, dwHeight );
          m_ExtraTimer.InitFeld( dwWidth, dwHeight );

          MemIn.ReadBlock( m_Field.Data(), m_Field.DataSize() );
        }
        break;
      case 0x0003:
        {
          Pac::eUnitTypes   UnitType = ( Pac::eUnitTypes)MemIn.ReadU32();

          Unit*    pUnit = Unit::FromType( UnitType );
          if ( pUnit )
          {
            pUnit->m_Position.x = MemIn.ReadU32();
            pUnit->m_Position.y = MemIn.ReadU32();
            pUnit->m_ExtraData = MemIn.ReadU32();
            pUnit->m_ExtraData2 = MemIn.ReadU32();

            m_Units.push_back( pUnit );
            pUnit->OnSpawned( *m_pState );
          }
          else
          {
            MemIn.ReadU32();
            MemIn.ReadU32();
            MemIn.ReadU32();
            MemIn.ReadU32();
          }
        }
        break;
      case 0x0004:
        {
          GR::tPoint    pos;

          pos.x = MemIn.ReadI32();
          pos.y = MemIn.ReadI32();

          m_ExtraStartPositions.push_back( pos );
        }
        break;
      case 0x0005:
        {
          GR::tPoint    pos;

          pos.x = MemIn.ReadI32();
          pos.y = MemIn.ReadI32();

          m_FlashStartPositions.push_back( pos );
        }
        break;
    }
  }
  return !m_Units.empty();
}



bool Level::Save( IIOStream& StreamOut )
{
  GR::IO::FileChunk    ChunkLevel( 0x0001 );

  // Version
  ChunkLevel.AppendU32( 1 );
  ChunkLevel.AppendString( "Name" );

  if ( !ChunkLevel.Write( StreamOut ) )
  {
    return false;
  }

  GR::IO::FileChunk    ChunkData( 0x0002 );

  ChunkData.AppendU32( m_Field.Width() );
  ChunkData.AppendU32( m_Field.Height() );
  ChunkData.AppendData( m_Field.Data(), m_Field.DataSize() );

  if ( !ChunkData.Write( StreamOut ) )
  {
    return false;
  }

  tListUnits::iterator    it( m_Units.begin() );
  while ( it != m_Units.end() )
  {
    Unit*    pUnit( *it );

    GR::IO::FileChunk    ChunkUnit( 0x0003 );

    ChunkUnit.AppendU32( pUnit->m_Type );
    ChunkUnit.AppendU32( pUnit->m_Position.x );
    ChunkUnit.AppendU32( pUnit->m_Position.y );
    ChunkUnit.AppendU32( pUnit->m_ExtraData );
    ChunkUnit.AppendU32( pUnit->m_ExtraData2 );

    if ( !ChunkUnit.Write( StreamOut ) )
    {
      return false;
    }

    ++it;
  }

  auto    it2( m_ExtraStartPositions.begin() );
  while ( it2 != m_ExtraStartPositions.end() )
  {
    GR::IO::FileChunk    chunkExtraStartPos( 0x0004 );

    chunkExtraStartPos.AppendI32( it2->x );
    chunkExtraStartPos.AppendI32( it2->y );

    if ( !chunkExtraStartPos.Write( StreamOut ) )
    {
      return false;
    }

    ++it2;
  }

  auto    it3( m_FlashStartPositions.begin() );
  while ( it3 != m_FlashStartPositions.end() )
  {
    GR::IO::FileChunk    chunkFlashStartPos( 0x0005 );

    chunkFlashStartPos.AppendI32( it3->x );
    chunkFlashStartPos.AppendI32( it3->y );

    if ( !chunkFlashStartPos.Write( StreamOut ) )
    {
      return false;
    }

    ++it3;
  }
  return true;
}



GR::u32 Level::OnCollision( Unit* pUnit1, Unit* pUnit2 )
{
  return GameLogic::UnitsCollide( *m_pState, pUnit1, pUnit2 );
}



void Level::CreateBorders()
{
  // clear off all borders
  for ( int i = 0; i < m_Field.Width(); ++i )
  {
    for ( int j = 0; j < m_Field.Height(); ++j )
    {
      m_Field.SetField( i, j, Pac::FT_EMPTY );
    }
  }
}



bool Level::PrepareForStart()
{
  CreateBorders();

  m_StartPause = 2.5f;
  m_DonePause  = 0.0f;
  m_DeathPause = 0.0f;

  m_NastiesAlive = 0;

  tListUnits::iterator    it( m_Units.begin() );
  while ( it != m_Units.end() )
  {
    Unit*    pUnit( *it );

    if ( pUnit->m_IsNasty )
    {
      m_NastiesAlive++;
      pUnit->m_FaceLeft = !!( rand() % 2 );
    }
    ++it;
  }

  return true;
}



void Level::DetectExits( Unit& Unit, std::set<Pac::eDir>& Directions )
{
  GR::tPoint    curPos( Unit.m_TilePos );

  Directions.clear();

  if ( CanMove( &Unit, Unit.m_TilePos, Pac::DIR_W ) )
  {
    Directions.insert( Pac::DIR_W );
  }
  if ( CanMove( &Unit, Unit.m_TilePos, Pac::DIR_E ) )
  {
    Directions.insert( Pac::DIR_E );
  }
  if ( CanMove( &Unit, Unit.m_TilePos, Pac::DIR_N ) )
  {
    Directions.insert( Pac::DIR_N );
  }
  if ( CanMove( &Unit, Unit.m_TilePos, Pac::DIR_S ) )
  {
    Directions.insert( Pac::DIR_S );
  }
}



Pac::eDir Level::OppositeDirection( Pac::eDir Dir )
{
  switch ( Dir )
  {
    case Pac::DIR_E:
      return Pac::DIR_W;
    case Pac::DIR_W:
      return Pac::DIR_E;
    case Pac::DIR_N:
      return Pac::DIR_S;
    case Pac::DIR_S:
      return Pac::DIR_N;
  }
  return Pac::DIR_NONE;
}



Player* Level::FindPlayer( int PlayerIndex )
{
  tListUnits::iterator    it( m_Units.begin() );
  while ( it != m_Units.end() )
  {
    Unit*    pUnit( *it );

    if ( pUnit->m_Type == Pac::UT_PLAYER )
    {
      Player*   pPlayer = (Player*)pUnit;
      if ( pPlayer->m_PlayerIndex == PlayerIndex )
      {
        return pPlayer;
      }
    }

    ++it;
  }
  return NULL;
}



std::list<Unit*> Level::EnumerateUnits( Pac::eUnitTypes Type )
{
  std::list<Unit*>    listOfUnits;

  tListUnits::iterator    it( m_Units.begin() );
  while ( it != m_Units.end() )
  {
    Unit*    pUnit( *it );

    if ( pUnit->m_Type == Type )
    {
      listOfUnits.push_back( pUnit );
    }

    ++it;
  }

  return listOfUnits;
}



GR::tPoint Level::FindPotentialPos()
{
  while ( true )
  {
    int     x = 1 + rand() % ( m_Field.Width() - 2 );
    int     y = 1 + rand() % ( m_Field.Height() - 2 );

    if ( ( m_Field.Field( x, y ) == Pac::FT_DOT )
    ||   ( m_Field.Field( x, y ) == Pac::FT_EMPTY )
    ||   ( ( m_Field.Field( x, y ) >= Pac::FT_FIRST_EXTRA )
    &&     ( m_Field.Field( x, y ) <= Pac::FT_LAST_EXTRA ) ) )
    {
      return GR::tPoint( x, y );
    }
  }
}



GR::tPoint Level::FindPotentialEmptyPos()
{
  while ( true )
  {
    int     x = 1 + rand() % ( m_Field.Width() - 2 );
    int     y = 1 + rand() % ( m_Field.Height() - 2 );

    if ( m_Field.Field( x, y ) == Pac::FT_EMPTY )
    {
      return GR::tPoint( x, y );
    }
  }
}



void Level::Generate()
{
  m_Field.ResetField( Pac::FT_WALL_16 );
  m_NumDots = 0;
  m_TotalStartDots = 0;


  int     numPrimitives = 8 + rand() % 6;

  for ( int i = 0; i < numPrimitives; ++i )
  {
    GR::tPoint      startPos;
    if ( i == 0 )
    {
      // just start somewhere
      startPos.set( 1 + rand() % ( m_Field.Width() - 2 ), 1 + rand() % ( m_Field.Height() - 2 ) );
    }
    else
    {
      // find a potential pos
      startPos = FindPotentialPos();
    }

    int     primitiveType = rand() % 3;

    switch ( primitiveType )
    {
      case 0:
        // vertical line
        while ( true )
        {
          int     y1 = 1 + rand() % ( m_Field.Height() - 2 );
          int     y2 = 1 + rand() % ( m_Field.Height() - 2 );
          if ( ( y1 < y2 )
          &&   ( startPos.y >= y1 )
          &&   ( startPos.y <= y2 ) )
          {
            for ( int y = y1; y <= y2; ++y )
            {
              if ( m_Field.Field( startPos.x, y ) != Pac::FT_DOT )
              {
                m_Field.SetField( startPos.x, y, Pac::FT_DOT );
                ++m_NumDots;
              }
            }
            break;
          }
        }
        break;
      case 1:
        // horizontal line
        while ( true )
        {
          int     x1 = 1 + rand() % ( m_Field.Width() - 2 );
          int     x2 = 1 + rand() % ( m_Field.Width() - 2 );
          if ( ( x1 < x2 )
          &&   ( startPos.x >= x1 )
          &&   ( startPos.x <= x2 ) )
          {
            for ( int x = x1; x <= x2; ++x )
            {
              if ( m_Field.Field( x, startPos.y ) != Pac::FT_DOT )
              {
                m_Field.SetField( x, startPos.y, Pac::FT_DOT );
                ++m_NumDots;
              }
            }
            break;
          }
        }
        break;
      case 2:
        // square
        if ( rand() % 2 )
        {
          // match on X
          while ( true )
          {
            int     x1 = 1 + rand() % ( m_Field.Width() - 2 );
            int     x2 = 1 + rand() % ( m_Field.Width() - 2 );
            int     y2 = 1 + rand() % ( m_Field.Height() - 2 );
            if ( ( x1 < x2 )
            &&   ( startPos.x >= x1 )
            &&   ( startPos.x <= x2 ) )
            {
              for ( int x = x1; x <= x2; ++x )
              {
                if ( m_Field.Field( x, startPos.y ) != Pac::FT_DOT )
                {
                  m_Field.SetField( x, startPos.y, Pac::FT_DOT );
                  ++m_NumDots;
                }
                if ( m_Field.Field( x, y2 ) != Pac::FT_DOT )
                {
                  m_Field.SetField( x, y2, Pac::FT_DOT );
                  ++m_NumDots;
                }
              }
              int y1 = startPos.y;
              if ( y2 < y1 )
              {
                y1 = y2;
                y2 = startPos.y;
              }
              for ( int y = y1; y <= y2; ++y )
              {
                if ( m_Field.Field( x1, y ) != Pac::FT_DOT )
                {
                  m_Field.SetField( x1, y, Pac::FT_DOT );
                  ++m_NumDots;
                }
                if ( m_Field.Field( x2, y ) != Pac::FT_DOT )
                {
                  m_Field.SetField( x2, y, Pac::FT_DOT );
                  ++m_NumDots;
                }
              }
              break;
            }
          }
        }
        else
        {
          // match on Y
          while ( true )
          {
            int     y1 = 1 + rand() % ( m_Field.Height() - 2 );
            int     y2 = 1 + rand() % ( m_Field.Height() - 2 );
            int     x2 = 1 + rand() % ( m_Field.Width() - 2 );
            if ( ( y1 < y2 )
            &&   ( startPos.y >= y1 )
            &&   ( startPos.y <= y2 ) )
            {
              for ( int y = y1; y <= y2; ++y )
              {
                if ( m_Field.Field( startPos.x, y ) != Pac::FT_DOT )
                {
                  m_Field.SetField( startPos.x, y, Pac::FT_DOT );
                  ++m_NumDots;
                }
                if ( m_Field.Field( x2, y ) != Pac::FT_DOT )
                {
                  m_Field.SetField( x2, y, Pac::FT_DOT );
                  ++m_NumDots;
                }
              }
              int x1 = startPos.x;
              if ( x2 < x1 )
              {
                x1 = x2;
                x2 = startPos.x;
              }
              for ( int x = x1; x <= x2; ++x )
              {
                if ( m_Field.Field( x, y1 ) != Pac::FT_DOT )
                {
                  m_Field.SetField( x, y1, Pac::FT_DOT );
                  ++m_NumDots;
                }
                if ( m_Field.Field( x, y2 ) != Pac::FT_DOT )
                {
                  m_Field.SetField( x, y2, Pac::FT_DOT );
                  ++m_NumDots;
                }
              }
              break;
            }
          }
        }
        break;
    }
  }
  m_TotalStartDots = m_NumDots;
  AdjustBorders( 0, 0, m_Field.Width() - 1, m_Field.Height() - 1 );

  int     randomTile = 1 + ( rand() % 6 );
  m_TileAnimPos = theApp.AnimationPos( Misc::Format( "Tile.%1%" ) << randomTile );
}



bool Level::IsWall( Pac::eFeldTypes Feld )
{
  if ( ( Feld >= Pac::FT_WALL_1 )
  &&   ( Feld <= Pac::FT_WALL_16 ) )
  {
    return true;
  }
  return false;
}



void Level::AdjustBorders( int X1, int Y1, int X2, int Y2 )
{
  for ( int x = X1; x <= X2; ++x )
  {
    for ( int y = Y1; y <= Y2; ++y )
    {
      if ( !IsWall( m_Field.Field( x, y ) ) )
      {
        continue;
      }

      bool    upIsWall = IsWall( m_Field.Field( x, y - 1 ) );
      bool    downIsWall = IsWall( m_Field.Field( x, y + 1 ) );
      bool    leftIsWall = IsWall( m_Field.Field( x - 1, y ) );
      bool    rightIsWall = IsWall( m_Field.Field( x + 1, y ) );

      int     finalField = Pac::FT_WALL_1;
      if ( !upIsWall )
      {
        finalField += 1;
      }
      if ( !leftIsWall )
      {
        finalField += 2;
      }
      if ( !rightIsWall )
      {
        finalField += 4;
      }
      if ( !downIsWall )
      {
        finalField += 8;
      }
      m_Field.SetField( x, y, ( Pac::eFeldTypes )finalField );
    }
  }
}