/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */
#pragma once

#include "ExecutorService.h"

namespace pulsar {

/**
 * A task that is executed periodically.
 *
 * After the `start()` method is called, it will trigger `callback_` method periodically whose interval is
 * `periodMs` in the constructor. After the `stop()` method is called, the timer will be cancelled and
 * `callback()` will never be called again unless `start()` was called again.
 *
 * If you don't want to execute the task infinitely, you can call `stop()` in the implementation of
 * `callback()` method.
 *
 * NOTE: If the `periodMs` is negative, the `callback()` will never be called.
 */
class PeriodicTask : public std::enable_shared_from_this<PeriodicTask> {
   public:
    using ErrorCode = ASIO_ERROR;
    using CallbackType = std::function<void(const ErrorCode&)>;

    enum State : std::uint8_t
    {
        Pending,
        Ready,
        Closing
    };

    PeriodicTask(ExecutorService& executor, int periodMs)
        : timer_(executor.createDeadlineTimer()), periodMs_(periodMs) {}

    void start();

    void stop() noexcept;

    void setCallback(CallbackType&& callback) noexcept { callback_ = std::move(callback); }

    State getState() const noexcept { return state_; }
    int getPeriodMs() const noexcept { return periodMs_; }

   private:
    std::atomic<State> state_{Pending};
    DeadlineTimerPtr timer_;
    const int periodMs_;
    CallbackType callback_{trivialCallback};

    void handleTimeout(const ErrorCode& ec);

    static void trivialCallback(const ErrorCode&) {}
};

}  // namespace pulsar
