// 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 guestman

import (
	"fmt"
	"strconv"
	"strings"

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

	"yunion.io/x/onecloud/pkg/hostman/guestman/desc"
	"yunion.io/x/onecloud/pkg/hostman/guestman/qemu"
	"yunion.io/x/onecloud/pkg/hostman/monitor"
	"yunion.io/x/onecloud/pkg/hostman/options"
	"yunion.io/x/onecloud/pkg/scheduler/api"
	"yunion.io/x/onecloud/pkg/util/fileutils2"
)

func (s *SKVMGuestInstance) addPCIController(controllerType, bus desc.PCI_CONTROLLER_TYPE) *desc.PCIController {
	cont := desc.NewPCIController(controllerType)
	cont.PCIDevice = desc.NewPCIDevice(bus, "", "")
	if s.Desc.PCIControllers == nil {
		s.Desc.PCIControllers = make([]*desc.PCIController, 0)
	}
	s.Desc.PCIControllers = append(s.Desc.PCIControllers, cont)
	return cont
}

func (s *SKVMGuestInstance) pciControllerFind(cont desc.PCI_CONTROLLER_TYPE) *desc.PCIController {
	for i := 0; i < len(s.Desc.PCIControllers); i++ {
		if s.Desc.PCIControllers[i].CType == cont {
			return s.Desc.PCIControllers[i]
		}
	}
	return nil
}

func (s *SKVMGuestInstance) initGuestDesc() error {
	err := s.initCpuDesc(0)
	if err != nil {
		return err
	}
	err = s.initMemDesc(s.Desc.Mem)
	if err != nil {
		return errors.Wrap(err, "initMemDesc")
	}
	s.initMachineDesc()

	pciRoot, pciBridge := s.initGuestPciControllers(s.manager.host.IsKvmSupport())
	err = s.initGuestPciAddresses()
	if err != nil {
		return errors.Wrap(err, "init guest pci addresses")
	}

	err = s.initGuestDevicesDesc(pciRoot, pciBridge)
	if err != nil {
		return err
	}

	if err := s.initMachineDefaultAddresses(); err != nil {
		return errors.Wrap(err, "init machine default devices")
	}
	return s.ensurePciAddresses()
}

func (s *SKVMGuestInstance) initGuestDevicesDesc(pciRoot, pciBridge *desc.PCIController) error {
	// vdi device for spice
	s.Desc.VdiDevice = new(desc.SGuestVdi)
	if s.IsVdiSpice() {
		s.initSpiceDevices(pciRoot)
	}

	s.initVirtioSerial(pciRoot)
	s.initGuestVga(pciRoot)
	s.initCdromDesc()
	s.initFloppyDesc()
	s.initGuestDisks(pciRoot, pciBridge, false)
	if err := s.initGuestNetworks(pciRoot, pciBridge); err != nil {
		return errors.Wrap(err, "init guest networks")
	}

	s.initIsolatedDevices(pciRoot, pciBridge)
	s.initUsbController(pciRoot)
	s.initRandomDevice(pciRoot, options.HostOptions.EnableVirtioRngDevice)
	s.initQgaDesc()
	s.initPvpanicDesc()
	s.initIsaSerialDesc()
	s.initTpmDesc()
	return nil
}

func (s *SKVMGuestInstance) loadGuestPciAddresses() error {
	err := s.initGuestPciAddresses()
	if err != nil {
		return errors.Wrap(err, "init guest pci addresses")
	}

	if err := s.initMachineDefaultAddresses(); err != nil {
		return errors.Wrap(err, "init machine default devices")
	}
	err = s.ensurePciAddresses()
	if err != nil {
		return errors.Wrap(err, "load desc ensure pci address")
	}
	if err = SaveLiveDesc(s, s.Desc); err != nil {
		return errors.Wrap(err, "loadGuestPciAddresses save desc")
	}
	return nil
}

func (s *SKVMGuestInstance) initMachineDefaultAddresses() error {
	switch s.Desc.Machine {
	case "pc":
		I440FXSlot1 := &desc.PCIDevice{
			PCIAddr:    &desc.PCIAddr{Bus: 0, Slot: 1},
			Controller: desc.CONTROLLER_TYPE_PCI_ROOT,
		}
		// ISA bridge
		multiFunc := true
		if err := s.ensureDevicePciAddress(I440FXSlot1, 0, &multiFunc); err != nil {
			return err
		}
		// primary IDE controller
		if err := s.ensureDevicePciAddress(I440FXSlot1, 1, nil); err != nil {
			return err
		}
		// PIIX3 USB controller
		if err := s.ensureDevicePciAddress(I440FXSlot1, 2, nil); err != nil {
			return err
		}
		// ACPI (power management) and SMBus controller
		if err := s.ensureDevicePciAddress(I440FXSlot1, 3, nil); err != nil {
			return err
		}
	case "q35":
		Q35Slot31 := &desc.PCIDevice{
			PCIAddr:    &desc.PCIAddr{Bus: 0, Slot: 31},
			Controller: desc.CONTROLLER_TYPE_PCIE_ROOT,
		}
		// ISA bridge
		multiFunc := true
		if err := s.ensureDevicePciAddress(Q35Slot31, 0, &multiFunc); err != nil {
			return err
		}
		// primary SATA controller
		if err := s.ensureDevicePciAddress(Q35Slot31, 2, nil); err != nil {
			return err
		}
		// SMBus
		if err := s.ensureDevicePciAddress(Q35Slot31, 3, nil); err != nil {
			return err
		}
		// usb controllers
		Q35Slot29 := &desc.PCIDevice{
			PCIAddr:    &desc.PCIAddr{Bus: 0, Slot: 29},
			Controller: desc.CONTROLLER_TYPE_PCIE_ROOT,
		}
		multiFunc = true
		if err := s.ensureDevicePciAddress(Q35Slot29, 0, &multiFunc); err != nil {
			return err
		}
		if err := s.ensureDevicePciAddress(Q35Slot29, 1, nil); err != nil {
			return err
		}
		if err := s.ensureDevicePciAddress(Q35Slot29, 2, nil); err != nil {
			return err
		}
		if err := s.ensureDevicePciAddress(Q35Slot29, 7, nil); err != nil {
			return err
		}
	case "virt":
		// do nothing
	}
	return nil
}

func (s *SKVMGuestInstance) isMachineDefaultAddress(pciAddr *desc.PCIAddr) bool {
	switch s.Desc.Machine {
	case "pc":
		if pciAddr.Bus == 0 && pciAddr.Slot == 1 && pciAddr.Function < 4 {
			return true
		}
	case "q35":
		if pciAddr.Bus == 0 && pciAddr.Slot == 29 {
			switch pciAddr.Function {
			case 0, 1, 2, 7:
				return true
			}
		} else if pciAddr.Bus == 0 && pciAddr.Slot == 31 {
			switch pciAddr.Function {
			case 0, 2, 3:
				return true
			}
		}
	}
	return false
}

func (s *SKVMGuestInstance) initGuestPciControllers(pciExtend bool) (*desc.PCIController, *desc.PCIController) {
	var pciRoot, pciBridge *desc.PCIController
	if len(s.Desc.PCIControllers) > 0 {
		pciRoot = s.Desc.PCIControllers[0]
		if len(s.Desc.PCIControllers) > 1 {
			pciBridge = s.Desc.PCIControllers[1]
		}
		return pciRoot, pciBridge
	}
	if s.isPcie() {
		pciRoot = s.addPCIController(desc.CONTROLLER_TYPE_PCIE_ROOT, "")
		if pciExtend {
			s.addPCIController(desc.CONTROLLER_TYPE_PCIE_TO_PCI_BRIDGE, desc.CONTROLLER_TYPE_PCIE_ROOT)
			pciBridge = s.addPCIController(desc.CONTROLLER_TYPE_PCI_BRIDGE, desc.CONTROLLER_TYPE_PCIE_TO_PCI_BRIDGE)
			for i := 0; i < s.vfioDevCount()+options.HostOptions.PcieRootPortCount; i++ {
				s.addPCIController(desc.CONTROLLER_TYPE_PCIE_ROOT_PORT, desc.CONTROLLER_TYPE_PCIE_ROOT)
			}
		}
	} else {
		pciRoot = s.addPCIController(desc.CONTROLLER_TYPE_PCI_ROOT, "")
	}
	return pciRoot, pciBridge
}

func (s *SKVMGuestInstance) initGuestPciAddresses() error {
	s.pciAddrs = &desc.SGuestPCIAddresses{
		Buses: make([]*desc.SGuestPCIAddressBus, len(s.Desc.PCIControllers)),
	}
	var err error
	for i := 0; i < len(s.pciAddrs.Buses); i++ {
		s.pciAddrs.Buses[i], err = desc.NewGuestPCIAddressBus(s.Desc.PCIControllers[i].CType)
		if err != nil {
			return err
		}
	}
	return nil
}

func (s *SKVMGuestInstance) cleanGuestPciAddressed() {
	s.pciAddrs = nil
}

func (s *SKVMGuestInstance) initRandomDevice(pciRoot *desc.PCIController, enableVirtioRngDevice bool) {
	if !enableVirtioRngDevice {
		return
	}

	var randev string
	if fileutils2.Exists("/dev/urandom") {
		randev = "/dev/urandom"
	} else if fileutils2.Exists("/dev/random") {
		randev = "/dev/random"
	} else {
		return
	}

	s.Desc.Rng = &desc.SGuestRng{
		PCIDevice: desc.NewPCIDevice(pciRoot.CType, "virtio-rng-pci", "random0"),
		RngRandom: desc.NewObject("rng-random", "rng0"),
	}
	s.Desc.Rng.Options = map[string]string{
		"max-bytes": "1024",
		"period":    "1000",
	}
	s.Desc.Rng.RngRandom.Options = map[string]string{
		"filename": randev,
	}
}

func (s *SKVMGuestInstance) initUsbController(pciRoot *desc.PCIController) {
	contType := s.getUsbControllerType()
	s.Desc.Usb = &desc.UsbController{
		PCIDevice: desc.NewPCIDevice(pciRoot.CType, contType, "usb"),
	}
}

func (s *SKVMGuestInstance) initGuestNetworks(pciRoot, pciBridge *desc.PCIController) error {
	cont := pciRoot
	if pciBridge != nil {
		cont = pciBridge
	}

	if s.GetOsName() == OS_NAME_ANDROID {
		s.Desc.Nics = s.Desc.Nics[:1]
	}

	for i := 0; i < len(s.Desc.Nics); i++ {
		if err := s.generateNicScripts(s.Desc.Nics[i]); err != nil {
			return errors.Wrapf(err, "generateNicScripts for nic: %v", s.Desc.Nics[i])
		}
		s.Desc.Nics[i].UpscriptPath = s.getNicUpScriptPath(s.Desc.Nics[i])
		s.Desc.Nics[i].DownscriptPath = s.getNicDownScriptPath(s.Desc.Nics[i])

		if s.Desc.Nics[i].Driver != "vfio-pci" {
			switch s.GetOsName() {
			case OS_NAME_MACOS:
				vectors := 0
				s.Desc.Nics[i].Vectors = &vectors
				s.Desc.Nics[i].Driver = "e1000"
			case OS_NAME_VMWARE:
				s.Desc.Nics[i].Driver = "vmxnet3"
			}
			if s.Desc.Nics[i].NumQueues > 1 {
				vectors := s.Desc.Nics[i].NumQueues*2 + 1
				s.Desc.Nics[i].Vectors = &vectors
			}

			id := fmt.Sprintf("netdev-%s", s.Desc.Nics[i].Ifname)
			switch s.Desc.Nics[i].Driver {
			case "virtio":
				s.Desc.Nics[i].Pci = desc.NewPCIDevice(cont.CType, "virtio-net-pci", id)
			case "e1000":
				s.Desc.Nics[i].Pci = desc.NewPCIDevice(cont.CType, "e1000-82545em", id)
			case "vmxnet3":
				s.Desc.Nics[i].Pci = desc.NewPCIDevice(cont.CType, "vmxnet3", id)
			case "rtl8139":
				s.Desc.Nics[i].Pci = desc.NewPCIDevice(cont.CType, "rtl8139", id)
			}
		}
	}
	return nil
}

func (s *SKVMGuestInstance) initIsolatedDevices(pciRoot, pciBridge *desc.PCIController) {
	cType := s.getVfioDeviceHotPlugPciControllerType()
	if cType == nil {
		cType = &pciRoot.CType
		if pciBridge != nil {
			cType = &pciBridge.CType
		}
	}

	manager := s.manager.GetHost().GetIsolatedDeviceManager()
	for i := 0; i < len(s.Desc.IsolatedDevices); i++ {
		dev := manager.GetDeviceByAddr(s.Desc.IsolatedDevices[i].Addr)
		if s.Desc.IsolatedDevices[i].DevType == api.USB_TYPE {
			s.Desc.IsolatedDevices[i].Usb = desc.NewUsbDevice("usb-host", dev.GetQemuId())
			s.Desc.IsolatedDevices[i].Usb.Options = dev.GetPassthroughOptions()
		} else {
			id := dev.GetQemuId()
			s.Desc.IsolatedDevices[i].VfioDevs = make([]*desc.VFIODevice, 0)
			vfioDev := desc.NewVfioDevice(
				*cType, "vfio-pci", id, dev.GetAddr(), s.Desc.IsolatedDevices[i].DevType == api.GPU_VGA_TYPE,
			)
			s.Desc.IsolatedDevices[i].VfioDevs = append(s.Desc.IsolatedDevices[i].VfioDevs, vfioDev)

			groupDevAddrs := dev.GetIOMMUGroupRestAddrs()
			for j := 0; j < len(groupDevAddrs); j++ {
				gid := fmt.Sprintf("%s-%d", id, j+1)
				vfioDev = desc.NewVfioDevice(*cType, "vfio-pci", gid, groupDevAddrs[j], false)
				s.Desc.IsolatedDevices[i].VfioDevs = append(
					s.Desc.IsolatedDevices[i].VfioDevs, vfioDev,
				)
			}
		}
	}
}

func (s *SKVMGuestInstance) initCdromDesc() {
	if s.Desc.Cdroms == nil {
		s.Desc.Cdroms = make([]*desc.SGuestCdrom, options.HostOptions.CdromCount)
		for i := range s.Desc.Cdroms {
			s.Desc.Cdroms[i] = new(desc.SGuestCdrom)
			s.Desc.Cdroms[i].Ordinal = int64(i)
		}
	}
	for i := range s.Desc.Cdroms {
		s.archMan.GenerateCdromDesc(s.GetOsName(), s.Desc.Cdroms[i])
	}
}

func (s *SKVMGuestInstance) initFloppyDesc() {
	if s.Desc.Machine != "pc" || s.GetOsName() != OS_NAME_WINDOWS {
		return
	}
	if s.Desc.Floppys == nil {
		s.Desc.Floppys = make([]*desc.SGuestFloppy, options.HostOptions.FloppyCount)
		for i := range s.Desc.Floppys {
			s.Desc.Floppys[i] = new(desc.SGuestFloppy)
			s.Desc.Floppys[i].Ordinal = int64(i)
		}
	}
	for i := range s.Desc.Floppys {
		s.archMan.GenerateFloppyDesc(s.GetOsName(), s.Desc.Floppys[i])
	}
}

func (s *SKVMGuestInstance) initGuestDisks(pciRoot, pciBridge *desc.PCIController, loadGuest bool) {
	if !loadGuest {
		hasVirtioScsi, hasPvScsi, hasSataDisk := s.fixDiskDriver()
		if hasVirtioScsi && s.Desc.VirtioScsi == nil {
			s.Desc.VirtioScsi = &desc.SGuestVirtioScsi{
				PCIDevice: desc.NewPCIDevice(pciRoot.CType, "virtio-scsi-pci", "scsi"),
			}
		} else if hasPvScsi && s.Desc.PvScsi == nil {
			s.Desc.PvScsi = &desc.SGuestPvScsi{
				PCIDevice: desc.NewPCIDevice(pciRoot.CType, "pvscsi", "scsi"),
			}
		} else if hasSataDisk {
			s.Desc.SataController = &desc.SGuestAhciDevice{
				PCIDevice: desc.NewPCIDevice(pciRoot.CType, "ahci", "ahci0"),
			}
		}
	}

	cont := pciRoot
	if pciBridge != nil {
		cont = pciBridge
	}
	for i := 0; i < len(s.Desc.Disks); i++ {
		devType := qemu.GetDiskDeviceModel(s.Desc.Disks[i].Driver)
		id := fmt.Sprintf("drive_%d", s.Desc.Disks[i].Index)
		if s.Desc.Disks[i].Pci != nil || s.Desc.Disks[i].Scsi != nil {
			log.Infof("guest %s disk %v has been init", s.Desc.Uuid, s.Desc.Disks[i].Index)
			continue
		}

		switch s.Desc.Disks[i].Driver {
		case DISK_DRIVER_VIRTIO:
			if s.Desc.Disks[i].Pci == nil {
				s.Desc.Disks[i].Pci = desc.NewPCIDevice(cont.CType, devType, id)
			}
		case DISK_DRIVER_SCSI:
			if s.Desc.VirtioScsi == nil {
				s.Desc.VirtioScsi = &desc.SGuestVirtioScsi{
					PCIDevice: desc.NewPCIDevice(pciRoot.CType, "virtio-scsi-pci", "scsi"),
				}
			}
			s.Desc.Disks[i].Scsi = desc.NewScsiDevice(s.Desc.VirtioScsi.Id, devType, id)
		case DISK_DRIVER_PVSCSI:
			if s.Desc.PvScsi == nil {
				s.Desc.PvScsi = &desc.SGuestPvScsi{
					PCIDevice: desc.NewPCIDevice(pciRoot.CType, "pvscsi", "scsi"),
				}
			}
			s.Desc.Disks[i].Scsi = desc.NewScsiDevice(s.Desc.PvScsi.Id, devType, id)
		case DISK_DRIVER_IDE:
			s.Desc.Disks[i].Ide = desc.NewIdeDevice(devType, id)
		case DISK_DRIVER_SATA: // -device ahci,id=ahci pci device
			s.Desc.Disks[i].Ide = desc.NewIdeDevice(devType, id)
		}
	}
}

func (s *SKVMGuestInstance) fixDiskDriver() (bool, bool, bool) {
	var virtioScsi, pvScsi, sataDisk = false, false, false
	isX86 := s.manager.host.IsX8664()
	osname := s.GetOsName()

	for i := 0; i < len(s.Desc.Disks); i++ {
		if !isX86 && (s.Desc.Disks[i].Driver == DISK_DRIVER_IDE ||
			s.Desc.Disks[i].Driver == DISK_DRIVER_SATA) {
			s.Desc.Disks[i].Driver = DISK_DRIVER_SCSI
		} else if osname == OS_NAME_MACOS {
			s.Desc.Disks[i].Driver = DISK_DRIVER_SATA
		}

		switch s.Desc.Disks[i].Driver {
		case DISK_DRIVER_SCSI:
			virtioScsi = true
		case DISK_DRIVER_PVSCSI:
			pvScsi = true
		case DISK_DRIVER_SATA:
			sataDisk = true
		}
	}
	return virtioScsi, pvScsi, sataDisk
}

func (s *SKVMGuestInstance) initVirtioSerial(pciRoot *desc.PCIController) {
	s.Desc.VirtioSerial = new(desc.SGuestVirtioSerial)
	s.Desc.VirtioSerial.PCIDevice = desc.NewPCIDevice(pciRoot.CType, "virtio-serial-pci", "virtio-serial0")
}

func (s *SKVMGuestInstance) initGuestVga(pciRoot *desc.PCIController) {
	var isNotX86 = !s.manager.host.IsX8664()
	if s.gpusHasVga() {
		s.Desc.Vga = "none"
	} else if isNotX86 {
		s.Desc.Vga = "virtio-gpu"
	} else if s.Desc.Vga == "" {
		s.Desc.Vga = "std"
	}
	s.Desc.VgaDevice = new(desc.SGuestVga)

	var vgaDevName string
	var options map[string]string
	switch s.Desc.Vga {
	case "std":
		vgaDevName = "VGA"
	case "qxl":
		vgaDevName = "qxl-vga"
		options = map[string]string{
			"ram_size":  "141557760",
			"vram_size": "141557760",
		}
	case "cirros":
		vgaDevName = "cirrus-vga"
	case "vmware":
		vgaDevName = "vmware-svga"
	case "virtio":
		vgaDevName = "virtio-vga"
	case "virtio-gpu":
		vgaDevName = "virtio-gpu-pci"
		options = map[string]string{
			"max_outputs": "1",
		}
	case "", "none":
		vgaDevName = "none"
	}
	if vgaDevName != "none" {
		s.Desc.VgaDevice.PCIDevice = desc.NewPCIDevice(pciRoot.CType, vgaDevName, "video0")
		s.Desc.VgaDevice.PCIDevice.Options = options
	}
}

func (s *SKVMGuestInstance) initSpiceDevices(pciRoot *desc.PCIController) {
	spice := new(desc.SSpiceDesc)
	spice.IntelHDA = &desc.SoundCard{
		PCIDevice: desc.NewPCIDevice(pciRoot.CType, "intel-hda", "sound0"),
		Codec: &desc.Codec{
			Id:   "sound0-codec0",
			Type: "hda-duplex",
			Cad:  0,
		},
	}
	var ehciId = "usbspice"
	spice.UsbRedirct = &desc.UsbRedirctDesc{
		EHCI1: desc.NewUsbController("", -1),
		UHCI1: desc.NewUsbController(ehciId, 0),
		UHCI2: desc.NewUsbController(ehciId, 2),
		UHCI3: desc.NewUsbController(ehciId, 4),
	}
	spice.UsbRedirct.UsbRedirDev1 = &desc.UsbRedir{
		Id:     "usbredirdev1",
		Source: desc.NewCharDev("spicevmc", "usbredirchardev1", "usbredir"),
	}
	spice.UsbRedirct.UsbRedirDev2 = &desc.UsbRedir{
		Id:     "usbredirdev2",
		Source: desc.NewCharDev("spicevmc", "usbredirchardev2", "usbredir"),
	}
	spice.UsbRedirct.EHCI1.PCIDevice = desc.NewPCIDevice(pciRoot.CType, "ich9-usb-ehci1", ehciId)
	spice.UsbRedirct.UHCI1.PCIDevice = desc.NewPCIDevice(pciRoot.CType, "ich9-usb-uhci1", "uhci1")
	spice.UsbRedirct.UHCI2.PCIDevice = desc.NewPCIDevice(pciRoot.CType, "ich9-usb-uhci2", "uhci2")
	spice.UsbRedirct.UHCI3.PCIDevice = desc.NewPCIDevice(pciRoot.CType, "ich9-usb-uhci3", "uhci3")

	spice.VdagentSerial = &desc.SGuestVirtioSerial{
		PCIDevice: desc.NewPCIDevice(pciRoot.CType, "virtio-serial-pci", "vdagent-serial0"),
	}
	spice.Vdagent = desc.NewCharDev("spicevmc", "vdagent", "vdagent")
	spice.VdagentSerialPort = &desc.VirtSerialPort{
		Chardev: "vdagent",
		Name:    "com.redhat.spice.0",
		Options: map[string]string{
			"nr": "1",
		},
	}
	spice.Options = map[string]string{
		"disable-ticketing":  "off",
		"seamless-migration": "on",
	}

	s.Desc.VdiDevice.Spice = spice
}

func (s *SKVMGuestInstance) ensureDevicePciAddress(
	dev *desc.PCIDevice, function int, multiFunc *bool,
) error {
	if function >= 8 {
		return errors.Errorf("invalid function %x", function)
	} else if function < 0 {
		// if function not given, give them default function 0
		if dev.PCIAddr != nil {
			function = int(dev.Function)
		} else {
			function = 0
		}
	}

	if dev.PCIAddr != nil {
		dev.Function = uint(function)
		return s.pciAddrs.Buses[dev.Bus].EnsureSlotFunction(dev.Slot, dev.Function)
	}

	bus, slot, found := s.findUnusedSlotForController(dev.Controller, function)
	if !found {
		return errors.Errorf("no valid pci address found ?")
	}

	s.pciAddrs.Buses[bus].EnsureSlotFunction(uint(slot), uint(function))
	dev.PCIAddr = &desc.PCIAddr{
		Bus:      uint(bus),
		Slot:     uint(slot),
		Function: uint(function),
		Multi:    multiFunc,
	}
	return nil
}

func (s *SKVMGuestInstance) findUnusedSlotForController(cont desc.PCI_CONTROLLER_TYPE, function int) (int, int, bool) {
	var (
		bus, slot = 0, 0
		found     = false
	)
	for ; bus < len(s.pciAddrs.Buses); bus++ {
		if cont == s.pciAddrs.Buses[bus].Contorller {
			slot = s.pciAddrs.Buses[bus].FindNextUnusedSlot(uint(function))
			if slot >= 0 {
				found = true
				break
			}
		}
	}
	return bus, slot, found
}

func (s *SKVMGuestInstance) ensurePciAddresses() error {
	var err error
	if s.Desc.VgaDevice != nil && s.Desc.VgaDevice.PCIDevice != nil {
		err = s.ensureDevicePciAddress(s.Desc.VgaDevice.PCIDevice, -1, nil)
		if err != nil {
			return errors.Wrap(err, "ensure vga pci address")
		}
	}
	if s.Desc.VirtioSerial != nil {
		err = s.ensureDevicePciAddress(s.Desc.VirtioSerial.PCIDevice, -1, nil)
	}

	if s.Desc.VdiDevice != nil && s.Desc.VdiDevice.Spice != nil {
		err = s.ensureDevicePciAddress(s.Desc.VdiDevice.Spice.IntelHDA.PCIDevice, -1, nil)
		if err != nil {
			return errors.Wrap(err, "ensure vdi hda pci address")
		}

		err = s.ensureDevicePciAddress(s.Desc.VdiDevice.Spice.VdagentSerial.PCIDevice, -1, nil)
		if err != nil {
			return errors.Wrap(err, "ensure vdagent serial pci address")
		}

		multiFunc := true
		err = s.ensureDevicePciAddress(s.Desc.VdiDevice.Spice.UsbRedirct.UHCI1.PCIDevice, 0, &multiFunc)
		if err != nil {
			return errors.Wrap(err, "ensure vdi usb ehci1 pci address")
		}
		s.Desc.VdiDevice.Spice.UsbRedirct.UHCI2.PCIAddr = s.Desc.VdiDevice.Spice.UsbRedirct.UHCI1.PCIAddr.Copy()
		err = s.ensureDevicePciAddress(s.Desc.VdiDevice.Spice.UsbRedirct.UHCI2.PCIDevice, 1, &multiFunc)
		if err != nil {
			return errors.Wrap(err, "ensure vdi usb ehci1 pci address")
		}
		s.Desc.VdiDevice.Spice.UsbRedirct.UHCI3.PCIAddr = s.Desc.VdiDevice.Spice.UsbRedirct.UHCI1.PCIAddr.Copy()
		err = s.ensureDevicePciAddress(s.Desc.VdiDevice.Spice.UsbRedirct.UHCI3.PCIDevice, 2, &multiFunc)
		if err != nil {
			return errors.Wrap(err, "ensure vdi usb ehci1 pci address")
		}
		s.Desc.VdiDevice.Spice.UsbRedirct.EHCI1.PCIAddr = s.Desc.VdiDevice.Spice.UsbRedirct.UHCI1.PCIAddr.Copy()
		err = s.ensureDevicePciAddress(s.Desc.VdiDevice.Spice.UsbRedirct.EHCI1.PCIDevice, 7, &multiFunc)
		if err != nil {
			return errors.Wrap(err, "ensure vdi usb ehci1 pci address")
		}
	}

	if s.Desc.VirtioScsi != nil {
		err = s.ensureDevicePciAddress(s.Desc.VirtioScsi.PCIDevice, -1, nil)
		if err != nil {
			return errors.Wrap(err, "ensure virtio scsi pci address")
		}
	} else if s.Desc.PvScsi != nil {
		err = s.ensureDevicePciAddress(s.Desc.PvScsi.PCIDevice, -1, nil)
		if err != nil {
			return errors.Wrap(err, "ensure pvscsi pci address")
		}
	}
	if s.Desc.SataController != nil {
		err = s.ensureDevicePciAddress(s.Desc.SataController.PCIDevice, -1, nil)
		if err != nil {
			return errors.Wrap(err, "ensure SataController pci address")
		}
	}

	// skip pci root or pcie root
	for i := 1; i < len(s.Desc.PCIControllers); i++ {
		err = s.ensureDevicePciAddress(s.Desc.PCIControllers[i].PCIDevice, -1, nil)
		if err != nil {
			return errors.Wrap(err, "ensure pci controllers address")
		}
	}

	for i := 0; i < len(s.Desc.Disks); i++ {
		if s.Desc.Disks[i].Pci != nil {
			err = s.ensureDevicePciAddress(s.Desc.Disks[i].Pci, -1, nil)
			if err != nil {
				return errors.Wrapf(err, "ensure disk %d pci address", s.Desc.Disks[i].Index)
			}
		}
	}
	for i := 0; i < len(s.Desc.Nics); i++ {
		if s.Desc.Nics[i].Pci != nil {
			err = s.ensureDevicePciAddress(s.Desc.Nics[i].Pci, -1, nil)
			if err != nil {
				return errors.Wrapf(err, "ensure nic %s pci address", s.Desc.Nics[i].Ifname)
			}
		}
	}

	for i := 0; i < len(s.Desc.IsolatedDevices); i++ {
		if len(s.Desc.IsolatedDevices[i].VfioDevs) > 0 {
			multiFunc := len(s.Desc.IsolatedDevices[i].VfioDevs) > 1
			err = s.ensureDevicePciAddress(s.Desc.IsolatedDevices[i].VfioDevs[0].PCIDevice, 0, &multiFunc)
			if err != nil {
				return errors.Wrapf(err, "ensure isolated device %s pci address", s.Desc.IsolatedDevices[i].VfioDevs[0].PCIAddr)
			}
			for j := 1; j < len(s.Desc.IsolatedDevices[i].VfioDevs); j++ {
				s.Desc.IsolatedDevices[i].VfioDevs[j].PCIAddr = s.Desc.IsolatedDevices[i].VfioDevs[0].PCIAddr.Copy()
				err = s.ensureDevicePciAddress(s.Desc.IsolatedDevices[i].VfioDevs[j].PCIDevice, j, nil)
				if err != nil {
					return errors.Wrapf(err, "ensure isolated device %s pci address", s.Desc.IsolatedDevices[i].VfioDevs[j].PCIAddr)
				}
			}
		}
	}

	if s.Desc.Usb != nil {
		err = s.ensureDevicePciAddress(s.Desc.Usb.PCIDevice, -1, nil)
		if err != nil {
			return errors.Wrap(err, "ensure usb controller pci address")
		}
	}

	if s.Desc.Rng != nil {
		err = s.ensureDevicePciAddress(s.Desc.Rng.PCIDevice, -1, nil)
		if err != nil {
			return errors.Wrap(err, "ensure random device pci address")
		}
	}

	anonymousPCIDevs := s.Desc.AnonymousPCIDevs[:0]
	for i := 0; i < len(s.Desc.AnonymousPCIDevs); i++ {
		if s.isMachineDefaultAddress(s.Desc.AnonymousPCIDevs[i].PCIAddr) {
			if _, inUse := s.pciAddrs.IsAddrInUse(s.Desc.AnonymousPCIDevs[i].PCIAddr); inUse {
				log.Infof("guest %s anonymous dev addr %s in use", s.GetName(), s.Desc.AnonymousPCIDevs[i].String())
				continue
			}
		}
		err = s.ensureDevicePciAddress(s.Desc.AnonymousPCIDevs[i], -1, nil)
		if err != nil {
			return errors.Wrap(err, "ensure anonymous pci dev pci address")
		}
		anonymousPCIDevs = append(anonymousPCIDevs, s.Desc.AnonymousPCIDevs[i])
	}
	if len(anonymousPCIDevs) == 0 {
		anonymousPCIDevs = nil
	}
	s.Desc.AnonymousPCIDevs = anonymousPCIDevs
	return nil
}

func (s *SKVMGuestInstance) getNetdevOfThePciAddress(qtree string, addr *desc.PCIAddr) string {
	var slotFunc = fmt.Sprintf("addr = %02x.%x", addr.Slot, addr.Function)
	var addressFound = false
	var lines = strings.Split(strings.TrimSuffix(qtree, "\r\n"), "\\r\\n")
	var currentIndentLevel = -1
	for _, line := range lines {
		trimmedLine := strings.TrimSpace(line)
		if trimmedLine == "" {
			continue
		}

		if currentIndentLevel > 0 {
			newIndentLevel := len(line) - len(trimmedLine)
			if newIndentLevel <= currentIndentLevel {
				if addressFound {
					break
				}

				currentIndentLevel = -1
				continue
			}

			if strings.HasPrefix(trimmedLine, slotFunc) {
				addressFound = true
				continue
			}

			if strings.HasPrefix(trimmedLine, "netdev =") {
				segs := strings.Split(trimmedLine, " ")
				netdev := strings.Trim(segs[2], `\\"`)
				log.Infof("found netdev %s: %s", netdev, trimmedLine)
				return netdev
			} else {
				continue
			}
		}

		if strings.HasPrefix(trimmedLine, "dev: virtio-net-pci") {
			currentIndentLevel = len(line) - len(trimmedLine)
		} else {
			continue
		}
	}
	return ""
}

// guests description no pci description before host-agent assign pci device address info
// in this case wo need query pci address info by `query-pci` command. Also memory devices.
func (s *SKVMGuestInstance) initGuestDescFromExistingGuest(
	cpuList []monitor.HotpluggableCPU, pciInfoList []monitor.PCIInfo,
	memoryDevicesInfoList []monitor.MemoryDeviceInfo, memDevs []monitor.Memdev,
	scsiNumQueues int64, qtree string,
) error {
	if len(pciInfoList) > 1 {
		return errors.Errorf("unsupported pci info list with multi bus")
	}
	unknownDevices := make([]monitor.PCIDeviceInfo, 0)

	err := s.initCpuDesc(uint(len(cpuList)))
	if err != nil {
		return err
	}
	err = s.initMemDescFromMemoryInfo(memoryDevicesInfoList, memDevs)
	if err != nil {
		return errors.Wrap(err, "init guest memory devices")
	}
	s.initMachineDesc()
	if s.manager.host.IsX8664() && !s.hasHpet(qtree) {
		noHpet := true
		s.Desc.NoHpet = &noHpet
	}

	// This code is designed to ensure compatibility with older guests.
	// However, it is not recommended for new guests to generate a desc file from it
	pciRoot, _ := s.initGuestPciControllers(false)
	err = s.initGuestPciAddresses()
	if err != nil {
		return errors.Wrap(err, "init guest pci addresses")
	}

	if err := s.initGuestNetworks(pciRoot, nil); err != nil {
		return errors.Wrap(err, "init guest networks")
	}
	//s.initIsolatedDevices(pciRoot, nil)
	s.initQgaDesc()
	s.initPvpanicDesc()
	s.initIsaSerialDesc()
	s.Desc.VdiDevice = new(desc.SGuestVdi)

	for i := 0; i < len(pciInfoList[0].Devices); i++ {
		pciAddr := &desc.PCIAddr{
			Bus:      uint(pciInfoList[0].Devices[i].Bus),
			Slot:     uint(pciInfoList[0].Devices[i].Slot),
			Function: uint(pciInfoList[0].Devices[i].Function),
		}
		switch pciInfoList[0].Devices[i].QdevID {
		case "scsi":
			_, hasPvScsi, _ := s.fixDiskDriver()
			if hasPvScsi && s.Desc.PvScsi == nil {
				s.Desc.PvScsi = &desc.SGuestPvScsi{
					PCIDevice: desc.NewPCIDevice(pciRoot.CType, "pvscsi", "scsi"),
				}
			} else if s.Desc.VirtioScsi == nil {
				s.Desc.VirtioScsi = &desc.SGuestVirtioScsi{
					PCIDevice: desc.NewPCIDevice(pciRoot.CType, "virtio-scsi-pci", "scsi"),
				}
			}

			if s.Desc.VirtioScsi != nil {
				s.Desc.VirtioScsi.PCIAddr = pciAddr
				err = s.ensureDevicePciAddress(s.Desc.VirtioScsi.PCIDevice, -1, nil)
				if err != nil {
					return errors.Wrap(err, "ensure virtio scsi pci address")
				}
				if scsiNumQueues > 1 {
					numQueues := uint8(scsiNumQueues)
					s.Desc.VirtioScsi.NumQueues = &numQueues
				}
			} else if s.Desc.PvScsi != nil {
				s.Desc.PvScsi.PCIAddr = pciAddr
				err = s.ensureDevicePciAddress(s.Desc.PvScsi.PCIDevice, -1, nil)
				if err != nil {
					return errors.Wrap(err, "ensure pvscsi pci address")
				}
			}
		case "video0", "video1":
			if s.Desc.VgaDevice == nil {
				s.initGuestVga(pciRoot)
			}

			s.Desc.VgaDevice.PCIAddr = pciAddr
			err = s.ensureDevicePciAddress(s.Desc.VgaDevice.PCIDevice, -1, nil)
			if err != nil {
				return errors.Wrap(err, "ensure vga pci address")
			}
		case "random0":
			if s.Desc.Rng == nil {
				// in case rng device disable by host options
				s.initRandomDevice(pciRoot, true)
			}
			s.Desc.Rng.PCIAddr = pciAddr
			err = s.ensureDevicePciAddress(s.Desc.Rng.PCIDevice, -1, nil)
			if err != nil {
				return errors.Wrap(err, "ensure random device pci address")
			}
		case "usb":
			if s.Desc.Usb == nil {
				s.initUsbController(pciRoot)
			}

			s.Desc.Usb.PCIAddr = pciAddr
			err = s.ensureDevicePciAddress(s.Desc.Usb.PCIDevice, -1, nil)
			if err != nil {
				return errors.Wrap(err, "ensure usb controller pci address")
			}
		case "sound0":
			if s.Desc.VdiDevice.Spice == nil {
				s.Desc.Vdi = "spice"
				s.initSpiceDevices(pciRoot)
			}
			s.Desc.VdiDevice.Spice.IntelHDA.PCIAddr = pciAddr
			err = s.ensureDevicePciAddress(s.Desc.VdiDevice.Spice.IntelHDA.PCIDevice, -1, nil)
			if err != nil {
				return errors.Wrap(err, "ensure vdi hda pci address")
			}
		case "vdagent-serial0":
			if s.Desc.VdiDevice.Spice == nil {
				s.Desc.Vdi = "spice"
				s.initSpiceDevices(pciRoot)
			}
			s.Desc.VdiDevice.Spice.VdagentSerial.PCIAddr = pciAddr
			err = s.ensureDevicePciAddress(s.Desc.VdiDevice.Spice.VdagentSerial.PCIDevice, -1, nil)
			if err != nil {
				return errors.Wrap(err, "ensure vdagent serial pci address")
			}
		case "virtio-serial0":
			if s.Desc.VirtioSerial == nil {
				s.initVirtioSerial(pciRoot)
			}

			s.Desc.VirtioSerial.PCIAddr = pciAddr
			err = s.ensureDevicePciAddress(s.Desc.VirtioSerial.PCIDevice, -1, nil)
			if err != nil {
				return errors.Wrap(err, "ensure virtio serial address")
			}
		case "usbspice":
			if s.Desc.VdiDevice.Spice == nil {
				s.Desc.Vdi = "spice"
				s.initSpiceDevices(pciRoot)
			}
			s.Desc.VdiDevice.Spice.UsbRedirct.EHCI1.PCIAddr = pciAddr
			multiFunc := true
			err = s.ensureDevicePciAddress(s.Desc.VdiDevice.Spice.UsbRedirct.EHCI1.PCIDevice, -1, &multiFunc)
			if err != nil {
				return errors.Wrap(err, "ensure vdi usb ehci1 pci address")
			}
		case "uhci1":
			if s.Desc.VdiDevice.Spice == nil {
				s.Desc.Vdi = "spice"
				s.initSpiceDevices(pciRoot)
			}
			multiFunc := true
			s.Desc.VdiDevice.Spice.UsbRedirct.UHCI1.PCIAddr = pciAddr
			err = s.ensureDevicePciAddress(s.Desc.VdiDevice.Spice.UsbRedirct.UHCI1.PCIDevice, -1, &multiFunc)
			if err != nil {
				return errors.Wrap(err, "ensure vdi usb uhci1 pci address")
			}
		case "uhci2":
			if s.Desc.VdiDevice.Spice == nil {
				s.Desc.Vdi = "spice"
				s.initSpiceDevices(pciRoot)
			}
			multiFunc := true
			s.Desc.VdiDevice.Spice.UsbRedirct.UHCI2.PCIAddr = pciAddr
			err = s.ensureDevicePciAddress(s.Desc.VdiDevice.Spice.UsbRedirct.UHCI2.PCIDevice, -1, &multiFunc)
			if err != nil {
				return errors.Wrap(err, "ensure vdi usb uhci2 pci address")
			}
		case "uhci3":
			if s.Desc.VdiDevice.Spice == nil {
				s.Desc.Vdi = "spice"
				s.initSpiceDevices(pciRoot)
			}
			multiFunc := true
			s.Desc.VdiDevice.Spice.UsbRedirct.UHCI3.PCIAddr = pciAddr
			err = s.ensureDevicePciAddress(s.Desc.VdiDevice.Spice.UsbRedirct.UHCI3.PCIDevice, -1, &multiFunc)
			if err != nil {
				return errors.Wrap(err, "ensure vdi usb uhci3 pci address")
			}
		case "ahci0":
			if s.Desc.SataController == nil {
				s.Desc.SataController = &desc.SGuestAhciDevice{
					PCIDevice: desc.NewPCIDevice(pciRoot.CType, "ahci", "ahci0"),
				}
			}

			s.Desc.SataController.PCIAddr = pciAddr
			err = s.ensureDevicePciAddress(s.Desc.SataController.PCIDevice, -1, nil)
			if err != nil {
				return errors.Wrap(err, "ensure SataController pci address")
			}
		default:
			switch {
			case strings.HasPrefix(pciInfoList[0].Devices[i].QdevID, "drive_"):
				indexStr := strings.TrimPrefix(pciInfoList[0].Devices[i].QdevID, "drive_")
				index, err := strconv.Atoi(indexStr)
				if err != nil {
					log.Errorf("failed parse disk pci id %s", pciInfoList[0].Devices[i].QdevID)
					unknownDevices = append(unknownDevices, pciInfoList[0].Devices[i])
					continue
				}
				for i := 0; i < len(s.Desc.Disks); i++ {
					if s.Desc.Disks[i].Index == int8(index) {
						if s.Desc.Disks[i].Pci == nil {
							devType := qemu.GetDiskDeviceModel(DISK_DRIVER_VIRTIO)
							s.Desc.Disks[i].Pci = desc.NewPCIDevice(pciRoot.CType, devType, pciInfoList[0].Devices[i].QdevID)
							s.Desc.Disks[i].Scsi = nil
							s.Desc.Disks[i].Ide = nil
							s.Desc.Disks[i].Driver = DISK_DRIVER_VIRTIO
						}
						s.Desc.Disks[i].Pci.PCIAddr = pciAddr
						err = s.ensureDevicePciAddress(s.Desc.Disks[i].Pci, -1, nil)
						if err != nil {
							return errors.Wrapf(err, "ensure disk %d pci address", s.Desc.Disks[i].Index)
						}
					}
				}
			case strings.HasPrefix(pciInfoList[0].Devices[i].QdevID, "netdev-"):
				ifname := strings.TrimPrefix(pciInfoList[0].Devices[i].QdevID, "netdev-")
				for i := 0; i < len(s.Desc.Nics); i++ {
					if s.Desc.Nics[i].Ifname == ifname {
						s.Desc.Nics[i].Pci.PCIAddr = pciAddr
						err = s.ensureDevicePciAddress(s.Desc.Nics[i].Pci, -1, nil)
						if err != nil {
							return errors.Wrapf(err, "ensure nic %s pci address", s.Desc.Nics[i].Ifname)
						}
					}
				}
			default:
				class := pciInfoList[0].Devices[i].ClassInfo.Class
				vendor := pciInfoList[0].Devices[i].ID.Vendor
				device := pciInfoList[0].Devices[i].ID.Device
				switch { // qemu: docs/specs/pci-ids.txt
				case class == 3075 && vendor == 6966 && device == 13: // { 0x0c03, "USB controller", "usb"}, 1b36:000d PCI xhci usb host adapter
					if s.Desc.Usb == nil {
						s.initUsbController(pciRoot)
					}

					s.Desc.Usb.PCIAddr = pciAddr
					err = s.ensureDevicePciAddress(s.Desc.Usb.PCIDevice, -1, nil)
					if err != nil {
						return errors.Wrap(err, "ensure usb controller pci address")
					}
				case class == 255 && vendor == 6900 && device == 4101: // 0x00ff, 1af4:1005  entropy generator device (legacy)
					if s.Desc.Rng == nil {
						// in case rng device disable by host options
						s.initRandomDevice(pciRoot, true)
					}

					s.Desc.Rng.PCIAddr = pciAddr
					err = s.ensureDevicePciAddress(s.Desc.Rng.PCIDevice, -1, nil)
					if err != nil {
						return errors.Wrap(err, "ensure random device pci address")
					}
				case class == 1920 && vendor == 6900 && device == 4099: // 0x0780, 1af4:1003  console device (legacy)
					if s.Desc.VirtioSerial == nil {
						s.initVirtioSerial(pciRoot)
					}

					s.Desc.VirtioSerial.PCIAddr = pciAddr
					err = s.ensureDevicePciAddress(s.Desc.VirtioSerial.PCIDevice, -1, nil)
					if err != nil {
						return errors.Wrap(err, "ensure virtio serial address")
					}
				case class == 768 && vendor == 4660 && device == 4369: // { 0x0300, "VGA controller", "display", 0x00ff}, PCI ID: 1234:1111
					if s.Desc.VgaDevice == nil {
						s.initGuestVga(pciRoot)
					}

					s.Desc.VgaDevice.PCIAddr = pciAddr
					err = s.ensureDevicePciAddress(s.Desc.VgaDevice.PCIDevice, -1, nil)
					if err != nil {
						return errors.Wrap(err, "ensure vga pci address")
					}
				case class == 512 && vendor == 6900 && device == 4096: // { 0x0200, "Ethernet controller", "ethernet"}, 1af4:1000  network device (legacy)
					// virtio nics has no ids
					ifname := s.getNetdevOfThePciAddress(qtree, pciAddr)

					index := 0
					for ; index < len(s.Desc.Nics); index++ {
						if s.Desc.Nics[index].Ifname == ifname {
							s.Desc.Nics[index].Pci.PCIAddr = pciAddr
							err = s.ensureDevicePciAddress(s.Desc.Nics[index].Pci, -1, nil)
							if err != nil {
								return errors.Wrapf(err, "ensure nic %s pci address", s.Desc.Nics[index].Ifname)
							}
							break
						}
					}
					if index >= len(s.Desc.Nics) {
						return errors.Errorf("failed find nics ifname")
					}
				default:
					unknownDevices = append(unknownDevices, pciInfoList[0].Devices[i])
				}
			}
		}
	}

	s.initGuestDisks(pciRoot, nil, true)

	for i := 0; i < len(unknownDevices); i++ {
		if unknownDevices[i].Bus == 0 && unknownDevices[i].Slot == 0 {
			continue // host bridge
		}
		pciDev := desc.NewPCIDevice(pciRoot.CType, "", unknownDevices[i].QdevID)
		pciDev.PCIAddr = &desc.PCIAddr{
			Bus:      uint(unknownDevices[i].Bus),
			Slot:     uint(unknownDevices[i].Slot),
			Function: uint(unknownDevices[i].Function),
		}
		err = s.ensureDevicePciAddress(pciDev, -1, nil)
		if err != nil {
			return errors.Wrap(err, "ensure anonymous pci dev address")
		}
		if s.isMachineDefaultAddress(pciDev.PCIAddr) {
			continue
		}
		if s.Desc.AnonymousPCIDevs == nil {
			s.Desc.AnonymousPCIDevs = make([]*desc.PCIDevice, 0)
		}
		s.Desc.AnonymousPCIDevs = append(s.Desc.AnonymousPCIDevs, pciDev)
	}
	return nil
}
