#include "GameLogic.h"
#include "PlayState.h"
#include "Unit.h"
#include "Player.h"
#include "GetEmGood.h"



namespace GameLogic
{
  void ActivateExtra( PlayState& State, Pac::eFeldTypes PickedExtra, int PlayerIndex )
  {
    switch ( PickedExtra )
    {
      case Pac::FT_EXTRA_LIFE:
        ++State.Lives;
        State.Score += 50;
        break;
      case Pac::FT_EXTRA_SPEED_UP:
        {
          ++State.Player[PlayerIndex].MoveSpeed;

          Player* pPlayer = State.Level.FindPlayer( PlayerIndex );
          if ( pPlayer != NULL )
          {
            pPlayer->m_MoveSpeed = State.Player[PlayerIndex].MoveSpeed;
          }
          State.Score += 40;
        }
        break;
      case Pac::FT_EXTRA_BULLET_POWER:
        {
          ++State.Player[PlayerIndex].BulletPower;

          Player* pPlayer = State.Level.FindPlayer( PlayerIndex );
          if ( pPlayer != NULL )
          {
            pPlayer->m_BulletPower = State.Player[PlayerIndex].BulletPower;
          }
          State.Score += 30;
        }
        break;
      case Pac::FT_EXTRA_FIRE_DELAY:
        {
          if ( State.Player[PlayerIndex].FireDelay > 4 )
          {
            --State.Player[PlayerIndex].FireDelay;

            Player* pPlayer = State.Level.FindPlayer( PlayerIndex );
            if ( pPlayer != NULL )
            {
              pPlayer->m_FireDelayMax = State.Player[PlayerIndex].FireDelay;
            }
          }
          State.Score += 10;
        }
        break;
      case Pac::FT_EXTRA_SHIELD:
        {
          Player* pPlayer = State.Level.FindPlayer( PlayerIndex );
          if ( pPlayer != NULL )
          {
            pPlayer->m_Invincibility = 300;
            pPlayer->m_DisplayBlinkingFast = true;
          }
          State.Score += 20;
        }
        break;
      case Pac::FT_EXTRA_SLOW_DOWN:
        {
          Player* pPlayer = State.Level.FindPlayer( PlayerIndex );
          if ( pPlayer != NULL )
          {
            pPlayer->m_SlowDown = 200;
          }
          State.Score += 25;
        }
        break;
      case Pac::FT_EXTRA_SUPER_KILL:
        {
          std::list<Unit*>::iterator    it( State.Level.m_Units.begin() );
          while ( it != State.Level.m_Units.end() )
          {
            Unit*   pUnit( *it );
            if ( IsEnemy( pUnit->m_Type ) )
            {
              KillEnemy( State, PlayerIndex, *pUnit );
            }
            ++it;
          }
          State.Score += 15;
        }
        break;
      case Pac::FT_EXTRA_TIME:
        State.Bonus += 100;
        State.Score += 5;
        break;
    }
  }



  bool IsEnemy( Pac::eUnitTypes UnitType )
  {
    return ( ( UnitType == Pac::UT_GREEN_MASK )
    ||       ( UnitType == Pac::UT_RED_MASK ) 
    ||       ( UnitType == Pac::UT_BOMB )
    ||       ( UnitType == Pac::UT_WILD )
    ||       ( UnitType == Pac::UT_CATERPILLAR ) );
  }



  void ExtraPickedUp( PlayState& State, const GR::tPoint& TilePos, int PlayerIndex )
  {
    Pac::eFeldTypes     pickedExtra = State.Level.m_Field.Field( TilePos.x, TilePos.y );

    if ( State.ExtraState[pickedExtra - Pac::FT_FIRST_EXTRA] == 8 )
    {
      ActivateExtra( State, pickedExtra, PlayerIndex );
      State.ExtraState[pickedExtra - Pac::FT_FIRST_EXTRA] = 0;
      theApp.SoundClass()->Play( theApp.Sound( "Extra.Activate" ) );
    }
    else
    {
      ++State.Score;
      ++State.ExtraState[pickedExtra - Pac::FT_FIRST_EXTRA];
      theApp.SoundClass()->Play( theApp.Sound( "Pickup.Extra" ) );
    }
  }



  void DecreaseNumberOfDots( PlayState& State )
  {
    --State.Level.m_NumDots;
    if ( State.Level.m_NumDots == 0 )
    {
      State.Hint = "GET THE KEY";
      // spawn key and lock
      GR::tPoint    potentialPos = State.Level.FindPotentialEmptyPos();
      State.Level.m_Field.SetField( potentialPos.x, potentialPos.y, Pac::FT_KEY );

      GR::tPoint    potentialPos2 = State.Level.FindPotentialEmptyPos();
      State.Level.m_Field.SetField( potentialPos2.x, potentialPos2.y, Pac::FT_LOCK );

      theApp.SoundClass()->Play( theApp.Sound( "All.Dots" ) );
    }
  }



  void GenerateExtra( PlayState& State, const GR::tPoint& TilePos )
  {
    Pac::eFeldTypes     extraTile = (Pac::eFeldTypes)( Pac::FT_FIRST_EXTRA + rand() % 8 );

    for ( int i = -1; i <= 1; ++i )
    {
      for ( int j = -1; j <= 1; ++j )
      {
        if ( ( TilePos.x + i >= 1 )
        &&   ( TilePos.x + i < State.Level.m_Field.Width() - 1 )
        &&   ( TilePos.y + j >= 1 )
        &&   ( TilePos.y + j < State.Level.m_Field.Height() - 1 ) )
        {
          if ( State.Level.m_Field.Field( TilePos.x + i, TilePos.y + j ) == Pac::FT_DOT )
          {
            DecreaseNumberOfDots( State );
            State.Level.m_Field.SetField( TilePos.x + i, TilePos.y + j, extraTile );
            State.Level.m_ExtraTimer.SetField( TilePos.x + i, TilePos.y + j, 450 );
          }
          else if ( ( ( State.Level.m_Field.Field( TilePos.x + i, TilePos.y + j ) >= Pac::FT_FIRST_EXTRA )
          &&          ( State.Level.m_Field.Field( TilePos.x + i, TilePos.y + j ) <= Pac::FT_LAST_EXTRA ) )
          ||        ( State.Level.m_Field.Field( TilePos.x + i, TilePos.y + j ) == Pac::FT_EMPTY ) ) 
          {
            State.Level.m_Field.SetField( TilePos.x + i, TilePos.y + j, extraTile );
            State.Level.m_ExtraTimer.SetField( TilePos.x + i, TilePos.y + j, 450 );
          }
        }
      }
    }
  }



  void KillPlayer( PlayState& State, Unit* pUnit )
  {
    pUnit->m_RemoveMe = true;
    theApp.SoundClass()->Play( theApp.Sound( "Player.Die" ) );

    if ( pUnit->m_HasKey )
    {
      DropKey( State, pUnit->m_TilePos );
      State.Hint = "GET THE KEY";
    }

    State.Player[pUnit->m_PlayerIndex].DeathDelay = 150;
  }



  GR::u32 UnitsCollide( PlayState& State, Unit* pUnit1, Unit* pUnit2 )
  {
    if ( pUnit1->m_Type == Pac::UT_PLAYER )
    {
      if ( IsEnemy( pUnit2->m_Type ) )
      {
        
        State.Level.SpawnUnit( pUnit1->m_Position.x, pUnit1->m_Position.y, Pac::UT_EXPLOSION );

        KillPlayer( State, pUnit1 );
      }
    }
    else if ( pUnit1->m_Type == Pac::UT_PLAYER_SHOT )
    {
      if ( IsEnemy( pUnit2->m_Type ) )
      {
        pUnit2->m_HP -= pUnit1->m_HP;
        if ( pUnit2->m_HP <= 0 )
        {
          KillEnemy( State, pUnit1->m_PlayerIndex, *pUnit2 );
        }
        else
        {
          // hit effect!
          ++State.Score;
          theApp.SoundClass()->Play( theApp.Sound( "Enemy.Hurt" ) );
          pUnit2->SetHit();
        }
        pUnit1->m_RemoveMe = true;
      }
    }
    return false;
  }



  void ExplodeBomb( PlayState& State, const GR::tPoint& TilePos )
  {
    theApp.SoundClass()->Play( theApp.Sound( "Bomb.Explode" ) );

    std::list<Unit*>::iterator    itU( State.Level.m_Units.begin() );
    while ( itU != State.Level.m_Units.end() )
    {
      Unit*   pUnit( *itU );

      if ( ( !pUnit->m_RemoveMe )
      &&   ( pUnit->m_Invincibility == 0 ) )
      {
        if ( ( math::absolute( pUnit->m_TilePos.x - TilePos.x ) <= 1 )
        &&   ( math::absolute( pUnit->m_TilePos.y - TilePos.y ) <= 1 ) )
        {
          if ( IsEnemy( pUnit->m_Type ) )
          {
            KillEnemy( State, -1, *pUnit );
          }
          else if ( pUnit->m_Type == Pac::UT_PLAYER )
          {
            KillPlayer( State, pUnit );
          }
        }
      }
      ++itU;
    }

    for ( int i = -1; i <= 1; ++i )
    {
      for ( int j = -1; j <= 1; ++j )
      {
        if ( ( TilePos.x + i >= 1 )
        &&   ( TilePos.x + i < State.Level.m_Field.Width() - 1 )
        &&   ( TilePos.y + j >= 1 )
        &&   ( TilePos.y + j < State.Level.m_Field.Height() - 1 ) )
        {
          Pac::eFeldTypes   fieldBelow = State.Level.m_Field.Field( TilePos.x + i, TilePos.y + j );
          if ( ( fieldBelow >= Pac::FT_FIRST_EXTRA )
          &&   ( fieldBelow <= Pac::FT_LAST_EXTRA ) )
          {
            // remove timer
            State.Level.m_ExtraTimer.SetField( TilePos.x + i, TilePos.y + j, 0 );
          }
          else if ( fieldBelow == Pac::FT_DOT )
          {
            DecreaseNumberOfDots( State );
          }
          else if ( ( fieldBelow == Pac::FT_LOCK )
          ||        ( fieldBelow == Pac::FT_KEY ) )
          {
            // do not explode those
            continue;
          }
          State.Level.m_Field.SetField( TilePos.x + i, TilePos.y + j, Pac::FT_EMPTY );
        }
      }
    }
    State.Level.AdjustBorders( TilePos.x - 2, TilePos.y - 2, TilePos.x + 2, TilePos.y + 2 );
  }



  void KillEnemy( PlayState& State, int PlayerIndex, Unit& Unit )
  {
    if ( PlayerIndex >= 0 )
    {
      switch ( Unit.m_Type )
      {
        case Pac::UT_GREEN_MASK:
          State.Score += 10;
          break;
        case Pac::UT_RED_MASK:
          State.Score += 50;
          break;
        case Pac::UT_CATERPILLAR:
          State.Score += 100;
          break;
        case Pac::UT_BOMB:
          State.Score += 250;
          break;
        case Pac::UT_WILD:
          State.Score += 500;
          break;
        default:
          State.Score += 100;
          break;
      }
    }
    Unit.m_RemoveMe = true;
    State.Level.SpawnUnit( Unit.m_Position.x, Unit.m_Position.y, Pac::UT_EXPLOSION );

    if ( Unit.m_Type == Pac::UT_BOMB )
    {
      ExplodeBomb( State, Unit.m_TilePos );
    }
    else
    {
      theApp.SoundClass()->Play( theApp.Sound( "Enemy.Die" ) );
      GenerateExtra( State, Unit.m_Position / 16 );
    }
    if ( Unit.m_HasKey )
    {
      DropKey( State, Unit.m_TilePos );
      State.Hint = "GET THE KEY";
    }
  }



  void DropKey( PlayState& State, const GR::tPoint& TilePos )
  {
    // TODO - what if above lock?? Can happen with enemies shot at the exit
    if ( State.Level.m_Field.Field( TilePos.x, TilePos.y ) == Pac::FT_LOCK )
    {
      dh::Log( "Dropped key on top of lock!!" );
    }
    State.Level.m_Field.SetField( TilePos.x, TilePos.y, Pac::FT_KEY );
    State.Level.m_ExtraTimer.SetField( TilePos.x, TilePos.y, 0 );
  }



  bool CanUnitPickKey( Pac::eUnitTypes UnitType )
  {
    if ( ( UnitType == Pac::UT_PLAYER )
    ||   ( IsEnemy( UnitType ) ) )
    {
      return true;
    }
    return false;
  }



  void LevelCompleted( PlayState& State )
  {
    State.LevelIsCompleted = true;
    State.Player[0].DeathDelay = 0;
    State.Player[1].DeathDelay = 0;
    State.LevelEndDelay         = 100;

    theApp.PlaySong( "" );
    theApp.SoundClass()->Play( theApp.Sound( "Level.Done" ) );
  }

}



