#include "TiledMover.h"
#include "Level.h"
#include "GetEmGood.h"
#include "PlayState.h"
#include "GameLogic.h"



static int    s_MoveSpeeds[4][4] =
          { { 0, 0, 0, 0 },
            { 0, 0, 0, 1 },
            { 1, 0, 1, 0 },
            { 1, 0, 1, 1 }
          };




TiledMover::TiledMover() :
  m_NotMoving( false )
{
}



bool TiledMover::Move( PlayState& State, const GR::tPoint& DeltaArg, bool bMovedByPlatform )
{
  return Unit::Move( State, DeltaArg, bMovedByPlatform );
}



void TiledMover::UpdateFixed( PlayState& State )
{
  Unit::UpdateFixed( State );

  if ( m_MoveDir != Pac::DIR_NONE )
  {
    GR::tPoint    deltaMove;

    m_MoveSpeedFract = ( m_MoveSpeedFract + 1 ) % 4;

    int           stepPixelDelta = m_MoveSpeed / 4;
    int           stepPixelRow = m_MoveSpeed % 4;

    int           moveSpeed = s_MoveSpeeds[stepPixelRow][m_MoveSpeedFract] + stepPixelDelta;

    switch ( m_MoveDir )
    {
      case Pac::DIR_N:
        deltaMove.set( 0, -moveSpeed );
        break;
      case Pac::DIR_S:
        deltaMove.set( 0, moveSpeed );
        break;
      case Pac::DIR_W:
        deltaMove.set( -moveSpeed, 0 );
        break;
      case Pac::DIR_E:
        deltaMove.set( moveSpeed, 0 );
        break;
    }
    if ( !Move( State, deltaMove ) )
    {
      OnArrivedAtTile( State );
    }
    /*
    if ( ( m_Position.x % 16 == 0 )
    &&   ( m_Position.y % 16 == 0 ) )
    {
      OnArrivedAtTile( State );
    }*/
  }
  else
  {
    OnArrivedAtTile( State );
  }
}



void TiledMover::OnArrivedAtTile( PlayState& State )
{
  if ( GameLogic::CanUnitPickKey( m_Type ) )
  {
    if ( State.Level.m_Field.Field( m_TilePos.x, m_TilePos.y ) == Pac::FT_KEY )
    {
      State.Level.m_Field.SetField( m_TilePos.x, m_TilePos.y, Pac::FT_EMPTY );
      m_DisplayBlinking = true;
      m_HasKey = true;

      if ( m_Type == Pac::UT_PLAYER )
      {
        State.Hint = "UNLOCK THE EXIT";
      }
      else
      {
        State.Hint = "RETRIEVE THE KEY";
      }

      theApp.SoundClass()->Play( theApp.Sound( "Pickup.Extra" ) );
    }
  }
}



void TiledMover::AutoDetectDirection( PlayState& State )
{
  GR::tPoint    curPos( m_TilePos );

  bool          canMoveLeft   = State.Level.CanMove( this, curPos, Pac::DIR_W );
  bool          canMoveRight  = State.Level.CanMove( this, curPos, Pac::DIR_E );
  bool          canMoveUp     = State.Level.CanMove( this, curPos, Pac::DIR_N );
  bool          canMoveDown   = State.Level.CanMove( this, curPos, Pac::DIR_S );

  if ( m_MoveDir == Pac::DIR_NONE )
  {
    return;
  }

  // continue onwards
  if ( ( ( m_MoveDir == Pac::DIR_W )
  &&     ( canMoveLeft ) )
  ||   ( ( m_MoveDir == Pac::DIR_E )
  &&     ( canMoveRight ) )
  ||   ( ( m_MoveDir == Pac::DIR_N )
  &&     ( canMoveUp ) )
  ||   ( ( m_MoveDir == Pac::DIR_S )
  &&     ( canMoveDown ) ) )
  {
    m_TileOffset = 16;
    return;
  }
  /*
  // only two possible directions?
  std::set<Pac::eDir>   potentialDirections;

  if ( ( canMoveLeft )
  &&   ( m_MoveDir != Pac::DIR_E ) )
  {
    potentialDirections.insert( Pac::DIR_W );
  }
  if ( ( canMoveRight )
  &&   ( m_MoveDir != Pac::DIR_W ) )
  {
    potentialDirections.insert( Pac::DIR_E );
  }
  if ( ( canMoveUp )
  &&   ( m_MoveDir != Pac::DIR_S ) )
  {
    potentialDirections.insert( Pac::DIR_N );
  }
  if ( ( canMoveDown )
  &&   ( m_MoveDir != Pac::DIR_N ) )
  {
    potentialDirections.insert( Pac::DIR_S );
  }

  bool    foundNewDir = false;
  if ( potentialDirections.size() == 1 )
  {
    m_MoveDir = *potentialDirections.begin();
    foundNewDir = true;
  }
  else
  {
    // prefer right over left?
    switch ( m_MoveDir )
    {
      case Pac::DIR_W:
        if ( canMoveUp )
        {
          m_MoveDir = Pac::DIR_N;
          foundNewDir = true;
        }
        break;
      case Pac::DIR_N:
        if ( canMoveRight )
        {
          m_MoveDir = Pac::DIR_E;
          foundNewDir = true;
        }
        break;
      case Pac::DIR_E:
        if ( canMoveDown )
        {
          m_MoveDir = Pac::DIR_S;
          foundNewDir = true;
        }
        break;
      case Pac::DIR_S:
        if ( canMoveLeft )
        {
          m_MoveDir = Pac::DIR_W;
          foundNewDir = true;
        }
        break;
    }
    if ( !foundNewDir )
    {
      // this should actually not be possible unless there's one ways!
      dh::Log( "Invalid dir detection state!" );
    }
  }
  */

  m_MoveDir = Pac::DIR_NONE;
}



void TiledMover::Update( PlayState& State, const GR::f32 ElapsedTime )
{
  Unit::Update( State, ElapsedTime );

  if ( m_TileOffset == 0 )
  {
    AutoDetectDirection( State );
  }
}



void TiledMover::ProcessEvent( PlayState& State, const eUnitEvent& Event )
{
  switch ( Event )
  {
    case ET_BLOCKED_UP:
      break;
    case ET_FALL_START:
      break;
    case ET_FALL_END:
      break;
    case ET_ENTER_PLATFORM:
      break;
    case ET_LEAVE_PLATFORM:
      break;
    case ET_DIE:
      break;
    case ET_MOVED_DOWN:
    case ET_MOVED_LEFT:
    case ET_MOVED_RIGHT:
    case ET_MOVED_UP:
      if ( ( ( m_Position.x % 16 ) == 0 )
      &&   ( ( m_Position.y % 16 ) == 0 ) )
      {
        m_TilePos = m_Position / 16;
        OnArrivedAtTile( State );
      }
      break;
  }
}



void TiledMover::SetTilePos( int X, int Y )
{
  m_TilePos.set( X, Y );

  m_Position.x = m_TilePos.x * 16 - ( 32 - 16 ) / 2;
  m_Position.y = m_TilePos.y * 16 - ( 32 - 16 ) / 2;
}



void TiledMover::TurnAround()
{
  m_NotMoving = false;
  switch ( m_MoveDir )
  {
    case Pac::DIR_E:
      m_MoveDir = Pac::DIR_W;
      if ( m_TileOffset != 0 )
      {
        m_TileOffset = 16 - m_TileOffset;
        ++m_TilePos.x;
      }
      break;
    case Pac::DIR_W:
      m_MoveDir = Pac::DIR_E;
      if ( m_TileOffset != 0 )
      {
        m_TileOffset = 16 - m_TileOffset;
        --m_TilePos.x;
      }
      break;
    case Pac::DIR_S:
      m_MoveDir = Pac::DIR_N;
      if ( m_TileOffset != 0 )
      {
        m_TileOffset = 16 - m_TileOffset;
        ++m_TilePos.y;
      }
      break;
    case Pac::DIR_N:
      m_MoveDir = Pac::DIR_S;
      if ( m_TileOffset != 0 )
      {
        m_TileOffset = 16 - m_TileOffset;
        --m_TilePos.y;
      }
      break;
  }
}



void TiledMover::OnSpawned( PlayState& State )
{
  Unit::OnSpawned( State );
  OnArrivedAtTile( State );
}