// Copyright (c) 2016-2021 LG Electronics, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

#ifndef UTIL_TIMER_H_
#define UTIL_TIMER_H_

#include <memory>
#include <mutex>
#include <unordered_set>

typedef struct _GTimer GTimer;
typedef unsigned int TimerID;

class Timer {
 public:
  Timer(bool is_repeating)
      : source_id_(0), is_running_(false), is_repeating_(is_repeating) {}
  virtual ~Timer() {}

  // Timer
  virtual void HandleCallback() = 0;
  virtual void Start(int delay_in_milli_seconds);

  bool IsRunning() { return is_running_; }
  bool IsRepeating() { return is_repeating_; }
  void Stop();

 protected:
  void Running(bool is_running) { is_running_ = is_running; }

 private:
  TimerID source_id_;
  bool is_running_;
  bool is_repeating_;
};

class TimerReceiver {
 public:
  virtual ~TimerReceiver();
  void AddTimer(Timer*);
  void RemoveTimer(Timer*);
  void StopAll();

 private:
  std::mutex timers_mutex_;
  std::unordered_set<Timer*> timers_;
};

template <class Receiver, bool kIsRepeating>
class BaseTimer : public Timer {
 public:
  typedef void (Receiver::*ReceiverMethod)();

  BaseTimer() : Timer(kIsRepeating), receiver_(nullptr), method_(nullptr) {}

  ~BaseTimer() override {
    if (IsRunning())
      Stop();
    receiver_->RemoveTimer(this);
  }

  void HandleCallback() override {
    Running(kIsRepeating);
    (receiver_->*method_)();
  }

  void Start(int delay_in_milli_seconds,
             Receiver* receiver,
             ReceiverMethod method) {
    receiver_ = receiver;
    method_ = method;
    receiver_->AddTimer(this);
    Timer::Start(delay_in_milli_seconds);
  }

 private:
  Receiver* receiver_;
  ReceiverMethod method_;
};

template <class Receiver>
class OneShotTimer : public BaseTimer<Receiver, false> {};

template <class Receiver>
class RepeatingTimer : public BaseTimer<Receiver, true> {};

template <class Receiver, typename Data, bool kIsRepeating>
class BaseTimerWithData : public Timer {
 public:
  typedef void (Receiver::*ReceiverMethod)(Data*);

  BaseTimerWithData() : Timer(kIsRepeating) {}

  ~BaseTimerWithData() override {
    if (IsRunning())
      Stop();
    receiver_->RemoveTimer(this);
  }

  void HandleCallback() override {
    Running(kIsRepeating);
    (receiver_->*method_)(data_.get());
  }

  void Start(int delay_in_milli_seconds,
             Receiver* receiver,
             ReceiverMethod method,
             std::unique_ptr<Data> data,
             bool will_destroy = false) {
    receiver->AddTimer(this);
    receiver_ = receiver;
    method_ = method;
    data_ = std::move(data);
    Timer::Start(delay_in_milli_seconds);
  }

 private:
  Receiver* receiver_ = nullptr;
  ReceiverMethod method_ = nullptr;
  std::unique_ptr<Data> data_;
};

template <class Receiver, class Data>
class OneShotTimerWithData : public BaseTimerWithData<Receiver, Data, false> {};

class ElapsedTimer {
 public:
  ElapsedTimer();
  ~ElapsedTimer();

  bool IsRunning() const;
  void Start();
  void Stop();
  int ElapsedMs() const;
  int ElapsedUs() const;

 private:
  ElapsedTimer(const ElapsedTimer&) = delete;
  ElapsedTimer& operator=(const ElapsedTimer&) = delete;

  bool is_running_;
  GTimer* timer_;
};

#endif  // UTIL_TIMER_H_
