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

#include <tango/client/DeviceDataHistory.h>

#include <tango/internal/utils.h>

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

#include <iomanip>
#include <memory>

using namespace CORBA;

namespace Tango
{

//-----------------------------------------------------------------------------
//
// DeviceDataHistory::DeviceDataHistory() - constructors to create DeviceDataHistory
//
//-----------------------------------------------------------------------------

DeviceDataHistory::DeviceDataHistory() :

    ext_hist(nullptr)
{
    fail = false;
    err = new DevErrorList();
    seq_ptr = nullptr;
    ref_ctr_ptr = nullptr;
}

DeviceDataHistory::DeviceDataHistory(int n, int *ref, DevCmdHistoryList *ptr) :
    ext_hist(nullptr)
{
    ref_ctr_ptr = ref;
    seq_ptr = ptr;

    (*ref_ctr_ptr)++;

    any = &((*ptr)[n].value);
    fail = (*ptr)[n].cmd_failed;
    time = (*ptr)[n].time;
    err = &((*ptr)[n].errors);
}

DeviceDataHistory::DeviceDataHistory(const DeviceDataHistory &source) :
    DeviceData(source),
    ext_hist(nullptr)
{
    fail = source.fail;
    time = source.time;
    err = const_cast<DeviceDataHistory &>(source).err._retn();

    seq_ptr = source.seq_ptr;
    ref_ctr_ptr = source.ref_ctr_ptr;
    if(ref_ctr_ptr != nullptr)
    {
        (*ref_ctr_ptr)++;
    }

    if(source.ext_hist != nullptr)
    {
        ext_hist = std::make_unique<DeviceDataHistoryExt>();
        *(ext_hist) = *(source.ext_hist);
    }
}

DeviceDataHistory::DeviceDataHistory(DeviceDataHistory &&source) :
    DeviceData(std::move(source)),
    ext_hist(nullptr)
{
    fail = source.fail;
    time = source.time;
    err = source.err._retn();

    seq_ptr = source.seq_ptr;
    ref_ctr_ptr = source.ref_ctr_ptr;

    if(source.ext_hist != nullptr)
    {
        ext_hist = std::move(source.ext_hist);
    }
    else
    {
        ext_hist.reset();
    }
}

//-----------------------------------------------------------------------------
//
// DeviceDataHistory::~DeviceDataHistory() - Destructor
//
//-----------------------------------------------------------------------------

DeviceDataHistory::~DeviceDataHistory()
{
    if(seq_ptr != nullptr)
    {
        any._retn();
        err._retn();

        (*ref_ctr_ptr)--;
        if(*ref_ctr_ptr == 0)
        {
            delete seq_ptr;
            delete ref_ctr_ptr;
        }
    }
}

//-----------------------------------------------------------------------------
//
// DeviceDataHistory::operator=() - assignement operator
//
//-----------------------------------------------------------------------------

DeviceDataHistory &DeviceDataHistory::operator=(const DeviceDataHistory &rval)
{
    if(this != &rval)
    {
        //
        // Assignement of DeviceData class members first
        //

        this->DeviceData::operator=(rval);

        //
        // Then, assignement of DeviceDataHistory members
        //

        fail = rval.fail;
        time = rval.time;
        err = rval.err;

        if(ref_ctr_ptr != nullptr)
        {
            (*ref_ctr_ptr)--;
            if(*ref_ctr_ptr == 0)
            {
                delete seq_ptr;
                delete ref_ctr_ptr;
            }
        }

        seq_ptr = rval.seq_ptr;
        ref_ctr_ptr = rval.ref_ctr_ptr;
        (*ref_ctr_ptr)++;

        if(rval.ext_hist != nullptr)
        {
            ext_hist = std::make_unique<DeviceDataHistoryExt>();
            *(ext_hist) = *(rval.ext_hist);
        }
        else
        {
            ext_hist.reset();
        }
    }

    return *this;
}

//-----------------------------------------------------------------------------
//
// DeviceDataHistory::operator=() - move assignement operator
//
//-----------------------------------------------------------------------------

DeviceDataHistory &DeviceDataHistory::operator=(DeviceDataHistory &&rval)
{
    //
    // Assignement of DeviceData class members first
    //

    this->DeviceData::operator=(std::move(rval));

    //
    // Then, assignement of DeviceDataHistory members
    //

    fail = rval.fail;
    time = rval.time;
    err = rval.err._retn();

    //
    // Decrement old ctr
    //
    if(ref_ctr_ptr != nullptr)
    {
        (*ref_ctr_ptr)--;
        if(*ref_ctr_ptr == 0)
        {
            delete seq_ptr;
            delete ref_ctr_ptr;
        }
    }

    //
    // Copy ctr (but don't increment it) and ptr
    //

    seq_ptr = rval.seq_ptr;
    ref_ctr_ptr = rval.ref_ctr_ptr;

    //
    // Extension class
    //

    if(rval.ext_hist != nullptr)
    {
        ext_hist = std::move(rval.ext_hist);
    }
    else
    {
        ext_hist.reset();
    }

    return *this;
}

//+-------------------------------------------------------------------------
//
// operator overloading :     <<
//
// description :     Friend function to ease printing instance of the
//            DeviceDataHistory class
//
//--------------------------------------------------------------------------

std::ostream &operator<<(std::ostream &o_str, const DeviceDataHistory &dh)
{
    //
    // First, print date
    //

    std::tm tm = Tango_localtime(dh.time.tv_sec);
    o_str << std::put_time(&tm, "%c") << " (" << dh.time.tv_sec << "," << std::setw(6) << std::setfill('0')
          << dh.time.tv_usec << " sec) : ";

    //
    // Print data or error stack
    //

    if(dh.fail)
    {
        unsigned int nb_err = dh.err.in().length();
        for(unsigned long i = 0; i < nb_err; i++)
        {
            o_str << "Tango error stack" << std::endl;
            o_str << "Severity = " << (dh.err.in())[i].severity << std::endl;
            o_str << "Error reason = " << (dh.err.in())[i].reason.in() << std::endl;
            o_str << "Desc : " << (dh.err.in())[i].desc.in() << std::endl;
            o_str << "Origin : " << (dh.err.in())[i].origin.in();
            if(i != nb_err - 1)
            {
                o_str << std::endl;
            }
        }
    }
    else
    {
        o_str << static_cast<const DeviceData &>(dh);
    }

    return o_str;
}

} // namespace Tango
