/*
 *  Copyright (c) 2020, The OpenThread Authors.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *  3. Neither the name of the copyright holder nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef CSL_TX_SCHEDULER_HPP_
#define CSL_TX_SCHEDULER_HPP_

#include "openthread-core-config.h"

#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE

#include "common/locator.hpp"
#include "common/message.hpp"
#include "common/non_copyable.hpp"
#include "common/time.hpp"
#include "mac/mac.hpp"
#include "mac/mac_frame.hpp"
#include "thread/indirect_sender_frame_context.hpp"

namespace ot {

/**
 * @addtogroup core-mesh-forwarding
 *
 * @brief
 *   This module includes definitions for CSL transmission scheduling.
 *
 * @{
 */

class Child;

/**
 * Implements CSL tx scheduling functionality.
 *
 */
class CslTxScheduler : public InstanceLocator, private NonCopyable
{
    friend class Mac::Mac;
    friend class IndirectSender;

public:
    static constexpr uint8_t kMaxCslTriggeredTxAttempts = OPENTHREAD_CONFIG_MAC_MAX_TX_ATTEMPTS_INDIRECT_POLLS;

    /**
     * Defines all the child info required for scheduling CSL transmissions.
     *
     * `Child` class publicly inherits from this class.
     *
     */
    class ChildInfo
    {
    public:
        uint8_t GetCslTxAttempts(void) const { return mCslTxAttempts; }
        void    IncrementCslTxAttempts(void) { mCslTxAttempts++; }
        void    ResetCslTxAttempts(void) { mCslTxAttempts = 0; }

        bool IsCslSynchronized(void) const { return mCslSynchronized && mCslPeriod > 0; }
        void SetCslSynchronized(bool aCslSynchronized) { mCslSynchronized = aCslSynchronized; }

        uint8_t GetCslChannel(void) const { return mCslChannel; }
        void    SetCslChannel(uint8_t aChannel) { mCslChannel = aChannel; }

        uint32_t GetCslTimeout(void) const { return mCslTimeout; }
        void     SetCslTimeout(uint32_t aTimeout) { mCslTimeout = aTimeout; }

        uint16_t GetCslPeriod(void) const { return mCslPeriod; }
        void     SetCslPeriod(uint16_t aPeriod) { mCslPeriod = aPeriod; }

        uint16_t GetCslPhase(void) const { return mCslPhase; }
        void     SetCslPhase(uint16_t aPhase) { mCslPhase = aPhase; }

        TimeMilli GetCslLastHeard(void) const { return mCslLastHeard; }
        void      SetCslLastHeard(TimeMilli aCslLastHeard) { mCslLastHeard = aCslLastHeard; }

        uint64_t GetLastRxTimestamp(void) const { return mLastRxTimestamp; }
        void     SetLastRxTimestamp(uint64_t aLastRxTimestamp) { mLastRxTimestamp = aLastRxTimestamp; }

    private:
        uint8_t  mCslTxAttempts : 7;   ///< Number of CSL triggered tx attempts.
        bool     mCslSynchronized : 1; ///< Indicates whether or not the child is CSL synchronized.
        uint8_t  mCslChannel;          ///< The channel the device will listen on.
        uint32_t mCslTimeout;          ///< The sync timeout, in seconds.
        uint16_t mCslPeriod; ///< CSL sampled listening period between consecutive channel samples in units of 10
                             ///< symbols (160 microseconds).

        /**
         * The time in units of 10 symbols from the first symbol of the frame
         * containing the CSL IE was transmitted until the next channel sample,
         * see IEEE 802.15.4-2015, section 6.12.2.
         *
         * The Thread standard further defines the CSL phase (see Thread 1.4,
         * section 3.2.6.3.4, also conforming to IEEE 802.15.4-2020, section
         * 6.12.2.1):
         *  * The "first symbol" from the definition SHALL be interpreted as the
         *    first symbol of the MAC Header.
         *  * "until the next channel sample":
         *     * The CSL Receiver SHALL be ready to receive when the preamble
         *       time T_pa as specified below is reached.
         *     * The CSL Receiver SHOULD be ready to receive earlier than T_pa
         *       and SHOULD stay ready to receive until after the time specified
         *       in CSL Phase, according to the implementation and accuracy
         *       expectations.
         *     * The CSL Transmitter SHALL start transmitting the first symbol
         *       of the preamble of the frame to transmit at the preamble time
         *       T_pa = (CSL-Phase-Time – 192 us) (that is, CCA must be
         *       performed before time T_pa). Here, CSL-Phase-Time is the time
         *       duration specified by the CslPhase field value (in units of 10
         *       symbol periods).
         *     * This implies that the CSL Transmitter SHALL start transmitting
         *       the first symbol of the MAC Header at the time T_mh =
         *       CSL-Phase-Time.
         *
         * Derivation of the next TX timestamp based on this definition and the
         * RX timestamp of the packet containing the CSL IE:
         *
         * Note that RX and TX timestamps are defined to point to the end of the
         * synchronization header (SHR).
         *
         * lastTmh = lastRxTimestamp + phrDuration
         *
         * nextTmh = lastTmh + symbolPeriod * 10 * (n * cslPeriod + cslPhase)
         *         = lastTmh + 160us * (n * cslPeriod + cslPhase)
         *
         * nextTxTimestamp
         *         = nextTmh - phrDuration
         *         = lastRxTimestamp + 160us * (n * cslPeriod + cslPhase)
         */
        uint16_t  mCslPhase;
        TimeMilli mCslLastHeard; ///< Radio clock time when last frame containing CSL IE was heard.
        uint64_t
            mLastRxTimestamp; ///< Radio clock time when last frame containing CSL IE was received, in microseconds.

        static_assert(kMaxCslTriggeredTxAttempts < (1 << 7), "mCslTxAttempts cannot fit max!");
    };

    /**
     * Defines the callbacks used by the `CslTxScheduler`.
     *
     */
    class Callbacks : public InstanceLocator
    {
        friend class CslTxScheduler;

    private:
        typedef IndirectSenderBase::FrameContext FrameContext;

        /**
         * Initializes the callbacks object.
         *
         * @param[in]  aInstance   A reference to the OpenThread instance.
         *
         */
        explicit Callbacks(Instance &aInstance);

        /**
         * This callback method requests a frame to be prepared for CSL transmission to a given SSED.
         *
         * @param[out] aFrame    A reference to a MAC frame where the new frame would be placed.
         * @param[out] aContext  A reference to a `FrameContext` where the context for the new frame would be placed.
         * @param[in]  aChild    The child for which to prepare the frame.
         *
         * @retval kErrorNone   Frame was prepared successfully.
         * @retval kErrorAbort  CSL transmission should be aborted (no frame for the child).
         *
         */
        Error PrepareFrameForChild(Mac::TxFrame &aFrame, FrameContext &aContext, Child &aChild);

        /**
         * This callback method notifies the end of CSL frame transmission to a child.
         *
         * @param[in]  aFrame     The transmitted frame.
         * @param[in]  aContext   The context associated with the frame when it was prepared.
         * @param[in]  aError     kErrorNone when the frame was transmitted successfully,
         *                        kErrorNoAck when the frame was transmitted but no ACK was received,
         *                        kErrorChannelAccessFailure tx failed due to activity on the channel,
         *                        kErrorAbort when transmission was aborted for other reasons.
         * @param[in]  aChild     The child to which the frame was transmitted.
         *
         */
        void HandleSentFrameToChild(const Mac::TxFrame &aFrame,
                                    const FrameContext &aContext,
                                    Error               aError,
                                    Child              &aChild);
    };
    /**
     * Initializes the CSL tx scheduler object.
     *
     * @param[in]  aInstance   A reference to the OpenThread instance.
     *
     */
    explicit CslTxScheduler(Instance &aInstance);

    /**
     * Updates the next CSL transmission (finds the nearest child).
     *
     * It would then request the `Mac` to do the CSL tx. If the last CSL tx has been fired at `Mac` but hasn't been
     * done yet, and it's aborted, this method would set `mCslTxChild` to `nullptr` to notify the `HandleTransmitDone`
     * that the operation has been aborted.
     *
     */
    void Update(void);

    /**
     * Clears all the states inside `CslTxScheduler` and the related states in each child.
     *
     */
    void Clear(void);

private:
    // Guard time in usec to add when checking delay while preparing the CSL frame for tx.
    static constexpr uint32_t kFramePreparationGuardInterval = 1500;

    void InitFrameRequestAhead(void);
    void RescheduleCslTx(void);

    uint32_t GetNextCslTransmissionDelay(const Child &aChild, uint32_t &aDelayFromLastRx, uint32_t aAheadUs) const;

    // Callbacks from `Mac`
    Mac::TxFrame *HandleFrameRequest(Mac::TxFrames &aTxFrames);
    void          HandleSentFrame(const Mac::TxFrame &aFrame, Error aError);

    void HandleSentFrame(const Mac::TxFrame &aFrame, Error aError, Child &aChild);

    uint32_t                mCslFrameRequestAheadUs;
    Child                  *mCslTxChild;
    Message                *mCslTxMessage;
    Callbacks::FrameContext mFrameContext;
    Callbacks               mCallbacks;
};

/**
 * @}
 *
 */

} // namespace ot

#endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE

#endif // CSL_TX_SCHEDULER_HPP_
