#include "catch2_common.h"

#include <catch2/matchers/catch_matchers.hpp>

constexpr static CORBA::ULong history_depth = 3u;

using ArrayType = Tango::DevVarBooleanArray;

ArrayType *get_array_data()
{
    auto out = new ArrayType;
    out->length(4);
    (*out)[0] = true;
    (*out)[1] = false;
    (*out)[2] = false;
    (*out)[3] = true;

    return out;
}

template <class Base>
class PolledDevice : public Base
{
  public:
    using Base::Base;

    ~PolledDevice() override { }

    void init_device() override { }

    ArrayType cmd_array()
    {
        return *get_array_data();
    }

    void fill_history()
    {
        Tango::CmdHistoryStack<ArrayType> chs;
        chs.length(history_depth);

        auto *dvba_array = get_array_data();

        auto when = Tango::get_current_system_datetime();
        for(CORBA::ULong k = 0; k < history_depth; k++)
        {
            Tango::TimedCmdData<ArrayType> tad(dvba_array, when);
            chs.push(tad);
        }

        std::string cmd_name = "cmd_array";
        Tango::Util *tg = Tango::Util::instance();
        tg->fill_cmd_polling_buffer(this, cmd_name, chs);
    }

    static void command_factory(std::vector<Tango::Command *> &cmds)
    {
        cmds.push_back(new TangoTest::AutoCommand<&PolledDevice::cmd_array>("cmd_array"));
        cmds.push_back(new TangoTest::AutoCommand<&PolledDevice::fill_history>("fill_history"));
    }
};

TANGO_TEST_AUTO_DEV_TMPL_INSTANTIATE(PolledDevice, 2)

SCENARIO("Command history can be injected and read out")
{
    int idlver = GENERATE(TangoTest::idlversion(2));
    GIVEN("a device proxy to a IDLv" << idlver << " device")
    {
        TangoTest::Context ctx{"cmd_history", "PolledDevice", idlver};
        auto device = ctx.get_proxy();
        REQUIRE(idlver == device->get_idl_version());

        THEN("we can fill the command history manually in the DS")
        {
            // we basically do not want, that history will be populated by Tango
            REQUIRE_NOTHROW(device->poll_command("cmd_array", 0));

            REQUIRE_NOTHROW(device->command_inout("fill_history"));

            AND_THEN("we can gather it as well")
            {
                using namespace Catch::Matchers;

                std::vector<Tango::DeviceDataHistory> *d_hist{nullptr};
                REQUIRE_NOTHROW(d_hist = device->command_history("cmd_array", history_depth));
                REQUIRE(d_hist != nullptr);

                for(CORBA::ULong i = 0; i < history_depth; i++)
                {
                    auto &elem = (*d_hist)[i];

                    const ArrayType *data;
                    elem >> data;
                    std::vector<bool> ref{true, false, false, true};
                    REQUIRE_THAT(*data, RangeEquals(ref));
                }

                delete d_hist;
            }
        }
    }
}
