//====================================================================================================================
//
// file :               PollRing.h
//
// description :        Include for the PollRing object. This class implements the polling ring buffer.
//                        Command result or attribute values are stored in this buffer manages as a ring
//                        buffer.
//
// project :            TANGO
//
// author(s) :          E.Taurel
//
// Copyright (C) :      2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015
//                        European Synchrotron Radiation Facility
//                      BP 220, Grenoble 38043
//                      FRANCE
//
// This file is part of Tango.
//
// Tango is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Tango is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along with Tango.
// If not, see <http://www.gnu.org/licenses/>.
//
//
//====================================================================================================================

#ifndef _POLLRING_H
#define _POLLRING_H

#include <tango/tango.h>
#include <tango/server/tango_clock.h>

namespace Tango
{

//===================================================================================================================
//
//            The RingElt class
//
// description :
//        Class to store all the necessary information which will be stored and returned to client on request
//
//===================================================================================================================

class RingElt
{
  public:
    RingElt();

    CORBA::Any *cmd_result;
    Tango::AttributeValueList *attr_value;
    Tango::AttributeValueList_3 *attr_value_3;
    Tango::AttributeValueList_4 *attr_value_4;
    Tango::AttributeValueList_5 *attr_value_5;
    Tango::DevFailed *except;
    PollClock::time_point when;
};

inline bool operator<(const RingElt &, const RingElt &)
{
    return true;
}

inline bool operator==(const RingElt &, const RingElt &)
{
    return true;
}

//===================================================================================================================
//
//            The PollRing class
//
// description :
//        Class to implement the ring buffer itself. This is mainly a vector of RingElt managed as a circular buffer
//
//==================================================================================================================

class PollRing
{
  public:
    PollRing();
    PollRing(long);
    ~PollRing();

    void insert_data(CORBA::Any *, PollClock::time_point);
    void insert_data(Tango::AttributeValueList *, PollClock::time_point);
    void insert_data(Tango::AttributeValueList_3 *, PollClock::time_point);
    void insert_data(Tango::AttributeValueList_4 *, PollClock::time_point, bool);
    void insert_data(Tango::AttributeValueList_5 *, PollClock::time_point, bool);
    void insert_except(Tango::DevFailed *, PollClock::time_point);

    template <typename T>
    void force_copy_data(T *);

    std::vector<PollClock::duration> get_delta_t(long nb);
    PollClock::time_point get_last_insert_date();

    bool is_empty()
    {
        return nb_elt == 0;
    }

    bool is_last_an_error();
    bool is_last_cmd_an_error();
    bool is_last_attr_an_error();

    Tango::DevFailed *get_last_except();
    Tango::DevErrorList &get_last_attr_error();

    CORBA::Any *get_last_cmd_result();
    Tango::AttributeValue &get_last_attr_value();
    Tango::AttributeValue_3 &get_last_attr_value_3();
    Tango::AttributeValue_4 &get_last_attr_value_4();
    Tango::AttributeValue_5 &get_last_attr_value_5();

    long get_nb_elt()
    {
        return nb_elt;
    }

    void get_cmd_history(long, Tango::DevCmdHistoryList *);
    void get_cmd_history(long, Tango::DevCmdHistory_4 *, Tango::CmdArgType &);

    void get_attr_history(long, Tango::DevAttrHistoryList *, long);
    void get_attr_history(long, Tango::DevAttrHistoryList_3 *, long);

    template <typename T>
    void get_attr_history(long, T *, long);

    void get_attr_history_43(long, Tango::DevAttrHistoryList_3 *, long);

  private:
    void inc_indexes();

    std::vector<RingElt> ring;
    long insert_elt;
    long nb_elt;
    long max_elt;
};

#define ADD_ELT_DATA_TO_GLOBAL_SEQ(GLOB, ELT, IND)        \
    {                                                     \
        unsigned int elt_data_length = ELT->length();     \
        for(unsigned int k = 0; k < elt_data_length; k++) \
            (*GLOB)[IND + k] = (*ELT)[k];                 \
        IND = IND + elt_data_length;                      \
    }

#define ADD_ELT_DATA_TO_GLOBAL_SEQ_BY_REF(GLOB, ELT, IND) \
    {                                                     \
        unsigned int elt_data_length = ELT.length();      \
        for(unsigned int k = 0; k < elt_data_length; k++) \
            GLOB[IND + k] = ELT[k];                       \
        IND = IND + elt_data_length;                      \
    }

#define ADD_ELT_DATA_TO_GLOBAL_SEQ_BY_PTR_REF(GLOB, ELT, IND) \
    {                                                         \
        unsigned int elt_data_length = ELT.length();          \
        for(unsigned int k = 0; k < elt_data_length; k++)     \
            (*GLOB)[IND + k] = ELT[k];                        \
        IND = IND + elt_data_length;                          \
    }

#define MANAGE_DIM_ARRAY(LENGTH)                              \
    if(last_dim.dim_x == LENGTH)                              \
    {                                                         \
        ptr->dims_array[dims_length - 2].nb_elt++;            \
    }                                                         \
    else                                                      \
    {                                                         \
        last_dim.dim_x = LENGTH;                              \
        last_dim.dim_y = 0;                                   \
        ptr->dims.length(dims_length);                        \
        ptr->dims[dims_length - 1] = last_dim;                \
        ptr->dims_array.length(dims_length);                  \
        ptr->dims_array[dims_length - 1].start = n - (i + 1); \
        ptr->dims_array[dims_length - 1].nb_elt = 1;          \
        dims_length++;                                        \
    }

#define ADD_SIMPLE_DATA_TO_GLOBAL_SEQ(GLOB, ELT, IND) \
    (*GLOB)[IND] = ELT;                               \
    IND = IND + 1;

#define MANAGE_DIM_SIMPLE()                                   \
    if(last_dim.dim_x == 1)                                   \
    {                                                         \
        ptr->dims_array[dims_length - 2].nb_elt++;            \
    }                                                         \
    else                                                      \
    {                                                         \
        last_dim.dim_x = 1;                                   \
        last_dim.dim_y = 0;                                   \
        ptr->dims.length(dims_length);                        \
        ptr->dims[dims_length - 1] = last_dim;                \
        ptr->dims_array.length(dims_length);                  \
        ptr->dims_array[dims_length - 1].start = n - (i + 1); \
        ptr->dims_array[dims_length - 1].nb_elt = 1;          \
        dims_length++;                                        \
    }

#define MANAGE_DIM_ARRAY_SEQ(LENGTH_STR, LENGTH_NUM)                     \
    if((last_dim.dim_x == LENGTH_STR) && (last_dim.dim_y == LENGTH_NUM)) \
    {                                                                    \
        ptr->dims_array[dims_length - 2].nb_elt++;                       \
    }                                                                    \
    else                                                                 \
    {                                                                    \
        last_dim.dim_x = LENGTH_STR;                                     \
        last_dim.dim_y = LENGTH_NUM;                                     \
        ptr->dims.length(dims_length);                                   \
        ptr->dims[dims_length - 1] = last_dim;                           \
        ptr->dims_array.length(dims_length);                             \
        ptr->dims_array[dims_length - 1].start = n - (i + 1);            \
        ptr->dims_array[dims_length - 1].nb_elt = 1;                     \
        dims_length++;                                                   \
    }

} // namespace Tango

#endif /* _POLLRING_ */
