#include "TitanInputHandler.h"
#include "SyAssert.h"
#include "SyDebug.h"
#include "SyScene.h"
#include "gameobj.h"
#include "animcontroller.h"
#include "graphic.h"
#include "intel.h"
#include "database.h"
#include "registry.h"
#include "spell.h"
#include "tuning.h"
#include "titan.h"
#include "stats.h"
#include "cameracontroller.h"
#include "debugoverlay.h"
#include "SyMemory.h"
#include "script_pawn.h"

static int s_resetHack = true;

TitanInputHandler::TitanInputHandler( TitanUII* titanUI )
: mpTitanUI( titanUI )
{
  Reset();
}

void TitanInputHandler::OnButtonDown( TitanButton tb )
{
  TitanControllerI* controller = mpTitanUI->GetController(mPlayerIndex);
  SyDebug( "TitanInputHandler::OnButtonDown %i (%i,%i)\n", tb, controller->GetForward(), controller->GetRight() );

  cGraphicCharacter *graphic = prop_cast<cGraphicCharacter*>(mpObject->GetGraphic());
  SyAssert(graphic);
  cAnimCharControllerInput *input = graphic->GetAnimInput();

  if (controller->GetButtonState(ButtonBlock) &&
      controller->GetButtonState(ButtonMagic) &&
      controller->GetButtonState(ButtonDodge) &&
      controller->GetButtonState(ButtonAttackRanged) 
      && s_resetHack) 
  {
    s_resetHack = false;
    // test code
    if (controller->GetButtonState(ButtonJump))
    {
      // save game 
      mpObject->GetTitan()->SaveGameOverwriteLast();
    }
    else if (controller->GetButtonState(ButtonAction))
    {
      // load game
      mpObject->GetTitan()->SaveGameLoadLast();
    }
    else if (controller->GetButtonState(ButtonAttackL))
    {
      gTuningSys.LoadValues("tuning");
    }
    else
    {
      SyVect3 oldLoc = mpObject->GetLocation();

      // reset player
      mpObject->Reset();
      mpObject->GetTitan()->GetCameraController()->RequestHeading(mpObject->GetHeading());

      if (mpObject->GetTitan()->GetNumLocalPlayers() > 1)
      {
        mpObject->SetSafeLocation(oldLoc);
      }
    }
  }

  if (!mpObject->GetTitan()->AllowCutSceneControllerMotion())
  {
    if (ButtonPause == tb)
    {
      mpObject->GetTitan()->GetScriptSys()->SkipCutscene(true);
    }
    return;
  }
  
  if (tb != ButtonAttackRanged && controller->GetButtonState(ButtonAttackRanged))
  {
    //we're holding the ranged button down, ignore input
    return;
  }

  if (ButtonMagic != tb && controller->GetButtonState(ButtonMagic))
  {
    const cAbilityMaster* pAbility = static_cast<cIntelPlayer*>(mpObject->GetIntel())->GetMappedAbility(tb);
    if (pAbility != NULL && pAbility->mSpell != ID_NONE)
    {
      mHeldSpellButton = tb;
      static_cast<cIntelPlayer*>(mpObject->GetIntel())->StartSpellCast(pAbility->mSpell);
      return;
    }
  }

  if (ButtonDPadRight != tb && controller->GetButtonState(ButtonDPadRight) &&
      (ButtonAction == tb || ButtonAttackL == tb || ButtonAttackS == tb || ButtonJump == tb))
  {
    int numAbilities = mpObject->GetTitan()->AbilityCurrentGetCount(mPlayerIndex);
    if (mSpellListIndex >= 0 && mSpellListIndex < numAbilities)
    {
      tGameID id;
      int rank;

      if (mpObject->GetTitan()->AbilityCurrentGetIDRank(mPlayerIndex, mSpellListIndex, &id, &rank))
      {
        const cAbilityMaster* pAbility = mpObject->GetTitan()->GetDatabaseSys()->GetAbilityMaster(id, rank);
        MapAbility(tb, pAbility);
        return;
      }
    }
  }

  eAnimState animState = graphic->GetAnimController()->GetAnimState();

  switch (tb)
  {
    case ButtonAttackL:
      input->mAttackRequestL = true;
      input->mTarget = mpObject->GetIntel()->PickAttackTarget(COMBO_L);
      if (input->mTarget == ID_NONE)
      {
        cIntelPlayer *intel = (cIntelPlayer *)mpObject->GetIntel();
        if (intel->GetControllerMagnitude() > 0.5f)
        {
          input->mTargetHeading = intel->GetControllerHeading(0.0f);
        }
        else
        {
          input->mTargetHeading = mpObject->GetHeading();
        }
      }

      if (AS_IN_AIR == animState || AS_RUN_IN_AIR == animState)
      {
        input->mbFloating = true;
      }
      else if (AS_PICKUP_PROP == animState ||
               AS_STAND_CARRY_PROP == animState ||
               AS_RUN_CARRY_PROP == animState ||
               AS_PUTDOWN_PROP == animState)
      {
        static_cast<cIntelPlayer*>(mpObject->GetIntel())->StartRangedAttack();
      }
      break;

    case ButtonAttackS:
      input->mAttackRequestS = true;
      input->mTarget = mpObject->GetIntel()->PickAttackTarget(COMBO_H);
      if (input->mTarget == ID_NONE)
      {
        cIntelPlayer *intel = (cIntelPlayer *)mpObject->GetIntel();
        if (intel->GetControllerMagnitude() > 0.5f)
        {
          input->mTargetHeading = intel->GetControllerHeading(0.0f);
        }
        else
        {
          input->mTargetHeading = mpObject->GetHeading();
        }
      }

      if (AS_IN_AIR == animState || AS_RUN_IN_AIR == animState)
      {
        input->mbFloating = true;
      }
      else if (AS_PICKUP_PROP == animState ||
               AS_STAND_CARRY_PROP == animState ||
               AS_RUN_CARRY_PROP == animState ||
               AS_PUTDOWN_PROP == animState)
      {
        static_cast<cIntelPlayer*>(mpObject->GetIntel())->StartRangedAttack();
      }
      break;

    case ButtonAttackRanged:
      // disable ranged attacks by design request - BP
//      static_cast<cIntelPlayer*>(mpObject->GetIntel())->StartRangedAttack();
      break;

    case ButtonJump:
      input->mJumpRequest = true;
      input->mbFloating = true;
      break;     
    case ButtonAction:
      input->mActionTarget  = mpObject->GetIntel()->PickActionTarget();
      input->mActionRequest = true;
      static_cast<cIntelPlayer*>(mpObject->GetIntel())->CancelRangedAttack();
      break;     
    case ButtonBlock:
      input->mBlockRequest = true;
      break;

    case ButtonPause:
//      if (mpObject->GetTitan()->IsPaused())
//      {
//        mpObject->GetTitan()->UnpauseGame();
//      }
//      else
//      {
//        mpObject->GetTitan()->PauseGame();
//      }
      break;

    case ButtonCameraCenter:
      if (mpObject->GetTitan()->GetNumLocalPlayers() == 1)
      {
        mpObject->GetTitan()->GetCameraController()->RequestHeading(mpObject->GetHeading());
      }
      break;

    case ButtonDPadUp:
#ifdef _WIN32
      mSpellListIndex = GetPrevAbilityIndex(mSpellListIndex);
      mSpellListTimer = 5.0f;
#endif
      break;

    case ButtonDPadDown:
#ifdef _WIN32
      mSpellListIndex = GetNextAbilityIndex(mSpellListIndex);
      mSpellListTimer = 5.0f;
#endif
      break;

    case ButtonDPadRight:
#ifdef _WIN32
      {
        int numAbilities = mpObject->GetTitan()->AbilityCurrentGetCount(mPlayerIndex);

        if (mSpellListIndex >= 0 && mSpellListIndex < numAbilities)
        {
          TitanButton tb = ButtonMax;
          if (controller->GetButtonState(ButtonAction))
          {
            tb = ButtonAction;
          }
          else if (controller->GetButtonState(ButtonAttackL))
          {
            tb = ButtonAttackL;
          }
          else if (controller->GetButtonState(ButtonAttackS))
          {
            tb = ButtonAttackS;
          }
          else if (controller->GetButtonState(ButtonJump))
          {
            tb = ButtonJump;
          }

          if (ButtonMax != tb)
          {
            tGameID id;
            int rank;

            if (mpObject->GetTitan()->AbilityCurrentGetIDRank(mPlayerIndex, mSpellListIndex, &id, &rank))
            {
              const cAbilityMaster* pAbility = mpObject->GetTitan()->GetDatabaseSys()->GetAbilityMaster(id, rank);
              MapAbility(tb, pAbility);
            }
          }
        }
      }
#endif
      break;
    case ButtonDodge:
      input->mDodging = true;
      if (static_cast<cIntelPlayer*>(mpObject->GetIntel())->GetControllerMagnitude() > 0.1f)
      {
        input->mDodgeDirection = static_cast<cIntelPlayer*>(mpObject->GetIntel())->GetControllerHeading(0.0f);
      }
      else
      {
        input->mDodgeDirection = mpObject->GetHeading();
      }
      break;
    default:
      break;
  }
}

void TitanInputHandler::OnButtonUp( TitanButton tb )
{
  cGraphicCharacter *graphic = prop_cast<cGraphicCharacter *>(mpObject->GetGraphic());
  SyAssert(graphic);
  cAnimCharControllerInput *input = graphic->GetAnimInput();
  SyAssert(input);

  TitanControllerI* controller = mpTitanUI->GetController(mPlayerIndex);

  SyDebug( "TitanInputHandler::OnButtonUp %i\n", tb );

  if (!controller->GetButtonState(ButtonBlock) &&
      !controller->GetButtonState(ButtonMagic) &&
      !controller->GetButtonState(ButtonDodge) &&
      !controller->GetButtonState(ButtonAttackRanged) 
      && !s_resetHack) 
  {
    s_resetHack = true;
  }

  if (!mpObject->GetTitan()->AllowCutSceneControllerMotion())
  {
    return;
  }

  if (ButtonMagic != tb && tb == mHeldSpellButton)
  {
    SyAssert(mHeldSpellButton != ButtonMax);
    static_cast<cIntelPlayer*>(mpObject->GetIntel())->EndSpellCast();
    mHeldSpellButton = ButtonMax;
    return;
  }

  eAnimState animState = graphic->GetAnimController()->GetAnimState();

  switch (tb)
  {
    case ButtonBlock:
      {
        float heading = input->mHeadingRequest;
        input->Clear();
        input->mHeadingRequest = heading;
      }
      break;
    case ButtonAction:
      if (AS_PUSH_PROP == animState)
      {
        input->mActionRequest = true;
      }
      break;
    case ButtonJump:
      if (AS_ATTACK_JUMP != animState)
      {
        input->mbFloating = false;
      }
      break;
    case ButtonAttackRanged:
      // disable ranged attacks by design request - BP
//      static_cast<cIntelPlayer*>(mpObject->GetIntel())->EndRangedAttack();
      break;
    case ButtonAttackL:
    case ButtonAttackS:
      if (AS_ATTACK_JUMP == animState)
      {
        input->mbFloating = false;
      }
      else
      {
        static_cast<cIntelPlayer*>(mpObject->GetIntel())->EndRangedAttack();
      }
      break;
    default:
      break;
  }
}

void TitanInputHandler::OnPauseGame( )
{
  cGraphicCharacter *graphic = prop_cast<cGraphicCharacter *>(mpObject->GetGraphic());
  SyAssert(graphic);
  cAnimCharControllerInput *input = graphic->GetAnimInput();
  SyAssert(input);

  static_cast<cIntelPlayer*>(mpObject->GetIntel())->EndRangedAttack();

  float heading = input->mHeadingRequest;
  input->Clear();
  input->mHeadingRequest = heading;
}

void TitanInputHandler::OnUnPauseGame( )
{
  cGraphicCharacter *graphic = prop_cast<cGraphicCharacter *>(mpObject->GetGraphic());
  SyAssert(graphic);
  cAnimCharControllerInput *input = graphic->GetAnimInput();
  SyAssert(input);

  static_cast<cIntelPlayer*>(mpObject->GetIntel())->EndRangedAttack();

  float heading = input->mHeadingRequest;
  input->Clear();
  input->mHeadingRequest = heading;

  TitanControllerI* controller = mpTitanUI->GetController(mPlayerIndex);
  
  input->mBlockRequest = controller->GetButtonState(ButtonBlock);

  if (AS_PUSH_PROP == graphic->GetAnimController()->GetAnimState())
  {
    input->mActionRequest = !controller->GetButtonState(ButtonAction);
  }
}

void TitanInputHandler::MapAbility( TitanButton tb, const cAbilityMaster* pAbility )
{
  SyAssert(tb != ButtonMax && mpObject != NULL);

  static_cast<cIntelPlayer*>(mpObject->GetIntel())->MapAbility(tb, pAbility);
}

const cAbilityMaster* TitanInputHandler::GetMappedAbility(TitanButton tb)
{
  SyAssert(tb != ButtonMax && mpObject != NULL);

  return static_cast<cIntelPlayer*>(mpObject->GetIntel())->GetMappedAbility(tb);
}

void TitanInputHandler::Update(float time)
{
  if (mSpellListTimer > 0.0f && mSpellListIndex >= 0)
  {
    SyString name;
    mpObject->GetTitan()->AbilityCurrentGetName(mPlayerIndex, mSpellListIndex, name);

    mSpellListTimer -= time;
    DEBUGOVERLAY_ENABLECHANNEL("TestUI", true);
    DEBUGOVERLAY_DRAWSCREENTEXT(0, "TestUI", 100, 425, ("%s (Map with DPadRight+button)", name.AsChar()), cDebugOverlay::CYAN);

    int prevIndex = GetPrevAbilityIndex(mSpellListIndex);
    int nextIndex = GetNextAbilityIndex(mSpellListIndex);

    if (prevIndex >= 0)
    {
      mpObject->GetTitan()->AbilityCurrentGetName(mPlayerIndex, prevIndex, name);
      DEBUGOVERLAY_DRAWSCREENTEXT(0, "TestUI", 100, 400, ("%s", name.AsChar()), SyColor32F(0.0f, 0.7f, 0.7f, 0.7f));
    }

    if (nextIndex >= 0)
    {
      mpObject->GetTitan()->AbilityCurrentGetName(mPlayerIndex, nextIndex, name);
      DEBUGOVERLAY_DRAWSCREENTEXT(0, "TestUI", 100, 450, ("%s", name.AsChar()), SyColor32F(0.0f, 0.7f, 0.7f, 0.7f));
    }
  }
}

int TitanInputHandler::GetPrevAbilityIndex(int index)
{
  int numAbilities = mpObject->GetTitan()->AbilityCurrentGetCount(mPlayerIndex);

  if (numAbilities > 0)
  {    
    index = (index <= 0 ? numAbilities : index) - 1;
    
    bool bLooped = false;
    while (!mpObject->GetTitan()->AbilityCurrentIsActive(mPlayerIndex, index))
    {
      if (index <= 0)
      {
        if (bLooped)
        {
          return -1;
        }
        else
        {
          bLooped = true;
        }
        
        index = numAbilities-1;
      }
      else
      {
        --index;
      }
    }
  }
  else
  {
    index = -1;
  }

  return index;
}

int TitanInputHandler::GetNextAbilityIndex(int index)
{
  int numAbilities = mpObject->GetTitan()->AbilityCurrentGetCount(mPlayerIndex);

  if (numAbilities > 0)
  {    
    index = (index + 1) % numAbilities;

    bool bLooped = false;

    while (!mpObject->GetTitan()->AbilityCurrentIsActive(mPlayerIndex, index))
    {
      if (index + 1 >= numAbilities)
      {
        if (bLooped)
        {
          return -1;
        }
        else
        {
          bLooped = true;
        }
      }
      index = (index + 1) % numAbilities;
    }
  }
  else
  {
    index = -1;
  }

  return index;
}

void TitanInputHandler::ToggleAbilityUnlock()
{
  mSpellListIndex = -1;
  mSpellListTimer = 0.0f;
}

void TitanInputHandler::Reset()
{
  mpObject = NULL;
  mPlayerIndex = -1;
  mHeldSpellButton = ButtonMax;
  mSpellListTimer = 0.0f;
  mSpellListIndex = -1;
}

void TitanInputHandler::SetGameObject(cGameObject *newobj, int playerIndex)
{
  Reset();

  mpObject = newobj;
  mPlayerIndex = playerIndex;
}
