#pragma once

#include "database_tables.h"
#include "database_device.h"

namespace FileDb
{
class FileDbDeviceBackend : public DatabaseDeviceBackend
{
  public:
    FileDbDeviceBackend(const char *filename = "", bool save = true);

    ~FileDbDeviceBackend() override { }

    Tango::DevVarStringArray *info() const override;

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

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

    void add_server(const Tango::DevVarStringArray &) override;
    void put_property(const Tango::DevVarStringArray &) override;
    void put_device_property(const Tango::DevVarStringArray &) override;
    void put_class_property(const Tango::DevVarStringArray &) override;

    void delete_server(const Tango::DevString &) override;
    void delete_property(const Tango::DevVarStringArray &) override;
    void delete_device_property(const Tango::DevVarStringArray &) override;
    void delete_class_property(const Tango::DevVarStringArray &) override;

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

    // For testing only, expects that the idents for each record are valid.
    explicit FileDbDeviceBackend(DbConfigTables &&initial_config_tables);
    explicit FileDbDeviceBackend(DbRuntimeTables &&initial_runtime_tables);
    FileDbDeviceBackend(DbConfigTables &&initial_config_tables, DbRuntimeTables &&initial_runtime_tables);

    const DbConfigTables &config_tables() const
    {
        return m_config_tables;
    }

    const DbRuntimeTables &runtime_tables() const
    {
        return m_runtime_tables;
    }

  private:
    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) override;

    void delete_server_no_save(cistring_view server);
    void add_devices_from_server(const ServerRecord &dd);
    void save_config();

    DbConfigTables m_config_tables;
    DbRuntimeTables m_runtime_tables;

    std::string m_filename;
    bool m_save;
};

class FileDbDeviceClass : public DatabaseDeviceClass
{
  public:
    ~FileDbDeviceClass() override { }

    static DatabaseDeviceClass *init(const char *s)
    {
        if(_instance == nullptr)
        {
            _instance = new FileDbDeviceClass{s};
        }

        return _instance;
    }

  private:
    using DatabaseDeviceClass::DatabaseDeviceClass;

    DatabaseDeviceBackend *backend_factory() override
    {
        std::string filename = ":memory:";
        Tango::ApiUtil::get_env_var("FILEDBDS_FILE", filename);

        if(filename == ":memory:")
        {
            return new FileDbDeviceBackend("", false);
        }
        else if(filename.find(":init_only:") == 0)
        {
            constexpr const size_t prefix_len = 11;
            return new FileDbDeviceBackend(filename.c_str() + prefix_len, false);
        }

        return new FileDbDeviceBackend(filename.c_str());
    }
};
} // namespace FileDb
