//
// Copyright (c) 2020-2022, NVIDIA CORPORATION.  All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto.  Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//

#pragma once

#include "cqi.hpp"
#include "nvswitch_nvlink_port.hpp"
#include "nvswitch_nvlink_lane.hpp"
#include "nvswitch_osfp.hpp"

#include "nscq/nscq.h"

#include <memory>

namespace nscq::cqi {

struct nvswitch_dev;

struct nvswitch_dev_base : public cqi {
    using cqi_t = nvswitch_dev_base;
    using id_t = std::shared_ptr<uuid>;
    using cqi::cqi;
    [[nodiscard]] virtual auto get_id() const -> id_t = 0;
    [[nodiscard]] virtual auto make_root_cqi() -> std::shared_ptr<nvswitch_dev> = 0;

    struct uuid_field : public field<nvswitch_dev_base, readable<std::shared_ptr<nscq::uuid>>> {};
    virtual auto get(uuid_field) -> std::shared_ptr<nscq::uuid> = 0;

    struct pcie_location_field : public field<nvswitch_dev_base, readable<nscq_pcie_location_t>> {};
    virtual auto get(pcie_location_field) -> nscq_pcie_location_t = 0;

    struct blacklist_reason_field : public field<nvswitch_dev_base, readable<blacklist_reason_t>> {
    };
    virtual auto get(blacklist_reason_field) -> blacklist_reason_t = 0;
};

struct nvswitch_dev : public cqi {
    using cqi_t = nvswitch_dev;
    using id_t = std::shared_ptr<uuid>;
    using cqi::cqi;
    [[nodiscard]] virtual auto get_id() const -> id_t = 0;

    virtual auto make_cqi(type<nvswitch_nvlink_port>, const nvswitch_nvlink_port::id_t&)
        -> std::shared_ptr<nvswitch_nvlink_port> = 0;

    virtual auto get_ids(type<nvswitch_nvlink_port>) const
        -> std::vector<nvswitch_nvlink_port::id_t> = 0;

    virtual auto make_cqi(type<nvswitch_osfp>, const nvswitch_osfp::id_t&)
        -> std::shared_ptr<nvswitch_osfp> = 0;

    virtual auto get_ids(type<nvswitch_osfp>) const
        -> std::vector<nvswitch_osfp::id_t> = 0;

    struct arch_field : public field<nvswitch_dev, readable<nscq_arch_t>> {};
    virtual auto get(arch_field) -> nscq_arch_t = 0;

    struct phys_id_field : public field<nvswitch_dev, readable<uint32_t>> {};
    virtual auto get(phys_id_field) -> uint32_t = 0;

    struct uuid_field : public field<nvswitch_dev, readable<std::shared_ptr<nscq::uuid>>> {};
    virtual auto get(uuid_field) -> std::shared_ptr<nscq::uuid> = 0;

    struct nvlink_id_field : public field<nvswitch_dev, readable<uint64_t>> {};
    virtual auto get(nvlink_id_field) -> uint64_t = 0;
    virtual auto has(nvlink_id_field) -> bool = 0;

    struct pcie_location_field : public field<nvswitch_dev, readable<nscq_pcie_location_t>> {};
    virtual auto get(pcie_location_field) -> nscq_pcie_location_t = 0;

    struct firmware_version_field
    : public field<nvswitch_dev, readable<std::shared_ptr<nscq::label>>> {};
    virtual auto get(firmware_version_field) -> std::shared_ptr<nscq::label> = 0;
    virtual auto has(firmware_version_field) -> bool = 0;

    struct inforom_version_field : public field<nvswitch_dev, readable<std::shared_ptr<nscq::label>>> {};
    virtual auto get(inforom_version_field) -> std::shared_ptr<nscq::label> = 0;
    virtual auto has(inforom_version_field) -> bool = 0;

    struct fabric_state_field : public watchable_field<nvswitch_dev, readable<fabric_state>> {
        fabric_state_field(std::shared_ptr<nscq::cqi_session> &cqi_session) : watchable_field<nvswitch_dev, readable<fabric_state>>(cqi_session) {}
    };
    virtual auto get(fabric_state_field) -> fabric_state = 0;
    virtual auto watch(fabric_state_field) -> void = 0;
    virtual auto unwatch(fabric_state_field) -> void = 0;

    struct reset_required_field : public field<nvswitch_dev, readable<bool>> {};
    virtual auto get(reset_required_field) -> bool = 0;

    struct current_temp_field : public field<nvswitch_dev, readable<int32_t>> {};
    virtual auto get(current_temp_field) -> int32_t = 0;

    struct temp_sensors_field : public field<nvswitch_dev, readable<nscq_temperature_sensors_t>> {};
    virtual auto get(temp_sensors_field) -> nscq_temperature_sensors_t = 0;
    virtual auto has(temp_sensors_field) -> bool = 0;

    struct limit_slowdown_temp_field : public field<nvswitch_dev, readable<int32_t>> {};
    virtual auto get(limit_slowdown_temp_field) -> int32_t = 0;

    struct limit_shutdown_temp_field : public field<nvswitch_dev, readable<int32_t>> {};
    virtual auto get(limit_shutdown_temp_field) -> int32_t = 0;

    struct nvlink_throughput_field : public field<nvswitch_nvlink_port, readable<nscq_link_throughput_t>> {};
    virtual auto get(nvlink_throughput_field) -> nscq_link_throughput_t = 0;
    virtual auto has(nvlink_throughput_field) -> bool = 0;

    struct nvlink_raw_throughput_field : public field<nvswitch_nvlink_port, readable<nscq_link_throughput_t>> {};
    virtual auto get(nvlink_raw_throughput_field) -> nscq_link_throughput_t = 0;
    virtual auto has(nvlink_raw_throughput_field) -> bool = 0;

    struct fatal_error_field : public watchable_field<nvswitch_dev, readable<std::vector<nscq_error_t>>> {
        fatal_error_field(std::shared_ptr<nscq::cqi_session> &cqi_session) : watchable_field<nvswitch_dev, readable<std::vector<nscq_error_t>>>(cqi_session) {}
    };
    virtual auto get(fatal_error_field) -> std::vector<nscq_error_t> = 0;
    virtual auto watch(fatal_error_field) -> void = 0;
    virtual auto unwatch(fatal_error_field) -> void = 0;

    struct nonfatal_error_field : public watchable_field<nvswitch_dev, readable<std::vector<nscq_error_t>>> {
        nonfatal_error_field(std::shared_ptr<nscq::cqi_session> &cqi_session) : watchable_field<nvswitch_dev,
                             readable<std::vector<nscq_error_t>>>(cqi_session) {}
    };
    virtual auto get(nonfatal_error_field) -> std::vector<nscq_error_t> = 0;
    virtual auto watch(nonfatal_error_field) -> void = 0;
    virtual auto unwatch(nonfatal_error_field) -> void = 0;

    struct nvlink_ports_num_field : public field<nvswitch_dev, readable<uint32_t>> {};
    virtual auto get(nvlink_ports_num_field) -> uint32_t = 0;

    struct nvlink_ports_mask_field : public field<nvswitch_dev, readable<uint64_t>> {};
    virtual auto get(nvlink_ports_mask_field) -> uint64_t = 0;

    struct nvlink_vcs_num_field : public field<nvswitch_dev, readable<uint32_t>> {};
    virtual auto get(nvlink_vcs_num_field) -> uint32_t = 0;

    struct nvlink_clock_info_field : public field<nvswitch_dev, readable<nscq_nvlink_clock_info_t>> {};
    virtual auto get(nvlink_clock_info_field) -> nscq_nvlink_clock_info_t = 0;

    struct nvlink_voltage_info_field : public field<nvswitch_dev, readable<nscq_nvlink_voltage_info_t>> {};
    virtual auto get(nvlink_voltage_info_field) -> nscq_nvlink_voltage_info_t = 0;

    struct nvlink_current_info_field : public field<nvswitch_dev, readable<nscq_nvlink_current_info_t>> {};
    virtual auto get(nvlink_current_info_field) -> nscq_nvlink_current_info_t = 0;

    struct nvlink_error_field : public field<nvswitch_dev, readable<std::vector<nscq_link_error_t>>> {};
    virtual auto get(nvlink_error_field) -> std::vector<nscq_link_error_t> = 0;

    struct ecc_error_count_field : public field<nvswitch_dev, readable<nscq_ecc_error_count_t>> {};
    virtual auto get(ecc_error_count_field) -> nscq_ecc_error_count_t = 0;

    struct ecc_error_entry_field : public field<nvswitch_dev, readable<std::vector<nscq_ecc_error_entry_t>>> {};
    virtual auto get(ecc_error_entry_field) -> std::vector<nscq_ecc_error_entry_t> = 0;

    struct sxid_error_field : public field<nvswitch_dev, readable<std::vector<nscq_sxid_error_entry_t>>> {};
    virtual auto get(sxid_error_field) -> std::vector<nscq_sxid_error_entry_t> = 0;

    struct cmis_presence_field : public field<nvswitch_dev, readable<nscq_cci_raw_cmis_presence_t>> {};
    virtual auto get(cmis_presence_field) -> nscq_cci_raw_cmis_presence_t = 0;

    struct cmis_lane_mapping_field : public set_input_field<nvswitch_dev, readable<nscq_cci_raw_cmis_lane_mapping_t>> {
        cmis_lane_mapping_field(std::shared_ptr<nscq::cqi_session> &cqi_session)
            : set_input_field<nvswitch_dev, readable<nscq_cci_raw_cmis_lane_mapping_t>>(cqi_session) {}
    };
    virtual auto get(cmis_lane_mapping_field) -> nscq_cci_raw_cmis_lane_mapping_t = 0;
    virtual auto set_input(cmis_lane_mapping_field, uint32_t) -> void = 0;

    struct cmis_read_field : public set_input_field<nvswitch_dev, readable<nscq_cci_raw_cmis_read_t>> {
        cmis_read_field(std::shared_ptr<nscq::cqi_session> &cqi_session)
            : set_input_field<nvswitch_dev, readable<nscq_cci_raw_cmis_read_t>>(cqi_session) {}
    };
    virtual auto get(cmis_read_field) -> nscq_cci_raw_cmis_read_t = 0;
    virtual auto set_input(cmis_read_field, uint32_t) -> void = 0;

    struct num_osfp_field : public field<nvswitch_dev, readable<uint32_t>> {};
    virtual auto get(num_osfp_field) -> uint32_t = 0;

};

} // namespace nscq::cqi
