/*
 * SPDX-FileCopyrightText: 2011 Copyright contributors to the cppTango project
 *
 * SPDX-License-Identifier: LGPL-3.0-or-later
 */
// NOLINTBEGIN(*)

#include "old_common.h"

#include <tango/client/AttrWrittenEvent.h>

class MyCallBack : public CallBack
{
  public:
    MyCallBack() :
        cb_executed(0),
        to(false),
        attr_failed(false)
    {
    }

    virtual void attr_written(AttrWrittenEvent *);

    long cb_executed;
    bool to;
    bool attr_failed;
    long nb_attr;
    long faulty_attr_nb;
};

void MyCallBack::attr_written(AttrWrittenEvent *att)
{
    TEST_LOG << "In attr_written method for device " << att->device->dev_name() << std::endl;
    for(unsigned int i = 0; i < att->attr_names.size(); i++)
    {
        TEST_LOG << "Attribute written = " << att->attr_names[i] << std::endl;
    }

    nb_attr = att->attr_names.size();
    faulty_attr_nb = att->errors.get_faulty_attr_nb();

    if(att->err == true)
    {
        long nb_err = att->errors.errors.length();
        TEST_LOG << "write_attributes returns error" << std::endl;
        TEST_LOG << "error length = " << nb_err << std::endl;
        for(int i = 0; i < nb_err; i++)
        {
            TEST_LOG << "error[" << i << "].reason = " << att->errors.errors[i].reason << std::endl;
        }
        if(strcmp(att->errors.errors[nb_err - 1].reason, API_DeviceTimedOut) == 0)
        {
            to = true;
            TEST_LOG << "Timeout error" << std::endl;
        }
        else if(strcmp(att->errors.errors[nb_err - 1].reason, API_AttributeFailed) == 0)
        {
            attr_failed = true;
            TEST_LOG << "Write attributes failed error" << std::endl;
        }
        else
        {
            TEST_LOG << "Unknown error" << std::endl;
        }
    }
    else
    {
        if(faulty_attr_nb != 0)
        {
            if(strcmp(att->errors.errors[0].reason, API_AttributeFailed) == 0)
            {
                if((strcmp(att->errors.err_list[0].err_stack[0].reason, "aaa") == 0) &&
                   (att->errors.err_list[0].idx_in_call == 0) &&
                   (att->errors.err_list[0].name == "attr_asyn_write_except"))
                {
                    attr_failed = true;
                    TEST_LOG << "Write attributes failed error" << std::endl;
                }
            }
        }
    }

    cb_executed++;
}

int main(int argc, char **argv)
{
    DeviceProxy *device;

    if(argc == 1)
    {
        TEST_LOG << "usage: %s device" << std::endl;
        exit(-1);
    }

    std::string device_name = argv[1];

    try
    {
        device = new DeviceProxy(device_name);
    }
    catch(CORBA::Exception &e)
    {
        Except::print_exception(e);
        exit(1);
    }

    TEST_LOG << std::endl << "new DeviceProxy(" << device->name() << ") returned" << std::endl << std::endl;

    try
    {
        // Change timeout (with a useless name as a workaround for ORBacus bug)

        //        device->set_timeout_millis(6000);

        // Write one attribute

        MyCallBack cb;

        DeviceAttribute send;

        send.set_name("attr_asyn_write");
        DevLong lg = 222;
        send << lg;

        device->write_attribute_asynch(send, cb);

        // Check if attribute returned

        long nb_not_arrived = 0;
        while(cb.cb_executed == 0)
        {
            TEST_LOG << "Attribute not written yet" << std::endl;
            nb_not_arrived++;
            std::this_thread::sleep_for(std::chrono::seconds(1));

            device->get_asynch_replies();
        }

        assert(cb.cb_executed == 1);
        assert(nb_not_arrived >= 2);
        assert(cb.nb_attr == 1);
        assert(cb.faulty_attr_nb == 0);

        TEST_LOG << "   Asynchronous write_attribute in callback mode --> OK" << std::endl;

        // Read attribute to check polling with blocking with timeout

        cb.cb_executed = 0;
        cb.nb_attr = 0;
        cb.faulty_attr_nb = 10;

        device->write_attribute_asynch(send, cb);

        // Check if attribute returned

        nb_not_arrived = 0;
        while(cb.cb_executed == 0)
        {
            try
            {
                device->get_asynch_replies(200);
            }
            catch(AsynReplyNotArrived &)
            {
                TEST_LOG << "Attribute not yet written" << std::endl;
                nb_not_arrived++;
            }
        }

        assert(cb.nb_attr == 1);
        assert(nb_not_arrived >= 4);
        assert(cb.cb_executed == 1);
        assert(cb.faulty_attr_nb == 0);

        TEST_LOG << "   Asynchronous write_attribute (callback) in blocking mode with call timeout --> OK" << std::endl;

        // Send a command to check polling with blocking

        cb.cb_executed = 0;
        cb.nb_attr = 0;
        cb.faulty_attr_nb = 10;

        device->write_attribute_asynch(send, cb);

        // Check if command returned

        device->get_asynch_replies(0);

        assert(cb.nb_attr == 1);
        assert(cb.cb_executed == 1);
        assert(cb.faulty_attr_nb == 0);

        TEST_LOG << "   Asynchronous write_attribute (callback) in blocking mode --> OK" << std::endl;

        //---------------------------------------------------------------------------
        //
        //            Now test Timeout exception and asynchronous calls
        //
        //---------------------------------------------------------------------------

        // Change timeout in order to test asynchronous calls and timeout

        //        device->set_timeout_millis(2000);

        // Read an attribute

        cb.cb_executed = 0;
        cb.nb_attr = 0;
        cb.faulty_attr_nb = 10;

        send.set_name("attr_asyn_write_to");
        device->write_attribute_asynch(send, cb);

        // Check if attribute returned

        nb_not_arrived = 0;
        while(cb.cb_executed == 0)
        {
            nb_not_arrived++;
            TEST_LOG << "Attribute not yet written" << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(1));

            device->get_asynch_replies();
        }

        assert(cb.to == true);
        assert(nb_not_arrived >= 2);
        assert(cb.nb_attr == 1);
        assert(cb.faulty_attr_nb == 0);

        TEST_LOG << "   Device timeout exception with non blocking get_asynch_replies --> OK" << std::endl;

        // Read an attribute to check timeout with polling and blocking with timeout

        cb.cb_executed = 0;
        cb.to = false;
        cb.nb_attr = 0;
        cb.faulty_attr_nb = 10;

        device->write_attribute_asynch(send, cb);

        // Check if command returned

        nb_not_arrived = 0;
        while(cb.cb_executed == 0)
        {
            try
            {
                device->get_asynch_replies(500);
            }
            catch(AsynReplyNotArrived &)
            {
                TEST_LOG << "Attribute not yet written" << std::endl;
                nb_not_arrived++;
            }
        }
        assert(cb.to == true);
        assert(nb_not_arrived >= 2);
        assert(cb.nb_attr == 1);
        assert(cb.faulty_attr_nb == 0);

        TEST_LOG << "   Device timeout with blocking get_asynch_replies with call timeout --> OK" << std::endl;

        // Read an attribute to check polling with blocking

        cb.cb_executed = 0;
        cb.to = false;
        cb.nb_attr = 0;
        cb.faulty_attr_nb = 10;

        device->write_attribute_asynch(send, cb);

        // Check if attribute returned

        device->get_asynch_replies(0);

        assert(cb.to == true);
        assert(cb.nb_attr == 1);
        assert(cb.faulty_attr_nb == 0);

        TEST_LOG << "   Device timeout with blocking get_asynch_replies --> OK" << std::endl;

        //---------------------------------------------------------------------------
        //
        //            Now test DevFailed exception sent by server
        //
        //---------------------------------------------------------------------------

        TEST_LOG << "   Waiting for server to execute all previous requests" << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(5));

        // Change timeout in order to test asynchronous calls and DevFailed exception

        //        device->set_timeout_millis(5000);

        // Read attribute

        cb.cb_executed = 0;
        cb.to = false;
        cb.nb_attr = 0;
        cb.faulty_attr_nb = 10;

        send.set_name("attr_asyn_write_except");
        device->write_attribute_asynch(send, cb);

        // Check if attribute returned

        nb_not_arrived = 0;
        while(cb.cb_executed == 0)
        {
            nb_not_arrived++;
            TEST_LOG << "Attribute not yet written" << std::endl;
            std::this_thread::sleep_for(std::chrono::seconds(1));

            device->get_asynch_replies();
        }

        assert(cb.attr_failed == true);
        assert(nb_not_arrived >= 2);
        assert(cb.nb_attr == 1);
        assert(cb.faulty_attr_nb == 1);

        TEST_LOG << "   Device exception with non blocking get_asynch_replies --> OK" << std::endl;

        // Read an attribute to check timeout with polling and blocking with timeout

        cb.cb_executed = 0;
        cb.attr_failed = false;
        cb.nb_attr = 0;
        nb_not_arrived = 0;

        device->write_attribute_asynch(send, cb);

        // Check if attribute returned

        while(cb.cb_executed == 0)
        {
            try
            {
                device->get_asynch_replies(500);
            }
            catch(AsynReplyNotArrived &)
            {
                TEST_LOG << "Attribute not yet written" << std::endl;
                nb_not_arrived++;
            }
        }
        assert(cb.attr_failed == true);
        assert(cb.cb_executed == 1);
        assert(nb_not_arrived >= 2);
        assert(cb.nb_attr == 1);

        TEST_LOG << "   Device exception with blocking get_asynch_replies with call timeout --> OK" << std::endl;

        // Read an attribute to check polling with blocking

        cb.cb_executed = 0;
        cb.attr_failed = false;
        cb.nb_attr = 0;

        device->write_attribute_asynch(send, cb);

        // Check if attribute returned

        device->get_asynch_replies(0);

        assert(cb.attr_failed == true);
        assert(cb.cb_executed == 1);
        assert(cb.nb_attr == 1);

        TEST_LOG << "   Device exception with blocking get_asynch_replies --> OK" << std::endl;

        //---------------------------------------------------------------------------
        //
        //            Now test several attributes written in one call
        //
        //---------------------------------------------------------------------------

        // Write several attributes in one call

        cb.nb_attr = 0;
        cb.cb_executed = 0;

        std::vector<DeviceAttribute> send_m(2);

        send_m[0].set_name("attr_asyn_write");
        lg = 222;
        send_m[0] << lg;

        send_m[1].set_name("Double_attr_w");
        double db = 4.55;
        send_m[1] << db;

        device->write_attributes_asynch(send_m, cb);

        // Check if attribute returned

        nb_not_arrived = 0;
        while(cb.cb_executed == 0)
        {
            TEST_LOG << "Attributes not read written" << std::endl;
            nb_not_arrived++;
            std::this_thread::sleep_for(std::chrono::seconds(1));

            device->get_asynch_replies();
        }

        assert(nb_not_arrived >= 2);
        assert(cb.nb_attr == 2);

        TEST_LOG << "   Several attributes in write_attributes in callback mode --> OK" << std::endl;
    }
    catch(Tango::DevFailed &e)
    {
        Except::print_exception(e);
        exit(-1);
    }
    catch(CORBA::Exception &ex)
    {
        Except::print_exception(ex);
        exit(-1);
    }

    delete device;

    return 0;
}

// NOLINTEND(*)
