#include "BounceOscillator.h"
#include "CasualCore.h"

BounceOscillator::BounceOscillator()
  : m_Object(0)
  , m_Settings()
  , m_OriginalScale(0.0f)
  , m_BaseScale(0.0f)
  , m_InitialDampScale(0.0f)
  , m_DampingDelay(0.0f)
  , m_ElapsedTime(0.0f)
  , m_IsFinished(false)
{
}

BounceOscillator::BounceOscillator(CasualCore::Object* a_Object, const BounceOscillatorSettings& a_Settings)
  : m_Object(a_Object)
  , m_Settings(a_Settings)
  , m_OriginalScale(a_Object->GetScale())
  , m_BaseScale(a_Object->GetScale())
  , m_InitialDampScale(0.0f)
  , m_ElapsedTime(0.0f)
  , m_IsFinished(false)
{
  m_DampingDelay = GetDampingDelay();
}

void BounceOscillator::Update(float a_DeltaTime)
{
  m_ElapsedTime += a_DeltaTime;
  if (!m_Object)
    m_IsFinished = true;
  if (m_IsFinished)
    return;

  if (m_ElapsedTime <= m_Settings.duration)
  {
    m_Object->SetScale(Oscillate());
  }
  else if (m_ElapsedTime > m_Settings.duration && m_ElapsedTime <= (m_Settings.duration + m_DampingDelay))
  {
    m_Object->SetScale(Damp(m_ElapsedTime - m_Settings.duration));
  }
  else
  {
    m_Object->SetScale(m_OriginalScale);
    m_IsFinished = true;
  }
}

void BounceOscillator::Impulse()
{
  m_BaseScale = Cap(m_Object->GetScale());
  m_DampingDelay = GetDampingDelay();
  m_ElapsedTime = 0.0f;
  m_InitialDampScale = 0.0f;
  m_IsFinished = false;
}

float BounceOscillator::Oscillate()
{
  float normalizedTime = m_ElapsedTime / m_Settings.duration;
  return m_BaseScale + m_Settings.amplitude * sinf(RKTWO_PI * m_Settings.frequency * normalizedTime) * (1.0f - normalizedTime);
}

float BounceOscillator::Damp(float currentDampTime)
{
  if (!m_InitialDampScale)
  {
    m_InitialDampScale = m_Object->GetScale();
  }
  return BackEaseInOutFunction(currentDampTime, m_DampingDelay, m_InitialDampScale, m_OriginalScale - m_InitialDampScale);
  return 0;
}

float BounceOscillator::Cap(float a_Value)
{
  return std::max(m_Settings.minRelativeScale * m_OriginalScale, std::min(m_Settings.maxRelativeScale * m_OriginalScale, a_Value));
}

float BounceOscillator::GetDampingDelay()
{
  float relativeScale = m_BaseScale / m_OriginalScale;
  float normalizedRelativeScale = (relativeScale - m_Settings.minRelativeScale) / (m_Settings.maxRelativeScale - m_Settings.minRelativeScale);
  return m_Settings.minDampingDelay + normalizedRelativeScale * (m_Settings.maxDampingDelay - m_Settings.minDampingDelay);
}

float BounceOscillator::BackEaseInOutFunction(float a_t, float a_d, float a_b, float a_c)
{
  float timeDelta = a_t / (a_d * 0.5f);
  float backExaggeration = 6.f;

  a_c /= 2.f;

  if (timeDelta < 1.f)
  {
    return a_c * (timeDelta * timeDelta * ((backExaggeration + 1.f) * timeDelta - backExaggeration)) + a_b;
  }
  else
  {
    timeDelta -= 2.f;
    return a_c * (timeDelta * timeDelta * ((backExaggeration + 1.f) * timeDelta + backExaggeration) + 2.f) + a_b;
  }
}
