// Copyright (c) 2017-2018 Intel Corporation
// Copyright (c) 2018-2019 Huawei Corporation
//
// SPDX-License-Identifier: Apache-2.0
//

package drivers

import (
	"context"
	"encoding/hex"

	"github.com/kata-containers/kata-containers/src/runtime/pkg/device/api"
	"github.com/kata-containers/kata-containers/src/runtime/pkg/device/config"
	"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/utils"
)

// VhostUserNetDevice is a network vhost-user based device
type VhostUserNetDevice struct {
	*GenericDevice
	*config.VhostUserDeviceAttrs
}

//
// VhostUserNetDevice's implementation of the device interface:
//

// Attach is standard interface of api.Device, it's used to add device to some
// DeviceReceiver
func (device *VhostUserNetDevice) Attach(ctx context.Context, devReceiver api.DeviceReceiver) (err error) {
	skip, err := device.bumpAttachCount(true)
	if err != nil {
		return err
	}
	if skip {
		return nil
	}

	defer func() {
		if err != nil {
			device.bumpAttachCount(false)
		}
	}()

	// generate a unique ID to be used for hypervisor commandline fields
	randBytes, err := utils.GenerateRandomBytes(8)
	if err != nil {
		return err
	}
	id := hex.EncodeToString(randBytes)

	device.DevID = id
	device.Type = device.DeviceType()

	return devReceiver.AppendDevice(ctx, device)
}

// Detach is standard interface of api.Device, it's used to remove device from some
// DeviceReceiver
func (device *VhostUserNetDevice) Detach(ctx context.Context, devReceiver api.DeviceReceiver) error {
	_, err := device.bumpAttachCount(false)
	return err
}

// DeviceType is standard interface of api.Device, it returns device type
func (device *VhostUserNetDevice) DeviceType() config.DeviceType {
	return config.VhostUserNet
}

// GetDeviceInfo returns device information used for creating
func (device *VhostUserNetDevice) GetDeviceInfo() interface{} {
	device.Type = device.DeviceType()
	return device.VhostUserDeviceAttrs
}

// Save converts Device to DeviceState
func (device *VhostUserNetDevice) Save() config.DeviceState {
	ds := device.GenericDevice.Save()
	ds.Type = string(device.DeviceType())

	ds.VhostUserDev = device.VhostUserDeviceAttrs

	return ds
}

// Load loads DeviceState and converts it to specific device
func (device *VhostUserNetDevice) Load(ds config.DeviceState) {
	device.GenericDevice = &GenericDevice{}
	device.GenericDevice.Load(ds)

	device.VhostUserDeviceAttrs = ds.VhostUserDev
}

// It should implement GetAttachCount() and DeviceID() as api.Device implementation
// here it shares function from *GenericDevice so we don't need duplicate codes
