// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package compute

import (
	"fmt"

	"yunion.io/x/jsonutils"
	"yunion.io/x/pkg/errors"

	"yunion.io/x/onecloud/pkg/apis"
)

const (
	ErrMsgIsolatedDeviceUsedByServer = "Isolated device used by server"
)

type IsolateDeviceDetails struct {
	apis.StandaloneResourceDetails
	apis.SharableResourceBaseInfo
	HostResourceInfo

	SIsolatedDevice

	// 云主机名称
	Guest string `json:"guest"`
	// 云主机状态
	GuestStatus string `json:"guest_status"`
}

type IsolatedDeviceListInput struct {
	apis.StandaloneResourceListInput
	apis.ExternalizedResourceBaseListInput
	apis.DomainizedResourceListInput

	HostFilterListInput

	// 只列出GPU直通设备
	Gpu *bool `json:"gpu"`
	// 只列出USB直通设备
	Usb *bool `json:"usb"`
	// 只列出未使用的直通设备
	Unused *bool `json:"unused"`

	// # PCI / GPU-HPC / GPU-VGA / USB / NIC
	// 设备类型
	DevType []string `json:"dev_type"`

	// # Specific device name read from lspci command, e.g. `Tesla K40m` ...
	Model []string `json:"model"`

	// # pci address of `Bus:Device.Function` format, or usb bus address of `bus.addr`
	Addr []string `json:"addr"`

	// 设备路径
	DevicePath []string `json:"device_path"`

	// 设备VENDOE编号
	VendorDeviceId []string `json:"vendor_device_id"`

	// NUMA节点序号
	NumaNode []uint8 `json:"numa_node"`

	// 展示物理机的上的设备
	ShowBaremetalIsolatedDevices bool `json:"show_baremetal_isolated_devices"`

	// 列出虚拟机上挂载的设备
	GuestId string `json:"guest_id"`

	// GPU index
	Index *int `json:"index"`
	// Nvidia GPU minor number, parsing from /proc/driver/nvidia/gpus/*/information
	DeviceMinor *int `json:"device_minor"`
}

type IsolatedDeviceCreateInput struct {
	apis.StandaloneResourceCreateInput

	HostResourceInput
	IsolatedDeviceReservedResourceInput

	// 设备类型USB/GPU
	// example: GPU
	DevType string `json:"dev_type"`

	// 设备型号
	// # Specific device name read from lspci command, e.g. `Tesla K40m` ...
	Model string `json:"model"`

	// PCI地址
	// # pci address of `Bus:Device.Function` format, or usb bus address of `bus.addr`
	Addr string `json:"addr"`

	// legacy vgpu mdev id
	MdevId string `json:"mdev_id"`

	// 设备VendorId
	VendorDeviceId string `json:"vendor_device_id"`
	// PCIE information
	PCIEInfo *IsolatedDevicePCIEInfo `json:"pcie_info"`
	// Host device path
	DevicePath string `json:"device_path"`
}

type IsolatedDeviceReservedResourceInput struct {
	// GPU 预留内存
	ReservedMemory *int `json:"reserved_memory"`

	// GPU 预留CPU
	ReservedCpu *int `json:"reserved_cpu"`

	// GPU 预留磁盘
	ReservedStorage *int `json:"reserved_storage"`
}

type IsolatedDeviceUpdateInput struct {
	apis.StandaloneResourceBaseUpdateInput
	IsolatedDeviceReservedResourceInput
	DevType string `json:"dev_type"`
	// PCIE information
	PCIEInfo *IsolatedDevicePCIEInfo `json:"pcie_info"`
	// Host device path
	DevicePath  string `json:"device_path"`
	Index       int    `json:"index"`
	DeviceMinor int    `json:"device_minor"`
}

type IsolatedDeviceJsonDesc struct {
	Id                  string `json:"id"`
	DevType             string `json:"dev_type"`
	Model               string `json:"model"`
	Addr                string `json:"addr"`
	VendorDeviceId      string `json:"vendor_device_id"`
	Vendor              string `json:"vendor"`
	NetworkIndex        int    `json:"network_index"`
	IsInfinibandNic     bool   `json:"is_infiniband_nic"`
	OvsOffloadInterface string `json:"ovs_offload_interface"`
	DiskIndex           int8   `json:"disk_index"`
	NvmeSizeMB          int    `json:"nvme_size_mb"`
	MdevId              string `json:"mdev_id"`
	NumaNode            int8   `json:"numa_node"`
}

type IsolatedDeviceModelCreateInput struct {
	apis.StandaloneAnonResourceCreateInput

	// 设备类型
	// example: NPU
	DevType string `json:"dev_type"`

	// 设备型号
	Model string `json:"model"`

	// 设备VendorId
	VendorId string `json:"vendor_id"`

	// 设备DeviceId
	DeviceId string `json:"device_id"`

	// 支持热插拔 HotPluggable
	HotPluggable bool `json:"hot_pluggable"`

	// hosts scan isolated device after isolated_device_model created
	Hosts []string `json:"hosts"`
}

type IsolatedDeviceModelUpdateInput struct {
	apis.StandaloneAnonResourceBaseUpdateInput
	// 设备类型
	// example: NPU
	DevType string `json:"dev_type"`

	// 设备型号
	Model string `json:"model"`

	// 设备VendorId
	VendorId string `json:"vendor_id"`

	// 设备DeviceId
	DeviceId string `json:"device_id"`

	// 支持热插拔 HotPluggable
	HotPluggable bool `json:"hot_pluggable"`
}

type IsolatedDeviceModelListInput struct {
	apis.StandaloneAnonResourceListInput

	// 设备类型
	// example: NPU
	DevType []string `json:"dev_type"`

	// 设备型号
	Model []string `json:"model"`

	// 设备VendorId
	VendorId string `json:"vendor_id"`

	// 设备DeviceId
	DeviceId string `json:"device_id"`

	// 支持热插拔 HotPluggable
	HotPluggable bool `json:"hot_pluggable"`

	// 宿主机 Id
	HostId string `json:"host_id"`
}

type IsolatedDeviceModelHardwareInfo struct {
	// GPU memory size
	MemoryMB int `json:"memory_mb" help:"Memory size MB of the device"`
	// GPU bandwidth. The unit is GB/s
	Bandwidth float64 `json:"bandwidth" help:"Bandwidth of the device, and the unit is GB/s"`
	// TFLOPS stands for number of floating point operations per second.
	TFLOPS float64 `json:"tflops" help:"TFLOPS of the device, which standing for number of floating point operations per second"`
}

type IsolatedDevicePCIEInfo struct {
	// Transder rate per lane
	// Transfer rate refers to the encoded serial bit rate; 2.5 GT/s means 2.5 Gbit/s serial data rate.
	TransferRatePerLane string `json:"transfer_rate_per_lane"`
	// Lane width
	LaneWidth int `json:"lane_width,omitzero"`

	// The following attributes are calculated by TransferRatePerLane and LaneWidth

	// Throughput indicates the unencoded bandwidth (without 8b/10b, 128b/130b, or 242B/256B encoding overhead).
	// The PCIe 1.0 transfer rate of 2.5 GT/s per lane means a 2.5 Gbit/s serial bit rate corresponding to a throughput of 2.0 Gbit/s or 250 MB/s prior to 8b/10b encoding.
	Throughput string `json:"throughput"`
	// Version is the PCIE version
	Version string `json:"version"`
}

func NewIsolatedDevicePCIEInfo(transferRate string, laneWidth int) (*IsolatedDevicePCIEInfo, error) {
	info := &IsolatedDevicePCIEInfo{
		TransferRatePerLane: transferRate,
		LaneWidth:           laneWidth,
	}
	if err := info.fillData(); err != nil {
		return info, errors.Wrap(err, "fillData")
	}
	return info, nil
}

func (info *IsolatedDevicePCIEInfo) String() string {
	return jsonutils.Marshal(info).String()
}

func (info *IsolatedDevicePCIEInfo) IsZero() bool {
	if *info == (IsolatedDevicePCIEInfo{}) {
		return true
	}
	return false
}

const (
	PCIEVersion1       = "1.0"
	PCIEVersion2       = "2.0"
	PCIEVersion3       = "3.0"
	PCIEVersion4       = "4.0"
	PCIEVersion5       = "5.0"
	PCIEVersion6       = "6.0"
	PCIEVersion7       = "7.0"
	PCIEVersionUnknown = "unknown"
)

type PCIEVersionThroughput struct {
	Version    string  `json:"version"`
	Throughput float64 `json:"throughput"`
}

func NewPCIEVersionThroughput(version string) PCIEVersionThroughput {
	// FROM: https://en.wikipedia.org/wiki/PCI_Express
	var (
		v1 = 0.25
		v2 = 0.5
		v3 = 0.985
		v4 = 1.969
		v5 = 3.938
		v6 = 7.563
		v7 = 15.125
	)
	table := map[string]float64{
		PCIEVersion1: v1,
		PCIEVersion2: v2,
		PCIEVersion3: v3,
		PCIEVersion4: v4,
		PCIEVersion5: v5,
		PCIEVersion6: v6,
		PCIEVersion7: v7,
	}
	tp, ok := table[version]
	if ok {
		return PCIEVersionThroughput{
			Version:    version,
			Throughput: tp,
		}
	}
	return PCIEVersionThroughput{
		Version:    PCIEVersionUnknown,
		Throughput: -1,
	}
}

func (info *IsolatedDevicePCIEInfo) fillData() error {
	vTp := info.GetThroughputPerLane()
	info.Version = vTp.Version
	info.Throughput = fmt.Sprintf("%.2f GB/s", vTp.Throughput*float64(info.LaneWidth))
	return nil
}

func (info IsolatedDevicePCIEInfo) GetThroughputPerLane() PCIEVersionThroughput {
	table := map[string]PCIEVersionThroughput{
		// version 1.0: 2003
		"2.5": NewPCIEVersionThroughput(PCIEVersion1),
		// version 2.0: 2007
		"5":   NewPCIEVersionThroughput(PCIEVersion2),
		"5.0": NewPCIEVersionThroughput(PCIEVersion2),
		// version 3.0: 2010
		"8":   NewPCIEVersionThroughput(PCIEVersion3),
		"8.0": NewPCIEVersionThroughput(PCIEVersion3),
		// version 4.0: 2017
		"16":   NewPCIEVersionThroughput(PCIEVersion4),
		"16.0": NewPCIEVersionThroughput(PCIEVersion4),
		// version 5.0: 2019
		"32":   NewPCIEVersionThroughput(PCIEVersion5),
		"32.0": NewPCIEVersionThroughput(PCIEVersion5),
		// version 6.0: 2022
		"64":   NewPCIEVersionThroughput(PCIEVersion6),
		"64.0": NewPCIEVersionThroughput(PCIEVersion6),
		// version 7.0: 2025(planned)
		"128":   NewPCIEVersionThroughput(PCIEVersion7),
		"128.0": NewPCIEVersionThroughput(PCIEVersion7),
	}
	for key, val := range table {
		if fmt.Sprintf("%sGT/s", key) == info.TransferRatePerLane {
			return val
		}
	}
	return NewPCIEVersionThroughput(PCIEVersionUnknown)
}

type HostIsolatedDeviceModelDetails struct {
	SHostJointsBase
	HostJointResourceDetails
	// 宿主机Id
	HostId string `json:"host_id"`
	// 存储Id
	IsolatedDeviceModelId string `json:"isolated_device_model_id"`

	Model             string `json:"model"`
	VendorId          string `json:"vendor_id"`
	DeviceId          string `json:"device_id"`
	DevType           string `json:"dev_type"`
	HotPluggable      bool   `json:"hot_pluggable"`
	DisableAutoDetect bool   `json:"disable_auto_detect"`
}
