// 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 UI_COMPOSITOR_CALLBACK_LAYER_ANIMATION_OBSERVER_H_
#define UI_COMPOSITOR_CALLBACK_LAYER_ANIMATION_OBSERVER_H_

#include "base/callback.h"
#include "base/macros.h"
#include "ui/compositor/compositor_export.h"
#include "ui/compositor/layer_animation_observer.h"

namespace ui {

class LayerAnimationSequence;

// A LayerAnimationObserver that invokes a Callback when all observed
// LayerAnimationSequence's have started and finished.
//
// Example usage:
//  class Foobar {
//    // The Callback that will be invoked when all the animation sequences have
//    // started.
//    void AnimationStartedCallback(
//        const CallbackLayerAnimationObserver& observer) {
//      // Do stuff.
//    }
//
//    // The Callback that will be invoked when all the animation sequences have
//    // finished.
//    bool AnimationEndedCallback(
//        const CallbackLayerAnimationObserver& observer) {
//      // Do stuff.
//      return true;  // Returns true so that |observer| destroys itself.
//    }
//
//    // Example method that uses the CallbackLayerAnimationObserver.
//    void Animate() {
//      ui::LayerAnimator* animator_1 = layer_1->GetAnimator();
//      ui::LayerAnimator* animator_2 = layer_2->GetAnimator();
//      CallbackLayerAnimationObserver* observer =
//          new CallbackLayerAnimationObserver(
//              base::Bind(&Foobar::AnimationStartedCallback),
//              base::Unretained(this));
//              base::Bind(&Foobar::AnimationEndedCallback),
//              base::Unretained(this));
//      animator_1->AddObserver(observer);
//      animator_2->AddObserver(observer);
//
//      // Set up animation sequences on |animator_1| and |animator_2|.
//
//      // The callback won't be invoked until SetActive() is called.
//      observer->SetActive();
//    }
//  }
//
// TODO(bruthig): Unify the CallbackLayerAnimationObserver with the
// ImplicitAnimationObserver. (See www.crbug.com/542825).
class COMPOSITOR_EXPORT CallbackLayerAnimationObserver
    : public ui::LayerAnimationObserver {
 public:
  // The Callback type that will be invoked when all animation sequences have
  // been started.
  typedef base::Callback<void(const CallbackLayerAnimationObserver&)>
      AnimationStartedCallback;

  // The Callback type that will be invoked when all animation sequences have
  // finished. |this| will be destroyed after invoking the Callback if it
  // returns true.
  typedef base::Callback<bool(const CallbackLayerAnimationObserver&)>
      AnimationEndedCallback;

  // A dummy no-op AnimationStartedCallback.
  static void DummyAnimationStartedCallback(
      const CallbackLayerAnimationObserver&);

  // A dummy no-op AnimationEndedCallback that will return
  // |should_delete_observer|.
  //
  // Example usage:
  //
  //    ui::CallbackLayerAnimationObserver* animation_observer =
  //        new ui::CallbackLayerAnimationObserver(
  //            base::Bind(&ui::CallbackLayerAnimationObserver::
  //                DummyAnimationStartedCallback),
  //            base::Bind(&ui::CallbackLayerAnimationObserver::
  //                DummyAnimationEndedCallback, false));
  static bool DummyAnimationEndedCallback(
      bool should_delete_observer,
      const CallbackLayerAnimationObserver&);

  // Create an instance that will invoke the |animation_started_callback| when
  // the animation has started and the |animation_ended_callback| when the
  // animation has ended.
  CallbackLayerAnimationObserver(
      AnimationStartedCallback animation_started_callback,
      AnimationEndedCallback animation_ended_callback);

  // Create an instance that will invoke the |animation_started_callback| when
  // the animation has started and will delete |this| when the animations have
  // ended if |should_delete_observer| is true.
  CallbackLayerAnimationObserver(
      AnimationStartedCallback animation_started_callback,
      bool should_delete_observer);

  // Create an instance that will invoke the |animation_ended_callback| when the
  // animations have ended.
  explicit CallbackLayerAnimationObserver(
      AnimationEndedCallback animation_ended_callback);

  ~CallbackLayerAnimationObserver() override;

  bool active() const { return active_; }

  // The callbacks will not be invoked until SetActive() has been called. This
  // allows each sequence to be attached before checking if the sequences have
  // finished.
  void SetActive();

  int aborted_count() const { return aborted_count_; }
  int successful_count() const { return successful_count_; }

  // ui::LayerAnimationObserver:
  void OnLayerAnimationStarted(ui::LayerAnimationSequence* sequence) override;
  void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override;
  void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override;
  void OnLayerAnimationScheduled(ui::LayerAnimationSequence* sequence) override;

 protected:
  // ui::LayerAnimationObserver:
  bool RequiresNotificationWhenAnimatorDestroyed() const override;
  void OnAttachedToSequence(ui::LayerAnimationSequence* sequence) override;
  void OnDetachedFromSequence(ui::LayerAnimationSequence* sequence) override;

 private:
  int GetNumSequencesCompleted();

  // Checks if all attached sequences have been started and invokes
  // |animation_started_callback_| if |active_| is true.
  void CheckAllSequencesStarted();

  // Checks if all attached sequences have completed and invokes
  // |animation_ended_callback_| if |active_| is true.
  void CheckAllSequencesCompleted();

  // Allows the callbacks to be invoked when true.
  bool active_ = false;

  // The total number of animation sequences that have been attached.
  int attached_sequence_count_ = 0;

  // The total number of animation sequences that have been detached.
  int detached_sequence_count_ = 0;

  // The number of animation sequences that have been started.
  int started_count_ = 0;

  // The number of animation sequences that were aborted.
  int aborted_count_ = 0;

  // The number of animation sequences that completed successfully.
  int successful_count_ = 0;

  // The callback to invoke once all the animation sequences have been started.
  AnimationStartedCallback animation_started_callback_;

  // The callback to invoke once all the animation sequences have finished.
  AnimationEndedCallback animation_ended_callback_;

  // Set to true in the destructor (if non-NULL). Used to detect deletion while
  // calling out.
  bool* destroyed_ = nullptr;

  DISALLOW_COPY_AND_ASSIGN(CallbackLayerAnimationObserver);
};

}  // namespace ui

#endif  // UI_COMPOSITOR_CALLBACK_LAYER_ANIMATION_OBSERVER_H_
