/*
 * SPDX-FileCopyrightText: 2025 Copyright contributors to the cppTango project
 *
 * SPDX-License-Identifier: LGPL-3.0-or-later
 */

#ifndef TANGO_COMMON_ZMQ_EVENT_MARSHALLING_H
#define TANGO_COMMON_ZMQ_EVENT_MARSHALLING_H

#include <tango/common/tango_const.h>
#include <tango/common/utils/assert.h>

#include <omniORB4/CORBA.h>

#include <iostream>

namespace Tango
{

/********************************************************************************
 *                                                                                 *
 *                         ZMQ event unmarshalling related classes                    *
 *                                                                                 *
 *******************************************************************************/

/***            TangoCdrMemoryStream                ***/

class TangoCdrMemoryStream : public cdrMemoryStream
{
  public:
    enum Unmarshal_type
    {
        UN_ATT,
        UN_PIPE
    };

    TangoCdrMemoryStream(void *buf, size_t si) :
        cdrMemoryStream(buf, si)
    {
    }

    TangoCdrMemoryStream() :
        cdrMemoryStream(512, false)
    {
    }

    void tango_get_octet_array(int size);

    void *get_end_out_buf()
    {
        return pd_outb_end;
    }

    void *get_mkr_out_buf()
    {
        return pd_outb_mkr;
    }

    void *get_end_in_buf()
    {
        return pd_inb_end;
    }

    void *get_mkr_in_buf()
    {
        return pd_inb_mkr;
    }

    void set_mkr_in_buf(void *_ptr)
    {
        pd_inb_mkr = _ptr;
    }

    void rewind_in(int _nb)
    {
        pd_inb_mkr = (char *) pd_inb_mkr - _nb;
    }

    void set_un_marshal_type(Unmarshal_type _ty)
    {
        un_type = _ty;
    }

    Unmarshal_type get_un_marshal_type()
    {
        return un_type;
    }

    omni::ptr_arith_t align_to(omni::ptr_arith_t p, omni::alignment_t align);

  protected:
    Unmarshal_type un_type;
};

inline omni::ptr_arith_t TangoCdrMemoryStream::align_to(omni::ptr_arith_t p, omni::alignment_t align)
{
    return (p + ((omni::ptr_arith_t) align - 1)) & ~((omni::ptr_arith_t) align - 1);
}

inline void TangoCdrMemoryStream::tango_get_octet_array(int size)
{
    pd_inb_mkr = (void *) ((char *) pd_inb_mkr + size);
}

/***            ZmqAttrValUnion               ***/

class ZmqAttrValUnion : public AttrValUnion
{
  public:
    void operator<<=(TangoCdrMemoryStream &);

    template <typename T, typename TA>
    void init_seq(char *, CORBA::ULong &, TangoCdrMemoryStream &);

    template <typename T>
    void set_seq(T &)
    {
        std::cerr << "In default ZmqAttrValUnion::set_seq!" << std::endl;
        TANGO_ASSERT(false);
    }

    template <typename T>
    T &get_seq()
    {
        std::cerr << "In default ZmqAttrValUnion::get_seq!" << std::endl;
        TANGO_ASSERT(false);
    }
};

/***            ZmqAttributeValue_4               ***/

struct ZmqAttributeValue_4 : public AttributeValue_4
{
    ZmqAttrValUnion zvalue;
    void operator<<=(TangoCdrMemoryStream &);
};

/***            ZmqAttributeValue_5               ***/

struct ZmqAttributeValue_5 : public AttributeValue_5
{
    ZmqAttrValUnion zvalue;
    void operator<<=(TangoCdrMemoryStream &);
};

/***            ZmqDevPipeData                  ***/

struct ZmqDevPipeData : public DevPipeData
{
    void operator<<=(TangoCdrMemoryStream &);
};

/***            ZmqDevPipeBlob                     ***/

struct ZmqDevPipeBlob : public DevPipeBlob
{
    void operator<<=(TangoCdrMemoryStream &);
};

/***            ZmqDevVarPipeDataEltArray         ***/

class ZmqDevVarPipeDataEltArray : public DevVarPipeDataEltArray
{
  public:
    void operator<<=(TangoCdrMemoryStream &);
};

/***            ZmqDevPipeDataElt                  ***/

struct ZmqDevPipeDataElt : public DevPipeDataElt
{
    void operator<<=(TangoCdrMemoryStream &);
};

/***    Macros to help coding       ***/

#ifndef Swap16
  #define Swap16(s) ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff))
#else
  #error "Swap16 has already been defined"
#endif

#ifndef Swap32
  #define Swap32(l) \
      ((((l) & 0xff000000) >> 24) | (((l) & 0x00ff0000) >> 8) | (((l) & 0x0000ff00) << 8) | (((l) & 0x000000ff) << 24))
#else
  #error "Swap32 has already been defined"
#endif

//
// These template specialization allow us to set or get sequences within a AttrValUnion union.
// Union method to get/set sequence value are named short_att_value(), double_att_value(),
// float_att_value()....
//

#define SEQ_METH(NAME, TYPE)                               \
    template <>                                            \
    inline void ZmqAttrValUnion::set_seq<TYPE>(TYPE & val) \
    {                                                      \
        NAME##_att_value(val);                             \
    }                                                      \
                                                           \
    template <>                                            \
    inline TYPE &ZmqAttrValUnion::get_seq<TYPE>()          \
    {                                                      \
        return NAME##_att_value();                         \
    }

/*** Macros call to generate required template specialisation   ***/

SEQ_METH(short, DevVarShortArray)
SEQ_METH(double, DevVarDoubleArray)
SEQ_METH(float, DevVarFloatArray)
SEQ_METH(ushort, DevVarUShortArray)
SEQ_METH(bool, DevVarBooleanArray)
SEQ_METH(long, DevVarLongArray)
SEQ_METH(long64, DevVarLong64Array)
SEQ_METH(ulong, DevVarULongArray)
SEQ_METH(uchar, DevVarUCharArray)
SEQ_METH(ulong64, DevVarULong64Array)
SEQ_METH(state, DevVarStateArray)

/**     Template methods definition     ***/

//
// Create a dummy empty sequence and init union with this dummy sequence (minimun data copy)
// Retrieve a reference to that sequence once it is in the union and replace its data pointer
// by the data in the CdrMemoryStream.
// Use set_seq(), get_seq() specialized template created just above to set/get union sequences
// T is the basic data type (DevShort)
// TA is the sequence data type (DevVarShortArray)
//
// Also manage big and litle endian!!
//

template <typename T, typename TA>
inline void ZmqAttrValUnion::init_seq(char *base_ptr, CORBA::ULong &length, TangoCdrMemoryStream &_n)
{
    TA dummy_val;
    set_seq<TA>(dummy_val);

    T *ptr;
    if(_n.get_un_marshal_type() == TangoCdrMemoryStream::UN_ATT)
    {
        ptr = (T *) (base_ptr + _n.currentInputPtr());
    }
    else
    {
        char *tmp = (char *) _n.get_mkr_in_buf();
        int delta = tmp - base_ptr;
        ptr = (T *) (base_ptr + delta);
    }

    if(_n.unmarshal_byte_swap())
    {
        if(sizeof(T) == 2)
        {
            for(CORBA::ULong i = 0; i < length; i++)
            {
                _CORBA_UShort *tmp_ptr = (_CORBA_UShort *) ptr;
                _CORBA_UShort tt = *(tmp_ptr + i);
                *(tmp_ptr + i) = Swap16(tt);
            }
        }
        else if(sizeof(T) == 4)
        {
            for(CORBA::ULong i = 0; i < length; i++)
            {
                CORBA::ULong *tmp_ptr = (CORBA::ULong *) ptr;
                CORBA::ULong t = *(tmp_ptr + i);
                *(tmp_ptr + i) = Swap32(t);
            }
        }
        else if(sizeof(T) == 8)
        {
            CORBA::ULong double_length = length * 2;
            for(CORBA::ULong i = 0; i < double_length; i += 2)
            {
                CORBA::ULong *tmp_ptr = (CORBA::ULong *) ptr;
                CORBA::ULong tl1 = *(tmp_ptr + i + 1);
                CORBA::ULong tl2 = Swap32(tl1);
                tl1 = *(tmp_ptr + i);
                *(tmp_ptr + i) = tl2;
                *(tmp_ptr + i + 1) = Swap32(tl1);
            }
        }
    }

    TA &the_seq = get_seq<TA>();
    the_seq.replace(length, length, ptr, false);

    _n.tango_get_octet_array((length * sizeof(T)));
}

} // namespace Tango

#endif
