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

#include <tango/server/TangoAppender.h>

#include <tango/client/DeviceProxy.h>
#include <tango/common/defines.h>
#include <tango/common/tango_const.h>

#include <chrono>
#include <iomanip>

#define USE_ASYNC_CALL

namespace Tango
{
TangoAppender::TangoAppender(const std::string &src_name,
                             const std::string &name,
                             const std::string &dev_name,
                             bool open_connection) :
    log4tango::Appender(name),
    _dev_name(dev_name),
    _src_name(src_name)

{
    _req_ctr = 0;
    if(open_connection)
    {
        reopen();
    }
}

TangoAppender::~TangoAppender()
{
    close();
}

bool TangoAppender::requires_layout() const
{
    return false;
}

void TangoAppender::set_layout(log4tango::Layout *)
{
    // no-op
}

bool TangoAppender::is_valid() const
{
    if(_dev_proxy == nullptr)
    {
        return false;
    }
    try
    {
        _dev_proxy->ping();
    }
    catch(...)
    {
        return false;
    }
    return true;
}

int TangoAppender::_append(const log4tango::LoggingEvent &event)
{
    //------------------------------------------------------------
    //- DO NOT LOG FROM THIS METHOD !!!
    //------------------------------------------------------------
    if(_dev_proxy == nullptr)
    {
        //--DO NOT RETURN -1 (ERROR ALREADY HANDLED)
        return 0;
    }
    try
    {
        auto *dvsa = new Tango::DevVarStringArray(6);
        if(dvsa != nullptr)
        {
            dvsa->length(6);
            auto ts_ms =
                std::chrono::duration_cast<std::chrono::milliseconds>(event.timestamp.time_since_epoch()).count();
            TangoSys_OMemStream ts_ms_str;

            ts_ms_str << std::fixed << std::noshowpoint << std::setprecision(0) << ts_ms << std::ends;
            std::string st = ts_ms_str.str();
            (*dvsa)[0] = Tango::string_dup(st.c_str());

            (*dvsa)[1] = Tango::string_dup(log4tango::Level::get_name(event.level).c_str());
            (*dvsa)[2] = Tango::string_dup(event.logger_name.c_str());
            (*dvsa)[3] = Tango::string_dup(event.message.c_str());
            (*dvsa)[4] = Tango::string_dup("");
            omni_thread *ct = omni_thread::self();
            if(ct != nullptr)
            {
                TangoSys_OMemStream ctstr;
                ctstr << "@" << std::hex << event.thread_id << " [" << ct->id() << "]" << std::ends;

                std::string st = ctstr.str();
                (*dvsa)[5] = Tango::string_dup(st.c_str());
            }
            else
            {
                (*dvsa)[5] = Tango::string_dup("unknown");
            }
            DeviceData argin;
            argin << dvsa;
#ifdef USE_ASYNC_CALL
            _dev_proxy->command_inout_asynch("Log", argin, false);
            _req_ctr++;
            if((_req_ctr % 10) == 0)
            {
                _dev_proxy->cancel_all_polling_asynch_request();
            }
#else
            _dev_proxy->command_inout("Log", argin);
#endif
        }
    }
    catch(...)
    {
        close();
        return -1;
    }
    return 0;
}

bool TangoAppender::reopen()
{
    bool result = true;
    try
    {
        close();
        _dev_proxy = new DeviceProxy(const_cast<std::string &>(_dev_name));
        try
        {
            DeviceData argin;
            argin << const_cast<std::string &>(_src_name);
#ifdef USE_ASYNC_CALL
            _dev_proxy->command_inout_asynch("Register", argin, true);
#else
            _dev_proxy->command_inout("Register", argin);
#endif
        }
        catch(...)
        {
        }
    }
    catch(...)
    {
        close();
        result = false;
    }
    return result;
}

void TangoAppender::close()
{
    if(_dev_proxy != nullptr)
    {
        try
        {
            DeviceData argin;
            argin << const_cast<std::string &>(_src_name);
#ifdef USE_ASYNC_CALL
            _dev_proxy->command_inout_asynch("UnRegister", argin, true);
#else
            _dev_proxy->command_inout("UnRegister", argin);
#endif
        }
        catch(...)
        {
            // Ignore error: some old logviewer may not support UnRegister
        }
        delete _dev_proxy;
        _dev_proxy = nullptr;
    }
}

} // namespace Tango
