/*
 * Copyright (c) 2015 ARM Limited. All rights reserved.
 * SPDX-License-Identifier: Apache-2.0
 * 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.
 */
#ifndef M2MREPORTHANDLER_H
#define M2MREPORTHANDLER_H

// Support for std args
#include <stdint.h>
#include "mbed-client/m2mconfig.h"
#include "mbed-client/m2mbase.h"
#include "mbed-client/m2mtimerobserver.h"
#include "mbed-client/m2mresourceinstance.h"
#include "mbed-client/m2mvector.h"
#include "mbed-client/m2mtimer.h"

//FORWARD DECLARATION
class M2MReportObserver;
class M2MTimer;
class M2MResourceInstance;

/**
 *  @brief M2MReportHandler.
 *  This class is handles all the observation related operations.
 */
class M2MReportHandler: public M2MTimerObserver
{
private:
    // Prevents the use of assignment operator by accident.
    M2MReportHandler& operator=( const M2MReportHandler& /*other*/ );

public:

    M2MReportHandler(M2MReportObserver &observer);

public:

    /**
     * Enum defining which write attributes are set.
    */
    enum {
        Cancel = 1,
        Pmin = 2,
        Pmax = 4,
        Lt = 8,
        Gt = 16,
        St = 32
    };

    /**
     * Destructor
     */
    virtual ~M2MReportHandler();

    /**
     * @brief Sets that object is under observation.
     * @param Value for the observation.
     * @param handler, Handler object for sending
     * observation callbacks.
     */
    void set_under_observation(bool observed);

    /**
     * @brief Sets the value of the given resource.
     * @param value, Value of the observed resource.
     */
    void set_value(float value);

    /**
     * @brief Sets notification trigger.
     * @param obj_instance_id, Object instance id that has changed
     */
    void set_notification_trigger(uint16_t obj_instance_id = 0);

    /**
     * @brief Parses the received query for notification
     * attribute.
     * @param query Query to be parsed for attributes.
     * @param type Type of the Base Object.
     * @param resource_type Type of the Resource.
     * @return true if required attributes are present else false.
     */
    bool parse_notification_attribute(const char *query,
                                              M2MBase::BaseType type,
                                              M2MResourceInstance::ResourceType resource_type = M2MResourceInstance::OPAQUE);

    /**
    * @brief Set back to default values.
    */
    void set_default_values();

    /**
     * @brief Return write attribute flags.
     */
    uint8_t attribute_flags() const;

    /**
     * \brief Sets the observation token value.
     * \param token A pointer to the token of the resource.
     * \param length The length of the token pointer.
     */
    void set_observation_token(const uint8_t *token, const uint8_t length);

    /**
     * \brief Provides a copy of the observation token of the object.
     * \param value[OUT] A pointer to the value of the token.
     * \param value_length[OUT] The length of the token pointer.
     */
    void get_observation_token(uint8_t *token, uint8_t &token_length) const;

    /**
     * \brief Returns the observation number.
     * \return The observation number of the object.
     */
    uint16_t observation_number() const;

    /**
     * \brief Adds the observation level for the object.
     * \param observation_level The level of observation.
     */
    void add_observation_level(M2MBase::Observation obs_level);

    /**
     * \brief Removes the observation level for the object.
     * \param observation_level The level of observation.
     */
    void remove_observation_level(M2MBase::Observation obs_level);

    /**
     * \brief Returns the observation level of the object.
     * \return The observation level of the object.
     */
    M2MBase::Observation observation_level() const;

    /**
     * @brief Returns whether this resource is under observation or not.
     * @return True if the resource is under observation, else false,
     */
    bool is_under_observation() const;

    /**
     * @brief Schedule a report, if the pmin is exceeded
     * report immediately, otherwise store the state to be
     * reported once the time fires.
     *
     * @param in_queue If the message is queued message then it must be send even if
     * current and last values are the same.
     */
    void schedule_report(bool in_queue = false);

    /**
     * @brief Set flag that new notification needs to be send.
     *
     * @param to_queue If True then notification is marked to be send
     */
    void set_notification_in_queue(bool to_queue);

    /**
     * @brief Returns whether notification needs to be send or not.
     *
     * @return Is notification sending needed or not.
     */
    bool notification_in_queue() const;

    /**
     * @brief Set flag that new notification needs to be send.
     *
     * @param to_queue If True then notification is marked to be send
     */
    void set_notification_send_in_progress(bool progress);

    /**
     * @brief Returns whether notification send is in progress or not.
     *
     * @return Is notification sending ongoing or not.
     */
    bool notification_send_in_progress() const;

    /**
     * @brief Sets whether notification will be sent using blockwise or not.
     *
     * @param blockwise_notify If True then notification is sent using blockwise.
     */
    void set_blockwise_notify(bool blockwise_notify);

    /**
     * @brief Returns whether notification is sent using blockwise or not.
     *
     * @return Is notification sent using blockwise.
     */
    bool blockwise_notify() const;

protected : // from M2MTimerObserver

    virtual void timer_expired(M2MTimerObserver::Type type =
                               M2MTimerObserver::Notdefined);

private:

    bool set_notification_attribute(const char* option,
            M2MBase::BaseType type,
            M2MResourceInstance::ResourceType resource_type);

    /**
    * @brief Reports a sample that satisfies the reporting criteria.
    *
    * @param in_queue If the message is queued message then it must be send even
    * current and last values are the same.
    */
    void report(bool in_queue = false);

    /**
    * @brief Manage timers for pmin and pmax.
    */
    void handle_timers();

    /**
    * @brief Check whether notification params can be accepted.
    */
    bool check_attribute_validity() const;

    /**
    * @brief Stop pmin & pmax timers.
    */
    void stop_timers();

    /**
     * @brief Check if current value match threshold values.
     * @return True if notify can be send otherwise false.
     */
    bool check_threshold_values() const;

    /**
     * @brief Check whether current value matches with GT & LT.
     * @return True if current value match with GT or LT values.
     */
    bool check_gt_lt_params() const;

    /**
     * \brief Allocate (size + 1) amount of memory, copy size bytes into
     * it and add zero termination.
     * \param source The source string to copy, may not be NULL.
     * \param size The size of memory to be reserved.
    */
    static uint8_t* alloc_string_copy(const uint8_t* source, uint32_t size);

private:
    M2MReportObserver           &_observer;
    bool                        _is_under_observation : 1;
    M2MBase::Observation        _observation_level : 4;
    uint8_t                     _attribute_state;
    unsigned                    _token_length : 8;
    bool                        _notify : 1;
    bool                        _pmin_exceeded : 1;
    bool                        _pmax_exceeded : 1;
    unsigned                    _observation_number : 24;
    M2MTimer                    _pmin_timer;
    M2MTimer                    _pmax_timer;
    uint8_t                     *_token;
    int32_t                     _pmax;
    int32_t                     _pmin;
    float                       _current_value;
    float                       _gt;
    float                       _lt;
    float                       _st;
    float                       _high_step;
    float                       _low_step;
    float                       _last_value;
    m2m::Vector<uint16_t>       _changed_instance_ids;
    bool                        _notification_send_in_progress : 1;
    bool                        _notification_in_queue : 1;
    bool                        _blockwise_notify : 1;
    bool                        _pmin_quiet_period : 1;

friend class Test_M2MReportHandler;

};

#endif // M2MREPORTHANDLER_H
