// Package proto contains the low-level Channel Access wire-protocol constants,
// header codec, and DBR type decode functions.  All types are big-endian as
// required by the CA specification.
package proto

// CA command opcodes (uint16, first field of every message header).
const (
	CmdVersion      = 0  // version negotiation (first message on every TCP connection)
	CmdEventAdd     = 1  // subscribe to value updates / server event push
	CmdEventCancel  = 2  // cancel subscription
	CmdWrite        = 4  // put value (fire-and-forget, no server reply)
	CmdSearch       = 6  // UDP: locate IOC hosting a PV
	CmdNotFound     = 14 // UDP: IOC does not host the requested PV
	CmdReadNotify   = 15 // get value with callback (async GET)
	CmdCreateChan   = 18 // create channel on TCP circuit
	CmdWriteNotify  = 19 // put value with acknowledgement
	CmdClientName   = 20 // announce client username (sent after VERSION)
	CmdHostName     = 21 // announce client hostname  (sent after VERSION)
	CmdAccessRights = 22 // server → client: access rights bitmask
	CmdEcho         = 23 // heartbeat ping/pong
	CmdCreateFail   = 26 // server → client: CREATE_CHAN failed
	CmdServerDisc   = 27 // server → client: server shutting down
)

// CA minor protocol revision announced during the VERSION handshake.
// Corresponds to CA 4.13, supported by EPICS 3.14+ and all EPICS 7 servers.
const MinorVersion = 13

// Default CA port (TCP and UDP).
const DefaultPort = 5064

// Search reply data_type values.
const (
	SearchReply   = 10 // DO_REPLY: ask server to respond
	SearchNoReply = 5  // DONT_REPLY: suppress response (used by repeater)
)

// AccessRights bitmask values (parameter2 of CmdAccessRights message).
const (
	AccessRead  = 1 << 0
	AccessWrite = 1 << 1
)

// DBE event mask bits used in the 16-byte EVENT_ADD payload.
const (
	DBEValue   = 0x01 // trigger on any value change
	DBEAlarm   = 0x04 // trigger on alarm state change
	DBEDefault = DBEValue | DBEAlarm
)

// ---- DBF field types (native IOC field type, reported in CREATE_CHAN reply) ----

const (
	DBFString = 0
	DBFShort  = 1 // also DBFInt
	DBFFloat  = 2
	DBFEnum   = 3
	DBFChar   = 4
	DBFLong   = 5
	DBFDouble = 6
)

// ---- DBR request types ----

// Plain value types (used in WRITE).
const (
	DBRString = 0
	DBRShort  = 1
	DBRFloat  = 2
	DBREnum   = 3
	DBRChar   = 4
	DBRLong   = 5
	DBRDouble = 6
)

// DBR_TIME_* types (value + timestamp + alarm status; used in EVENT_ADD).
// Numbers from db_access.h: STRING=14, SHORT/INT=15, FLOAT=16, ENUM=17, CHAR=18, LONG=19, DOUBLE=20.
const (
	DBRTimeString = 14
	DBRTimeShort  = 15
	DBRTimeFloat  = 16
	DBRTimeEnum   = 17
	DBRTimeChar   = 18
	DBRTimeLong   = 19
	DBRTimeDouble = 20
)

// DBR_CTRL_* types (full control info: units, limits, enum strings; used in READ_NOTIFY).
const (
	DBRCtrlString = 28
	DBRCtrlShort  = 29
	DBRCtrlFloat  = 30
	DBRCtrlEnum   = 31
	DBRCtrlChar   = 32
	DBRCtrlLong   = 33
	DBRCtrlDouble = 34
)

// NativeTimeType maps a DBF field type to the corresponding DBR_TIME_* type
// that should be used for monitor subscriptions.
func NativeTimeType(dbfType int, elementCount int) uint16 {
	switch dbfType {
	case DBFDouble:
		return DBRTimeDouble
	case DBFFloat:
		return DBRTimeFloat
	case DBFLong:
		return DBRTimeLong
	case DBFShort:
		return DBRTimeShort
	case DBFEnum:
		return DBRTimeEnum
	case DBFString:
		return DBRTimeString
	case DBFChar:
		if elementCount > 1 {
			return DBRTimeString // waveform of chars treated as string
		}
		return DBRTimeChar
	default:
		return DBRTimeDouble
	}
}

// NativeCtrlType maps a DBF field type to the corresponding DBR_CTRL_* type
// that should be used for metadata gets.
func NativeCtrlType(dbfType int) uint16 {
	switch dbfType {
	case DBFDouble, DBFFloat:
		return DBRCtrlDouble
	case DBFLong, DBFShort, DBFChar:
		return DBRCtrlLong
	case DBFEnum:
		return DBRCtrlEnum
	case DBFString:
		return DBRCtrlString
	default:
		return DBRCtrlDouble
	}
}
