/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "AnimationInfo.h"
#include "mozilla/layers/WebRenderLayerManager.h"
#include "mozilla/layers/AnimationHelper.h"

namespace mozilla {
namespace layers {

AnimationInfo::AnimationInfo(LayerManager* aManager) :
  mManager(aManager),
  mCompositorAnimationsId(0),
  mAnimationGeneration(0),
  mMutated(false)
{
}

AnimationInfo::~AnimationInfo()
{
}

void
AnimationInfo::EnsureAnimationsId()
{
  if (!mCompositorAnimationsId) {
    mCompositorAnimationsId = AnimationHelper::GetNextCompositorAnimationsId();
  }
}

Animation*
AnimationInfo::AddAnimation()
{
  // Here generates a new id when the first animation is added and
  // this id is used to represent the animations in this layer.
  EnsureAnimationsId();

  MOZ_ASSERT(!mPendingAnimations, "should have called ClearAnimations first");

  Animation* anim = mAnimations.AppendElement();

  mMutated = true;

  return anim;
}

Animation*
AnimationInfo::AddAnimationForNextTransaction()
{
  MOZ_ASSERT(mPendingAnimations,
             "should have called ClearAnimationsForNextTransaction first");

  Animation* anim = mPendingAnimations->AppendElement();

  return anim;
}

void
AnimationInfo::ClearAnimations()
{
  mPendingAnimations = nullptr;

  if (mAnimations.IsEmpty() && mAnimationData.IsEmpty()) {
    return;
  }

  mAnimations.Clear();
  mAnimationData.Clear();

  mMutated = true;
}

void
AnimationInfo::ClearAnimationsForNextTransaction()
{
  // Ensure we have a non-null mPendingAnimations to mark a future clear.
  if (!mPendingAnimations) {
    mPendingAnimations = new AnimationArray;
  }

  mPendingAnimations->Clear();
}

void
AnimationInfo::SetCompositorAnimations(const CompositorAnimations& aCompositorAnimations)
{
  mAnimations = aCompositorAnimations.animations();
  mCompositorAnimationsId = aCompositorAnimations.id();
  mAnimationData.Clear();
  AnimationHelper::SetAnimations(mAnimations,
                                 mAnimationData,
                                 mBaseAnimationStyle);
}

bool
AnimationInfo::StartPendingAnimations(const TimeStamp& aReadyTime)
{
  bool updated = false;
  for (size_t animIdx = 0, animEnd = mAnimations.Length();
       animIdx < animEnd; animIdx++) {
    Animation& anim = mAnimations[animIdx];

    // If the animation is play-pending, resolve the start time.
    // This mirrors the calculation in Animation::StartTimeFromReadyTime.
    if (anim.startTime().type() == MaybeTimeDuration::Tnull_t &&
        !anim.originTime().IsNull() &&
        !anim.isNotPlaying()) {
      TimeDuration readyTime = aReadyTime - anim.originTime();
      anim.startTime() =
        anim.playbackRate() == 0
        ? readyTime
        : readyTime - anim.holdTime().MultDouble(1.0 /
                                                 anim.playbackRate());
      updated = true;
    }
  }
  return updated;
}

void
AnimationInfo::TransferMutatedFlagToLayer(Layer* aLayer)
{
  if (mMutated) {
    aLayer->Mutated();
    mMutated = false;
  }
}

bool
AnimationInfo::ApplyPendingUpdatesForThisTransaction()
{
  if (mPendingAnimations) {
    mPendingAnimations->SwapElements(mAnimations);
    mPendingAnimations = nullptr;
    return true;
  }

  return false;
}

bool
AnimationInfo::HasOpacityAnimation() const
{
  for (uint32_t i = 0; i < mAnimations.Length(); i++) {
    if (mAnimations[i].property() == eCSSProperty_opacity) {
      return true;
    }
  }
  return false;
}

bool
AnimationInfo::HasTransformAnimation() const
{
  for (uint32_t i = 0; i < mAnimations.Length(); i++) {
    if (mAnimations[i].property() == eCSSProperty_transform) {
      return true;
    }
  }
  return false;
}

} // namespace layers
} // namespace mozilla
