#include "Player.h"
#include "LD.h"
#include "Level.h"
#include "HomingEnemy.h"
#include "SittingEnemy.h"
#include "Particle.h"

#include <list>



Player::Player() :
  GameObject( LD::GO_PLAYER )
{
  m_Texture = LD::TEX_PLAYER_1;
  Width = 32;
  Height = 32;
  AnimDelay = 0.0f;
  SizeFactor = 0.25f;
  Moving = false;
  LastDir = 0;
}


void Player::Display()
{
  int     trueWidth = (int)( Width * SizeFactor );
  int     trueHeight = (int)( Height * SizeFactor );

  /*
  theApp.RenderTextureSection2d( X - trueWidth / 2, Y - trueHeight / 2, 
                                 theApp.m_TexSec[m_Texture], 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 
                                 trueWidth, trueHeight );*/
  theApp.RenderTextureSectionRotated2d( X, Y, 
    theApp.m_TexSec[m_Texture], Angle, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
    trueWidth, trueHeight );
}



void Player::Grow( Level& Level, float GrowFactor )
{
  int     oldWidth = (int)( Width * SizeFactor );
  int     oldHeight = (int)( Height * SizeFactor );

  float     newFactor = SizeFactor + GrowFactor;
  int     trueWidth = (int)( Width * newFactor );
  int     trueHeight = (int)( Height * newFactor );

  if ( ( trueWidth == oldWidth )
  &&   ( trueHeight == oldHeight ) )
  {
    SizeFactor += GrowFactor;
    return;
  }
  // grow carefully to all sides to avoid getting stuck in a wall
  int       dx = trueWidth - oldWidth;
  int       dy = trueHeight - oldHeight;

  RECT      rc( Bounds() );

  while ( ( dx > 0 )
  ||      ( dy > 0 ) )
  {
    bool    wantedToGrowX = ( dx > 0 );
    bool    couldGrowX = false;
    bool    wantedToGrowY = ( dy > 0 );
    bool    couldGrowY = false;
    if ( dx > 0 )
    {
      ++rc.right;

      if ( Level.IsAreaBlocked( this, rc ) )
      {
        --rc.right;
      }
      else
      {
        --dx;
        couldGrowX = true;
      }
    }
    if ( dx > 0 )
    {
      --rc.left;

      if ( Level.IsAreaBlocked( this, rc ) )
      {
        ++rc.left;
      }
      else
      {
        --dx;
        couldGrowX = true;
        --X;
      }
    }
    if ( dy > 0 )
    {
      ++rc.bottom;

      if ( Level.IsAreaBlocked( this, rc ) )
      {
        --rc.bottom;
      }
      else
      {
        --dy;
        couldGrowY = true;
      }
    }
    if ( dy > 0 )
    {
      --rc.top;

      if ( Level.IsAreaBlocked( this, rc ) )
      {
        ++rc.top;
      }
      else
      {
        --dy;
        couldGrowY = true;
        --Y;
      }
    }

    if ( ( ( wantedToGrowX )
    &&     ( !couldGrowX ) )
    ||   ( ( wantedToGrowY )
    &&     ( !couldGrowY ) ) )
    {
      // could not grow, stuck!
      // TODO - pop!
      RemoveMe = true;
      return;
    }
  }
  SizeFactor += GrowFactor;

  RECT    bounds = Bounds();

  if ( ( bounds.right != rc.right )
    || ( bounds.left != rc.left )
    || ( bounds.top != rc.top )
    || ( bounds.bottom != rc.bottom ) )
  {
    if ( ( bounds.right - bounds.left == rc.right - rc.left )
    &&   ( bounds.bottom - bounds.top == rc.bottom - rc.top ) )
    {
      X += rc.left - bounds.left;
      Y += rc.top - bounds.top;
    }
    else
    {
      int a = 0;
    }
  }
}



void Player::Shrink( Level& Level, float GrowFactor )
{
  int     oldWidth = (int)( Width * SizeFactor );
  int     oldHeight = (int)( Height * SizeFactor );

  float     newFactor = SizeFactor - GrowFactor;
  int     trueWidth = (int)( Width * newFactor );
  int     trueHeight = (int)( Height * newFactor );

  if ( ( trueWidth == oldWidth )
  &&   ( trueHeight == oldHeight ) )
  {
    SizeFactor -= GrowFactor;
    return;
  }
  // grow carefully to all sides to avoid getting stuck in a wall
  int       dx = trueWidth - oldWidth;
  int       dy = trueHeight - oldHeight;

  while ( ( dx < 0 )
  ||      ( dy < 0 ) )
  {
    bool    wantedToGrowX = ( dx < 0 );
    bool    couldGrowX = false;
    bool    wantedToGrowY = ( dy < 0 );
    bool    couldGrowY = false;
    if ( dx < 0 )
    {
      RECT    rc = Bounds();

      --rc.right;

      if ( Level.IsAreaBlocked( this, rc ) )
      {
        ++rc.right;
      }
      else
      {
        ++dx;
        couldGrowX = true;
      }
    }
    if ( dx < 0 )
    {
      RECT    rc = Bounds();

      ++rc.left;

      if ( Level.IsAreaBlocked( this, rc ) )
      {
        --rc.left;
      }
      else
      {
        ++dx;
        couldGrowX = true;
        ++X;
      }
    }
    if ( dy < 0 )
    {
      RECT    rc = Bounds();

      --rc.bottom;

      if ( Level.IsAreaBlocked( this, rc ) )
      {
        ++rc.bottom;
      }
      else
      {
        ++dy;
        couldGrowY = true;
      }
    }
    if ( dy < 0 )
    {
      RECT    rc = Bounds();

      ++rc.top;

      if ( Level.IsAreaBlocked( this, rc ) )
      {
        --rc.top;
      }
      else
      {
        ++dy;
        couldGrowY = true;
        ++Y;
      }
    }

    if ( ( ( wantedToGrowX )
    &&     ( !couldGrowX ) )
    ||   ( ( wantedToGrowY )
    &&     ( !couldGrowY ) ) )
    {
      // could not grow, stuck!
      // TODO - pop!
      RemoveMe = true;
      Level.SpawnParticles( 50, X, Y, 0xffff0000 );
      return;
    }
  }
  SizeFactor -= GrowFactor;
}



void Player::Update( Level& Level, float ElapsedTime )
{
  bool    moved = false;
  if ( theApp.m_bKeyPressed[VK_LEFT] )
  {
    XFract -= 100.0f * ElapsedTime;
    moved = true;
  }
  if ( theApp.m_bKeyPressed[VK_RIGHT] )
  {
    XFract += 100.0f * ElapsedTime;
    moved = true;
  }
  if ( theApp.m_bKeyPressed[VK_UP] )
  {
    YFract -= 100.0f * ElapsedTime;
    moved = true;
  }
  if ( theApp.m_bKeyPressed[VK_DOWN] )
  {
    YFract += 100.0f * ElapsedTime;
    moved = true;
  }

  int     newDir = 0;

  if ( theApp.m_bKeyPressed[VK_LEFT] )
  {
    newDir |= 1;
  }
  if ( theApp.m_bKeyPressed[VK_RIGHT] )
  {
    newDir |= 2;
  }
  if ( theApp.m_bKeyPressed[VK_UP] )
  {
    newDir |= 4;
  }
  if ( theApp.m_bKeyPressed[VK_DOWN] )
  {
    newDir |= 8;
  }
  if ( newDir != LastDir )
  {
    Angle = ( rand() % 8 ) * 45.0f;
    LastDir = newDir;
  }

  if ( Moving != moved )
  {
    Moving = moved;
    if ( Moving )
    {
      theApp.LoopSound( LD::SOUND_SLIME );
    }
    else
    {
      theApp.StopSound( LD::SOUND_SLIME );
    }
  }

  if ( moved )
  {
    AnimDelay += ElapsedTime;
    if ( fmodf( AnimDelay, 0.5f ) >= 0.25f )
    {
      m_Texture = LD::TEX_PLAYER_1; 
    }
    else
    {
      m_Texture = LD::TEX_PLAYER_2;
    }
  }

  Level.MoveObject( this, 0.0f, 0.0f );

  std::list<GameObject*>::iterator    itO( Level.Objects.begin() );
  while ( itO != Level.Objects.end() )
  {
    GameObject*   pObj( *itO );

    if ( pObj->m_Type == LD::GO_PLANKTON )
    {
      RECT    rcDummy;
      if ( IntersectRect( &rcDummy, &Bounds(), &pObj->Bounds() ) )
      {
        pObj->RemoveMe = true;
        Grow( Level, 0.05f );
        theApp.PlaySoundA( LD::SOUND_EAT );
      }
    }
    else if ( ( pObj->m_Type == LD::GO_HOMING_ENEMY )
    ||        ( pObj->m_Type == LD::GO_SITTING_ENEMY ) )
    {
      RECT    rcDummy;
      if ( IntersectRect( &rcDummy, &Bounds(), &pObj->Bounds() ) )
      {
        if ( pObj->m_Type == LD::GO_SITTING_ENEMY )
        {
          SittingEnemy*  pEnemy = (SittingEnemy*)pObj;
          if ( SizeFactor > pEnemy->SizeFactor )
          {
            Shrink( Level, 0.05f );
            //Grow( Level, 0.15f );
            //SizeFactor += 0.15f;
            pObj->RemoveMe = true;
            theApp.PlaySoundA( LD::SOUND_EAT );

            Level.SpawnParticles( 50, pObj->X, pObj->Y, 0xff00ffff );
          }
        }
        else
        {
          HomingEnemy*  pEnemy = (HomingEnemy*)pObj;
          if ( SizeFactor > pEnemy->SizeFactor )
          {
            Shrink( Level, 0.05f );
            //Grow( Level, 0.15f );
            //SizeFactor += 0.15f;
            pObj->RemoveMe = true;
            theApp.PlaySoundA( LD::SOUND_EAT );

            Level.SpawnParticles( 50, pObj->X, pObj->Y, 0xffff0000 );
          }
          else if ( pObj->m_Type == LD::GO_HOMING_ENEMY )
          {
            // eaten by enemy!
            RemoveMe = true;

            Level.SpawnParticles( 50, X, Y, 0xff00ff00 );
          }
        }
      }
    }
    ++itO;
  }
}



RECT Player::Bounds()
{
  RECT    rc;

  int     trueWidth = (int)( Width * SizeFactor );
  int     trueHeight = (int)( Height * SizeFactor );

  SetRect( &rc, X - trueWidth / 2, Y - trueHeight / 2, X - trueWidth / 2 + trueWidth, Y - trueHeight / 2 + trueHeight );

  return rc;
}



void Player::OnObjectRemoved( Level& Level, GameObject* pKilledObject )
{
  if ( pKilledObject == this )
  {
    theApp.StopSound( LD::SOUND_SLIME );
    theApp.PlaySoundA( LD::SOUND_DIE );
  }
}
