// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CC_ANIMATION_ANIMATION_H_
#define CC_ANIMATION_ANIMATION_H_

#include <vector>

#include <memory>
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/time/time.h"
#include "cc/animation/animation_curve.h"
#include "cc/animation/animation_export.h"
#include "cc/animation/element_animations.h"
#include "cc/animation/keyframe_model.h"
#include "cc/trees/element_id.h"

namespace cc {

class AnimationDelegate;
class AnimationEvents;
class AnimationHost;
class AnimationTimeline;
class KeyframeEffect;
struct AnimationEvent;

// An Animation is responsible for managing animating properties for a set of
// targets. Each target is represented by a KeyframeEffect and can be animating
// multiple properties on that target; see the KeyframeEffect class.
//
// A particular Animation may not own all the KeyframeEffects for a given
// target. Animation is only a grouping mechanism for related effects, and the
// grouping relationship is defined by the client. It is also the client's
// responsibility to deal with any conflicts that arise from animating the same
// property of the same target across multiple Animations.
//
// Each Animation has a copy on the impl thread, and will take care of
// synchronizing to/from the impl thread when requested.
class CC_ANIMATION_EXPORT Animation : public base::RefCounted<Animation> {
 public:
  static scoped_refptr<Animation> Create(int id);
  virtual scoped_refptr<Animation> CreateImplInstance() const;

  int id() const { return id_; }
  typedef size_t KeyframeEffectId;
  ElementId element_id_of_keyframe_effect(
      KeyframeEffectId keyframe_effect_id) const;
  bool IsElementAttached(ElementId id) const;

  // Parent AnimationHost. Animation can be detached from AnimationTimeline.
  AnimationHost* animation_host() { return animation_host_; }
  const AnimationHost* animation_host() const { return animation_host_; }
  void SetAnimationHost(AnimationHost* animation_host);
  bool has_animation_host() const { return !!animation_host_; }

  // Parent AnimationTimeline.
  AnimationTimeline* animation_timeline() { return animation_timeline_; }
  const AnimationTimeline* animation_timeline() const {
    return animation_timeline_;
  }
  virtual void SetAnimationTimeline(AnimationTimeline* timeline);

  // TODO(smcgruer): If/once ScrollTimeline is supported on normal Animations,
  // we will need to move the promotion logic from WorkletAnimation to here.
  virtual void PromoteScrollTimelinePendingToActive() {}

  bool has_element_animations() const;
  scoped_refptr<ElementAnimations> element_animations(
      KeyframeEffectId keyframe_effect_id) const;

  void set_animation_delegate(AnimationDelegate* delegate) {
    animation_delegate_ = delegate;
  }

  void AttachElementForKeyframeEffect(ElementId element_id,
                                      KeyframeEffectId keyframe_effect_id);
  void DetachElementForKeyframeEffect(ElementId element_id,
                                      KeyframeEffectId keyframe_effect_id);
  virtual void DetachElement();

  void AddKeyframeModelForKeyframeEffect(
      std::unique_ptr<KeyframeModel> keyframe_model,
      KeyframeEffectId keyframe_effect_id);
  void PauseKeyframeModelForKeyframeEffect(int keyframe_model_id,
                                           double time_offset,
                                           KeyframeEffectId keyframe_effect_id);
  void RemoveKeyframeModelForKeyframeEffect(
      int keyframe_model_id,
      KeyframeEffectId keyframe_effect_id);
  void AbortKeyframeModelForKeyframeEffect(int keyframe_model_id,
                                           KeyframeEffectId keyframe_effect_id);
  void AbortKeyframeModelsWithProperty(TargetProperty::Type target_property,
                                       bool needs_completion);

  virtual void PushPropertiesTo(Animation* animation_impl);

  void UpdateState(bool start_ready_keyframe_models, AnimationEvents* events);
  virtual void Tick(base::TimeTicks monotonic_time);

  void AddToTicking();
  void KeyframeModelRemovedFromTicking();

  // AnimationDelegate routing.
  void NotifyKeyframeModelStarted(const AnimationEvent& event);
  void NotifyKeyframeModelFinished(const AnimationEvent& event);
  void NotifyKeyframeModelAborted(const AnimationEvent& event);
  void NotifyKeyframeModelTakeover(const AnimationEvent& event);
  size_t TickingKeyframeModelsCount() const;

  void SetNeedsPushProperties();

  // Make KeyframeModels affect active elements if and only if they affect
  // pending elements. Any KeyframeModels that no longer affect any elements
  // are deleted.
  void ActivateKeyframeEffects();

  // Returns the keyframe model animating the given property that is either
  // running, or is next to run, if such a keyframe model exists.
  KeyframeModel* GetKeyframeModelForKeyframeEffect(
      TargetProperty::Type target_property,
      KeyframeEffectId keyframe_effect_id) const;

  std::string ToString() const;

  void SetNeedsCommit();

  virtual bool IsWorkletAnimation() const;
  void AddKeyframeEffect(std::unique_ptr<KeyframeEffect>);

  KeyframeEffect* GetKeyframeEffectById(
      KeyframeEffectId keyframe_effect_id) const;
  KeyframeEffectId NextKeyframeEffectId() { return keyframe_effects_.size(); }

 private:
  friend class base::RefCounted<Animation>;

  void RegisterKeyframeEffect(ElementId element_id,
                              KeyframeEffectId keyframe_effect_id);
  void UnregisterKeyframeEffect(ElementId element_id,
                                KeyframeEffectId keyframe_effect_id);
  void RegisterKeyframeEffects();
  void UnregisterKeyframeEffects();

  void PushAttachedKeyframeEffectsToImplThread(Animation* animation_impl) const;
  void PushPropertiesToImplThread(Animation* animation_impl);

 protected:
  explicit Animation(int id);
  virtual ~Animation();

  AnimationHost* animation_host_;
  AnimationTimeline* animation_timeline_;
  AnimationDelegate* animation_delegate_;

  int id_;

  using ElementToKeyframeEffectIdMap =
      std::unordered_map<ElementId,
                         std::unordered_set<KeyframeEffectId>,
                         ElementIdHash>;
  using KeyframeEffects = std::vector<std::unique_ptr<KeyframeEffect>>;

  // It is possible for a keyframe_effect to be in keyframe_effects_ but not in
  // element_to_keyframe_effect_id_map_ but the reverse is not possible.
  ElementToKeyframeEffectIdMap element_to_keyframe_effect_id_map_;
  KeyframeEffects keyframe_effects_;

  int ticking_keyframe_effects_count;

  DISALLOW_COPY_AND_ASSIGN(Animation);
};

}  // namespace cc

#endif  // CC_ANIMATION_ANIMATION_H_
