#pragma once

#include <tango/tango.h>

namespace FileDb
{
class DatabaseDeviceBackend
{
  public:
    virtual ~DatabaseDeviceBackend();

    virtual Tango::DevVarStringArray *info() const = 0;

    virtual Tango::DevVarStringArray *get_property(const Tango::DevVarStringArray &) const = 0;
    virtual Tango::DevVarStringArray *get_device_property(const Tango::DevVarStringArray &) const = 0;
    virtual Tango::DevVarStringArray *get_class_property(const Tango::DevVarStringArray &) const = 0;
    virtual Tango::DevVarStringArray *get_device_attribute_property2(const Tango::DevVarStringArray &) const = 0;
    virtual Tango::DevVarStringArray *get_class_attribute_property2(const Tango::DevVarStringArray &) const = 0;
    virtual Tango::DevVarStringArray *get_device_pipe_property(const Tango::DevVarStringArray &) const = 0;
    virtual Tango::DevVarStringArray *get_class_pipe_property(const Tango::DevVarStringArray &) const = 0;

    virtual Tango::DevVarStringArray *get_device_list(const Tango::DevVarStringArray &) const = 0;
    virtual Tango::DevVarStringArray *get_device_domain_list(const Tango::DevString &) const = 0;
    virtual Tango::DevVarStringArray *get_device_family_list(const Tango::DevString &) const = 0;
    virtual Tango::DevVarStringArray *get_device_member_list(const Tango::DevString &) const = 0;
    virtual Tango::DevVarStringArray *get_device_wide_list(const Tango::DevString &) const = 0;
    virtual Tango::DevVarStringArray *get_device_exported_list(const Tango::DevString &) const = 0;
    virtual Tango::DevVarStringArray *get_device_property_list(const Tango::DevVarStringArray &) const = 0;
    virtual Tango::DevVarStringArray *get_server_list(const Tango::DevString &) const = 0;
    virtual Tango::DevVarStringArray *get_server_name_list(const Tango::DevString &) const = 0;
    virtual Tango::DevVarStringArray *get_device_class_list(const Tango::DevString &) const = 0;
    virtual Tango::DevVarStringArray *get_class_list(const Tango::DevString &) const = 0;

    virtual void add_server(const Tango::DevVarStringArray &) = 0;
    virtual void put_property(const Tango::DevVarStringArray &) = 0;
    virtual void put_device_property(const Tango::DevVarStringArray &) = 0;
    virtual void put_class_property(const Tango::DevVarStringArray &) = 0;

    virtual void delete_server(const Tango::DevString &) = 0;
    virtual void delete_property(const Tango::DevVarStringArray &) = 0;
    virtual void delete_device_property(const Tango::DevVarStringArray &) = 0;
    virtual void delete_class_property(const Tango::DevVarStringArray &) = 0;

    virtual Tango::DevVarLongStringArray *import_device(const Tango::DevString &) const = 0;
    virtual void export_device(const Tango::DevVarStringArray &) = 0;
    virtual void unexport_device(const Tango::DevString &) = 0;
    virtual void unexport_server(const Tango::DevString &) = 0;

  private:
    friend class DatabaseDeviceClass;

    virtual void export_self(std::string_view class_name,
                             std::string_view name,
                             std::string_view dserver_name,
                             std::string_view ior,
                             std::string_view dserver_ior,
                             std::string_view host,
                             int pid,
                             std::string_view version) = 0;
};

class DatabaseDevice : public TANGO_BASE_CLASS
{
  public:
    DatabaseDevice(Tango::DeviceClass *device_class, const std::string &dev_name) :
        TANGO_BASE_CLASS(device_class, dev_name)
    {
        init_device();
    }

    void init_device() override { }

  private:
    std::unique_ptr<DatabaseDeviceBackend> m_backend = nullptr;

    friend class DatabaseDeviceClass;
    template <auto fn>
    friend class DatabaseDeviceCommand;
};

class DatabaseDeviceClass : public Tango::DeviceClass
{
    using Tango::DeviceClass::DeviceClass;

  public:
    ~DatabaseDeviceClass() override { }

    void device_factory(const Tango::DevVarStringArray *devlist_ptr) override;
    void command_factory() override;
    void initialise_database();

    static std::string db_name;

  protected:
    static DatabaseDeviceClass *_instance;

  private:
    virtual DatabaseDeviceBackend *backend_factory() = 0;
};

} // namespace FileDb
