package ipmi

import "fmt"

// 5.4 Sensor Owner Identification
// the "owner" of the sensor.
// The combination of Sensor Owner ID and Sensor Number uniquely identify a sensor in the system.
// the Sensor Data Record and SEL information must contain information to identify the "owner" of the sensor.
//
// For management controllers, a Slave Address and LUN identify the owner of a sensor on the IPMB.
//
// For system software, a Software ID identifies the owner of a sensor.
//
// These fields are used in Event Messages, where events from management controllers or the IPMB are identified by an eight-bit field where the upper 7-bits are the Slave Address or System Software ID.
//
// The least significant bit is a 0 if the value represents a Slave Address and a 1 if the value represents a System Software ID.
// So all Software IDs are odd numbers (because the bit 0 is fixed to 1b),
// and all slave addresses are even numbers (because bit 0 is fixed to 0b)
//
// 5.5 Software IDs (SWIDs)
type SoftwareID uint8
type SoftwareType string

const (
	SoftwareTypeBIOS        SoftwareType = "BIOS"
	SoftwareTypeSMIHandler  SoftwareType = "SMI Handler"
	SoftwareTypeSMS         SoftwareType = "System Management Software"
	SoftwareTypeOEM         SoftwareType = "OEM"
	SoftwareTypeRCS         SoftwareType = "Remote Console Software"
	SoftwareTypeTerminalRCS SoftwareType = "Terminal Mode Remote Console Software"
	SoftwareTypeReserved    SoftwareType = "Reserved"
)

// Software IDs can be cla
func (i SoftwareID) Type() SoftwareType {
	if i >= SoftwareID(0x01) && i <= SoftwareID(0x1f) {
		return SoftwareTypeBIOS
	}
	if i >= SoftwareID(0x21) && i <= SoftwareID(0x3f) {
		return SoftwareTypeSMIHandler
	}
	if i >= SoftwareID(0x41) && i <= SoftwareID(0x5f) {
		return SoftwareTypeSMS
	}
	if i >= SoftwareID(0x61) && i <= SoftwareID(0x7f) {
		return SoftwareTypeOEM
	}
	if i >= SoftwareID(0x81) && i <= SoftwareID(0x8d) {
		return SoftwareTypeRCS
	}
	if i == SoftwareID(0x8f) {
		return SoftwareTypeTerminalRCS
	}
	return SoftwareTypeReserved
}

// GeneratorID is 2 bytes.
// the LSB represents: Slave Address (for IPMB) or Software ID (for system software);
// the MSB represents: Channel Number / LUN (for IPMB) or always 0 (for system software)
//
// In some scenario, the GeneratorID is used as 1 byte, because
// for IPMB, the Slave Address and LUN info are carried in IPMI Request/Response Messages;
// and for system software, the MSB is always 0, so only LSB is need.
//
// 32.1 SEL Event Records
//
//	Byte 1
//	[7:1] - 7-bit Slave Address, or 7-bit system software ID
//	[0] 0b = IPMB Slave Address, 1b = system software ID
//
//	Byte 2
//	[7:4] - Channel number. Channel that event message was received over. 0h if the
//	event message was received via the system interface, primary IPMB, or
//	internally generated by the BMC. (New for IPMI v1.5. These bits were reserved
//	in IPMI v1.0)
//	[3:2] - reserved. Write as 00b.
//	[1:0] - IPMB device LUN if byte 1 holds Slave Address. 00b otherwise
type GeneratorID uint16

const (
	GeneratorBMC             GeneratorID = 0x0020
	GeneratorBIOSPOST        GeneratorID = 0x0001
	GeneratorBIOSSMIHandler  GeneratorID = 0x0033
	GeneratorIntelNMFirmware GeneratorID = 0x002c // Node Manager
	GeneratorIntelMEFirmware GeneratorID = 0x602c // Management Engine

	// With Microsoft Windows Server* 2003 R2 and later versions, an Intelligent Platform Management Interface (IPMI) driver was added.
	// This added the capability of logging some operating system events to the SEL.
	GeneratorMicrosoftOS GeneratorID = 0x0041

	// The **Open IPMI driver** supports the ability to put semi-custom and custom events in the system event log if a panic occurs.
	GeneratorLinuxKernelPanic GeneratorID = 0x0021
)

func (g GeneratorID) String() string {
	return fmt.Sprintf("Owner: %#02x, Channel: %#02x, LUN: %#02x", g.OwnerID(), g.ChannelNumber(), g.LUN())
}

func (g GeneratorID) OwnerID() uint8 {
	return uint8(g & 0xff)
}

// OwnerIsSystemSoftware returns true if the owner is a system software, false means the owner is a IPMB slave address
func (g GeneratorID) OwnerIsSystemSoftware() bool {
	return g.OwnerID()&0x01 == 0x01
}

func (g GeneratorID) ChannelNumber() uint8 {
	return (uint8(g>>8) & 0xf0) >> 4
}

func (g GeneratorID) LUN() uint8 {
	return uint8(g>>8) & 0x03
}

// see: Intel System Event Log (SEL) Troubleshooting Guide Rev 3.4 September 2019 section 3.1
type SensorNumber uint8

const SensorNumberReserved = 0xff

type SDRMapBySensorNumber map[GeneratorID]map[SensorNumber]*SDR
