#include "icsneo/api/event.h"
#include "icsneo/device/device.h"
#include <sstream>

using namespace icsneo;

APIEvent::APIEvent(Type type, APIEvent::Severity severity, std::weak_ptr<Device> device) : eventStruct({}), device(device) {
	auto shared = device.lock();
	if(shared) {
		serial = shared->getSerial();
		eventStruct.serial[serial.copy(eventStruct.serial, sizeof(eventStruct.serial))] = '\0';
	}

	init(type, severity);
}

void APIEvent::init(Type event, APIEvent::Severity severity) {
	timepoint = EventClock::now();
	eventStruct.description = DescriptionForType(event);
	eventStruct.eventNumber = (uint32_t)event;
	eventStruct.severity = (uint8_t) severity;
	eventStruct.timestamp = EventClock::to_time_t(timepoint);
}

void APIEvent::downgradeFromError() noexcept {
	eventStruct.severity = (uint8_t) APIEvent::Severity::EventWarning;
}

bool APIEvent::isForDevice(std::string filterSerial) const noexcept {
	auto shared = device.lock();
	if(!shared || filterSerial.length() == 0)
		return false;
	return shared->getSerial() == filterSerial;
}

// API Errors
static constexpr const char* INVALID_NEODEVICE = "The provided neodevice_t object was invalid.";
static constexpr const char* REQUIRED_PARAMETER_NULL = "A required parameter was NULL.";
static constexpr const char* OUTPUT_TRUNCATED = "The output was too large for the provided buffer and has been truncated.";
static constexpr const char* BUFFER_INSUFFICIENT = "The provided buffer was insufficient. No data was written.";
static constexpr const char* PARAMETER_OUT_OF_RANGE = "A parameter was out of range.";
static constexpr const char* DEVICE_CURRENTLY_OPEN = "The device is currently open.";
static constexpr const char* DEVICE_CURRENTLY_CLOSED = "The device is currently closed.";
static constexpr const char* DEVICE_CURRENTLY_ONLINE = "The device is currently online.";
static constexpr const char* DEVICE_CURRENTLY_OFFLINE = "The device is currently offline.";
static constexpr const char* DEVICE_CURRENTLY_POLLING = "The device is currently polling for messages.";
static constexpr const char* DEVICE_NOT_CURRENTLY_POLLING = "The device is not currently polling for messages.";
static constexpr const char* UNSUPPORTED_TX_NETWORK = "Message network is not a supported TX network.";
static constexpr const char* MESSAGE_MAX_LENGTH_EXCEEDED = "The message was too long.";
static constexpr const char* VALUE_NOT_YET_PRESENT = "The value is not yet present.";
static constexpr const char* TIMEOUT = "The timeout was reached.";
static constexpr const char* WIVI_NOT_SUPPORTED = "Wireless neoVI functions are not supported on this device.";
static constexpr const char* RESTRICTED_ENTRY_FLAG = "Attempted to set a restricted flag in a Root Directory entry.";
static constexpr const char* NOT_SUPPORTED = "The requested feature is not supported.";
static constexpr const char* FIXED_POINT_OVERFLOW = "Value is too large to convert to fixed point.";
static constexpr const char* FIXED_POINT_PRECISION = "Value is too small for fixed point precision.";
static constexpr const char* SYSCALL_ERROR = "Error returned from syscall, check errno/GetLastError().";

// Device Errors
static constexpr const char* POLLING_MESSAGE_OVERFLOW = "Too many messages have been recieved for the polling message buffer, some have been lost!";
static constexpr const char* NO_SERIAL_NUMBER_FW_12V = "Communication could not be established with the device. Perhaps it is not powered with 12 volts?";
static constexpr const char* NO_SERIAL_NUMBER_FW = "Communication could not be established with the device. Perhaps it is not powered?";
static constexpr const char* NO_SERIAL_NUMBER_12V = "Communication could not be established with the device. Perhaps it is not powered with 12 volts or requires a firmware update using Vehicle Spy.";
static constexpr const char* NO_SERIAL_NUMBER = "Communication could not be established with the device. Perhaps it is not powered or requires a firmware update using Vehicle Spy.";
static constexpr const char* INCORRECT_SERIAL_NUMBER = "The device did not return the expected serial number!";
static constexpr const char* SETTINGS_READ = "The device settings could not be read.";
static constexpr const char* SETTINGS_VERSION = "The settings version is incorrect, please update your firmware with ICS Device Manager.";
static constexpr const char* SETTINGS_LENGTH = "The settings length is incorrect, please update your firmware with ICS Device Manager.";
static constexpr const char* SETTINGS_CHECKSUM = "The settings checksum is incorrect, attempting to set defaults may remedy this issue.";
static constexpr const char* SETTINGS_NOT_AVAILABLE = "Settings are not available for this device.";
static constexpr const char* SETTINGS_READONLY = "Settings are read-only for this device.";
static constexpr const char* CAN_SETTINGS_NOT_AVAILABLE = "CAN settings are not available for this device.";
static constexpr const char* CANFD_SETTINGS_NOT_AVAILABLE = "CANFD settings are not available for this device.";
static constexpr const char* LSFTCAN_SETTINGS_NOT_AVAILABLE = "LSFTCAN settings are not available for this device.";
static constexpr const char* SWCAN_SETTINGS_NOT_AVAILABLE = "SWCAN settings are not available for this device.";
static constexpr const char* BAUDRATE_NOT_FOUND = "The baudrate was not found.";
static constexpr const char* UNEXPECTED_NETWORK_TYPE = "The network type was not found.";
static constexpr const char* DEVICE_FIRMWARE_OUT_OF_DATE = "The device firmware is out of date. New API functionality may not be supported.";
static constexpr const char* SETTINGS_STRUCTURE_MISMATCH = "Unexpected settings structure for this device.";
static constexpr const char* SETTINGS_STRUCTURE_TRUNCATED = "Settings structure is longer than the device supports and will be truncated.";
static constexpr const char* NO_DEVICE_RESPONSE = "Expected a response from the device but none were found.";
static constexpr const char* MESSAGE_FORMATTING = "The message was not properly formed.";
static constexpr const char* CANFD_NOT_SUPPORTED = "This device does not support CANFD.";
static constexpr const char* RTR_NOT_SUPPORTED = "RTR is not supported with CANFD.";
static constexpr const char* DEVICE_DISCONNECTED = "The device was disconnected.";
static constexpr const char* ONLINE_NOT_SUPPORTED = "This device does not support going online.";
static constexpr const char* TERMINATION_NOT_SUPPORTED_DEVICE = "This device does not support software selectable termination.";
static constexpr const char* TERMINATION_NOT_SUPPORTED_NETWORK = "This network does not support software selectable termination on this device.";
static constexpr const char* ANOTHER_IN_TERMINATION_GROUP_ENABLED = "A mutually exclusive network already has termination enabled.";
static constexpr const char* ETH_PHY_REGISTER_CONTROL_NOT_AVAILABLE = "Ethernet PHY register control is not available for this device.";
static constexpr const char* DISK_NOT_SUPPORTED = "This device does not support accessing the specified disk.";
static constexpr const char* EOF_REACHED = "The requested length exceeds the available data from this disk.";
static constexpr const char* SETTINGS_DEFAULTS_USED = "The device settings could not be loaded, the default settings have been applied.";
static constexpr const char* ATOMIC_OPERATION_RETRIED = "An operation failed to be atomically completed, but will be retried.";
static constexpr const char* ATOMIC_OPERATION_COMPLETED_NONATOMICALLY = "An ideally-atomic operation was completed nonatomically.";
static constexpr const char* WIVI_STACK_REFRESH_FAILED = "The Wireless neoVI stack encountered a communication error.";
static constexpr const char* WIVI_UPLOAD_STACK_OVERFLOW = "The Wireless neoVI upload stack has encountered an overflow condition.";
static constexpr const char* A2B_MESSAGE_INCOMPLETE_FRAME = "At least one of the frames of the A2B message does not contain samples for each channel and stream.";
static constexpr const char* I2C_MESSAGE_EXCEED_MAX_LENGTH = "The I2C message was too long.";
static constexpr const char* COREMINI_UPLOAD_VERSION_MISMATCH = "The version of the coremini engine on the device and the script uploaded are not the same.";
static constexpr const char* DISK_NOT_CONNECTED = "The program tried to access a disk that is not connected.";
static constexpr const char* UNEXPECTED_RESPONSE = "Received an unexpected or invalid response from the device.";
static constexpr const char* LIVE_DATA_INVALID_HANDLE = "The live data handle was invalid.";
static constexpr const char* LIVE_DATA_INVALID_COMMAND = "The live data command was invalid.";
static constexpr const char* LIVE_DATA_INVALID_ARGUMENT = "The live data argument was invalid.";
static constexpr const char* LIVE_DATA_VERSION_MISMATCH = "The live data version between libicsneo and firmware are not the same.";
static constexpr const char* LIVE_DATA_NO_DEVICE_RESPONSE = "Expected a response from the device for live data but none were found.";
static constexpr const char* LIVE_DATA_MAX_SIGNALS_REACHED = "The max amound of signals to subscribe to for live data has been reached.";
static constexpr const char* LIVE_DATA_COMMAND_FAILED = "The live data command failed.";
static constexpr const char* LIVE_DATA_ENCODER_ERROR = "Failure to encode live data message.";
static constexpr const char* LIVE_DATA_DECODER_ERROR = "Failure to decode live data message.";
static constexpr const char* LIVE_DATA_NOT_SUPPORTED = "Live data is not supported on this device.";
static constexpr const char* LIN_SETTINGS_NOT_AVAILABLE = "LIN settings are not available for this device.";
static constexpr const char* MODE_NOT_FOUND = "The mode was not found.";
static constexpr const char* APP_ERROR_PARSING_FAILED = "Failure to decode app error message.";
static constexpr const char* GPTP_NOT_SUPPORTED = "GPTP clock synchronization is not supported on this device.";
static constexpr const char* SETTING_NOT_AVAILABLE = "Requested a setting that is not available on this device";
static constexpr const char* DISK_FORMAT_NOT_SUPPORTED = "Disk formatting is not supported on this device.";
static constexpr const char* DISK_FORMAT_INVALID_COUNT = "Disk format config disk count is mismatched with device disk count.";

// Transport Errors
static constexpr const char* FAILED_TO_READ = "A read operation failed.";
static constexpr const char* FAILED_TO_WRITE = "A write operation failed.";
static constexpr const char* DRIVER_FAILED_TO_OPEN = "The device driver encountered a low-level error while opening the device.";
static constexpr const char* DRIVER_FAILED_TO_CLOSE = "The device driver encountered a low-level error while closing the device.";
static constexpr const char* PACKET_CHECKSUM_ERROR = "There was a checksum error while decoding a packet. The packet was dropped.";
static constexpr const char* TRANSMIT_BUFFER_FULL = "The transmit buffer is full and the device is set to non-blocking.";
static constexpr const char* DEVICE_IN_USE = "The device is currently in use by another program.";
static constexpr const char* PCAP_COULD_NOT_START = "The PCAP driver could not be started. Ethernet devices will not be found.";
static constexpr const char* PCAP_COULD_NOT_FIND_DEVICES = "The PCAP driver failed to find devices. Ethernet devices will not be found.";
static constexpr const char* PACKET_DECODING = "There was an error decoding a packet from the device.";
static constexpr const char* SOCKET_FAILED_TO_OPEN = "Unable to open new socket.";
static constexpr const char* FAILED_TO_BIND = "Unable to bind socket.";
static constexpr const char* ERROR_SETTING_SOCKET_OPTION = "A call to setsockopt() failed.";
static constexpr const char* GETIFADDRS_ERROR = "A call to getifaddrs() failed.";
static constexpr const char* SEND_TO_ERROR = "A call to sendto() failed.";
static constexpr const char* MDIO_MESSAGE_EXCEED_MAX_LENGTH = "The MDIO message was too long.";

// VSA
static constexpr const char* VSA_BUFFER_CORRUPTED = "VSA data in record buffer is corrupted.";
static constexpr const char* VSA_TIMESTAMP_NOT_FOUND = "Unable to find a VSA record with a valid timestamp.";
static constexpr const char* VSA_BUFFER_FORMAT_ERROR = "VSA record buffer is formatted incorrectly.";
static constexpr const char* VSA_MAX_READ_ATTEMPTS_REACHED = "Reached max attempts to read VSA records before exit.";
static constexpr const char* VSA_BYTE_PARSE_FAILURE = "Failure to parse record bytes from VSA buffer.";
static constexpr const char* VSA_EXTENDED_MESSAGE_ERROR = "Failure to parse extended message record sequence";
static constexpr const char* VSA_OTHER_ERROR = "Unknown error in VSA read API.";

// MACSEC
static constexpr const char* MACSEC_SECY_LIMIT = "Attempted to exceed the limit of SecY additions to this port";
static constexpr const char* MACSEC_RULE_LIMIT = "Attempted to exceed the limit of rule additions to this port";
static constexpr const char* MACSEC_SA_LIMIT = "Attempted to exceed the limit of SA additions to this port";
static constexpr const char* MACSEC_INVALID_SECY_INDEX = "Attempted to access an invalid SecY index";
static constexpr const char* MACSEC_INVALID_SA_INDEX = "Attempted to access an invalid SA index";
static constexpr const char* MACSEC_INVALID_RULE_INDEX = "Attempted to access an invalid rule index";
static constexpr const char* MACSEC_REKEY_NOT_ENABLED = "Attempted to set rekey SA when rekey was not enabled";
static constexpr const char* MACSEC_NOT_SUPPORTED = "MACsec is not supported on this device";
static constexpr const char* MACSEC_CONFIG_MISMATCH = "Attempted to configure device with a macsec configuration for a different device";
// Servd
static constexpr const char* SERVD_BIND_ERROR = "Error binding socket for Servd communication";
static constexpr const char* SERVD_NONBLOCK_ERROR = "Error setting non-blocking mode for Servd socket";
static constexpr const char* SERVD_TRANSCEIVE_ERROR = "Error while sending to or receiving from Servd";
static constexpr const char* SERVD_OUTDATED_ERROR = "Servd version is lower than client (libicsneo) version, update Servd";
static constexpr const char* SERVD_INVALID_RESPONSE_ERROR = "Unexpected response from Servd";
static constexpr const char* SERVD_LOCK_ERROR = "Error locking Servd mutex";
static constexpr const char* SERVD_SEND_ERROR = "Error sending to Servd";
static constexpr const char* SERVD_RECV_ERROR = "Error receiving from Servd";
static constexpr const char* SERVD_POLL_ERROR = "Error polling on Servd socket";
static constexpr const char* SERVD_NODATA_ERROR = "No data received from Servd";
static constexpr const char* SERVD_JOIN_MULTICAST_ERROR = "Error joining Servd multicast group";
static constexpr const char* SERVD_NOT_REACHABLE = "Could not reach Servd; ensure it is installed and running";

// DXX
static constexpr const char* DXX_ERROR_SYS = "System error, check errno/GetLastError()";
static constexpr const char* DXX_ERROR_INT = "DXX interrupt called";
static constexpr const char* DXX_ERROR_OVERFLOW = "Overflow in DXX";
static constexpr const char* DXX_ERROR_IO = "I/O failure in DXX";
static constexpr const char* DXX_ERROR_ARG = "Invalid arg passed to DXX";

static constexpr const char* NO_ERROR_FOUND = "No errors were found.";
static constexpr const char* TOO_MANY_EVENTS = "Too many events have occurred. The list has been truncated.";
static constexpr const char* UNKNOWN = "An unknown internal error occurred.";
static constexpr const char* INVALID = "An invalid internal error occurred.";

const char* APIEvent::DescriptionForType(Type type) {
	switch(type) {
		// API Errors
		case Type::InvalidNeoDevice:
			return INVALID_NEODEVICE;
		case Type::RequiredParameterNull:
			return REQUIRED_PARAMETER_NULL;
		case Type::BufferInsufficient:
			return BUFFER_INSUFFICIENT;
		case Type::OutputTruncated:
			return OUTPUT_TRUNCATED;
		case Type::ParameterOutOfRange:
			return PARAMETER_OUT_OF_RANGE;
		case Type::DeviceCurrentlyOpen:
			return DEVICE_CURRENTLY_OPEN;
		case Type::DeviceCurrentlyClosed:
			return DEVICE_CURRENTLY_CLOSED;
		case Type::DeviceCurrentlyOnline:
			return DEVICE_CURRENTLY_ONLINE;
		case Type::DeviceCurrentlyOffline:
			return DEVICE_CURRENTLY_OFFLINE;
		case Type::DeviceCurrentlyPolling:
			return DEVICE_CURRENTLY_POLLING;
		case Type::DeviceNotCurrentlyPolling:
			return DEVICE_NOT_CURRENTLY_POLLING;
		case Type::UnsupportedTXNetwork:
			return UNSUPPORTED_TX_NETWORK;
		case Type::MessageMaxLengthExceeded:
			return MESSAGE_MAX_LENGTH_EXCEEDED;
		case Type::ValueNotYetPresent:
			return VALUE_NOT_YET_PRESENT;
		case Type::Timeout:
			return TIMEOUT;
		case Type::WiVINotSupported:
			return WIVI_NOT_SUPPORTED;
		case Type::RestrictedEntryFlag:
			return RESTRICTED_ENTRY_FLAG;
		case Type::NotSupported:
			return NOT_SUPPORTED;
		case Type::FixedPointOverflow:
			return FIXED_POINT_OVERFLOW;
		case Type::FixedPointPrecision:
			return FIXED_POINT_PRECISION;
		case Type::SyscallError:
			return SYSCALL_ERROR;

		// Device Errors
		case Type::PollingMessageOverflow:
			return POLLING_MESSAGE_OVERFLOW;
		case Type::NoSerialNumber:
			return NO_SERIAL_NUMBER;
		case Type::IncorrectSerialNumber:
			return INCORRECT_SERIAL_NUMBER;
		case Type::SettingsReadError:
			return SETTINGS_READ;
		case Type::SettingsVersionError:
			return SETTINGS_VERSION;
		case Type::SettingsLengthError:
			return SETTINGS_LENGTH;
		case Type::SettingsChecksumError:
			return SETTINGS_CHECKSUM;
		case Type::SettingsNotAvailable:
			return SETTINGS_NOT_AVAILABLE;
		case Type::SettingsReadOnly:
			return SETTINGS_READONLY;
		case Type::CANSettingsNotAvailable:
			return CAN_SETTINGS_NOT_AVAILABLE;
		case Type::CANFDSettingsNotAvailable:
			return CANFD_SETTINGS_NOT_AVAILABLE;
		case Type::LSFTCANSettingsNotAvailable:
			return LSFTCAN_SETTINGS_NOT_AVAILABLE;
		case Type::SWCANSettingsNotAvailable:
			return SWCAN_SETTINGS_NOT_AVAILABLE;
		case Type::BaudrateNotFound:
			return BAUDRATE_NOT_FOUND;
		case Type::UnexpectedNetworkType:
			return UNEXPECTED_NETWORK_TYPE;
		case Type::DeviceFirmwareOutOfDate:
			return DEVICE_FIRMWARE_OUT_OF_DATE;
		case Type::SettingsStructureMismatch:
			return SETTINGS_STRUCTURE_MISMATCH;
		case Type::SettingsStructureTruncated:
			return SETTINGS_STRUCTURE_TRUNCATED;
		case Type::NoDeviceResponse:
			return NO_DEVICE_RESPONSE;
		case Type::MessageFormattingError:
			return MESSAGE_FORMATTING;
		case Type::CANFDNotSupported:
			return CANFD_NOT_SUPPORTED;
		case Type::RTRNotSupported:
			return RTR_NOT_SUPPORTED;
		case Type::DeviceDisconnected:
			return DEVICE_DISCONNECTED;
		case Type::OnlineNotSupported:
			return ONLINE_NOT_SUPPORTED;
		case Type::TerminationNotSupportedDevice:
			return TERMINATION_NOT_SUPPORTED_DEVICE;
		case Type::TerminationNotSupportedNetwork:
			return TERMINATION_NOT_SUPPORTED_NETWORK;
		case Type::AnotherInTerminationGroupEnabled:
			return ANOTHER_IN_TERMINATION_GROUP_ENABLED;
		case Type::NoSerialNumberFW:
			return NO_SERIAL_NUMBER_FW;
		case Type::NoSerialNumber12V:
			return NO_SERIAL_NUMBER_12V;
		case Type::NoSerialNumberFW12V:
			return NO_SERIAL_NUMBER_FW_12V;
		case Type::EthPhyRegisterControlNotAvailable:
			return ETH_PHY_REGISTER_CONTROL_NOT_AVAILABLE;
		case Type::DiskNotSupported:
			return DISK_NOT_SUPPORTED;
		case Type::EOFReached:
			return EOF_REACHED;
		case Type::SettingsDefaultsUsed:
			return SETTINGS_DEFAULTS_USED;
		case Type::AtomicOperationRetried:
			return ATOMIC_OPERATION_RETRIED;
		case Type::AtomicOperationCompletedNonatomically:
			return ATOMIC_OPERATION_COMPLETED_NONATOMICALLY;
		case Type::WiVIStackRefreshFailed:
			return WIVI_STACK_REFRESH_FAILED;
		case Type::WiVIUploadStackOverflow:
			return WIVI_UPLOAD_STACK_OVERFLOW;
		case Type::I2CMessageExceedsMaxLength:
			return I2C_MESSAGE_EXCEED_MAX_LENGTH;
		case Type::A2BMessageIncompleteFrame:
			return A2B_MESSAGE_INCOMPLETE_FRAME;
		case Type::CoreminiUploadVersionMismatch:
			return COREMINI_UPLOAD_VERSION_MISMATCH;
		case Type::DiskNotConnected:
			return DISK_NOT_CONNECTED;
		case Type::UnexpectedResponse:
			return UNEXPECTED_RESPONSE;
		case Type::LiveDataInvalidHandle:
			return LIVE_DATA_INVALID_HANDLE;
		case Type::LiveDataInvalidCommand:
			return LIVE_DATA_INVALID_COMMAND;
		case Type::LiveDataInvalidArgument:
			return LIVE_DATA_INVALID_ARGUMENT;
		case Type::LiveDataVersionMismatch:
			return LIVE_DATA_VERSION_MISMATCH;
		case Type::LiveDataNoDeviceResponse:
			return LIVE_DATA_NO_DEVICE_RESPONSE;
		case Type::LiveDataMaxSignalsReached:
			return LIVE_DATA_MAX_SIGNALS_REACHED;
		case Type::LiveDataCommandFailed:
			return LIVE_DATA_COMMAND_FAILED;
		case Type::LiveDataEncoderError:
			return LIVE_DATA_ENCODER_ERROR;
		case Type::LiveDataDecoderError:
			return LIVE_DATA_DECODER_ERROR;
		case Type::LiveDataNotSupported:
			return LIVE_DATA_NOT_SUPPORTED;
		case Type::LINSettingsNotAvailable:
			return LIN_SETTINGS_NOT_AVAILABLE;
		case Type::ModeNotFound:
			return MODE_NOT_FOUND;
		case Type::AppErrorParsingFailed:
			return APP_ERROR_PARSING_FAILED;
		case Type::SettingNotAvaiableDevice:
			return SETTING_NOT_AVAILABLE;
		// Transport Errors
		case Type::FailedToRead:
			return FAILED_TO_READ;
		case Type::FailedToWrite:
			return FAILED_TO_WRITE;
		case Type::DriverFailedToOpen:
			return DRIVER_FAILED_TO_OPEN;
		case Type::DriverFailedToClose:
			return DRIVER_FAILED_TO_CLOSE;
		case Type::PacketChecksumError:
			return PACKET_CHECKSUM_ERROR;
		case Type::TransmitBufferFull:
			return TRANSMIT_BUFFER_FULL;
		case Type::DeviceInUse:
			return DEVICE_IN_USE;
		case Type::PCAPCouldNotStart:
			return PCAP_COULD_NOT_START;
		case Type::PCAPCouldNotFindDevices:
			return PCAP_COULD_NOT_FIND_DEVICES;
		case Type::PacketDecodingError:
			return PACKET_DECODING;
		case Type::SocketFailedToOpen:
			return SOCKET_FAILED_TO_OPEN;
		case Type::FailedToBind:
			return FAILED_TO_BIND;
		case Type::ErrorSettingSocketOption:
			return ERROR_SETTING_SOCKET_OPTION;
		case Type::GetIfAddrsError:
			return GETIFADDRS_ERROR;
		case Type::SendToError:
			return SEND_TO_ERROR;
		case Type::MDIOMessageExceedsMaxLength:
			return MDIO_MESSAGE_EXCEED_MAX_LENGTH;
		case Type::GPTPNotSupported:
			return GPTP_NOT_SUPPORTED;
		case Type::DiskFormatNotSupported:
			return DISK_FORMAT_NOT_SUPPORTED;
		case Type::DiskFormatInvalidCount:
			return DISK_FORMAT_INVALID_COUNT;

		// VSA
		case Type::VSABufferCorrupted:
			return VSA_BUFFER_CORRUPTED;
		case Type::VSATimestampNotFound:
			return VSA_TIMESTAMP_NOT_FOUND;
		case Type::VSABufferFormatError:
			return VSA_BUFFER_FORMAT_ERROR;
		case Type::VSAMaxReadAttemptsReached:
			return VSA_MAX_READ_ATTEMPTS_REACHED;
		case Type::VSAByteParseFailure:
			return VSA_BYTE_PARSE_FAILURE;
		case Type::VSAExtendedMessageError:
			return VSA_EXTENDED_MESSAGE_ERROR;
		case Type::VSAOtherError:
			return VSA_OTHER_ERROR;

		// MACSEC
		case Type::MACsecSecYLimit:
			return MACSEC_SECY_LIMIT;
		case Type::MACsecSaLimit:
			return MACSEC_SA_LIMIT;
		case Type::MACsecRuleLimit:
			return MACSEC_RULE_LIMIT;
		case Type::MACsecInvalidSecYIndex:
			return MACSEC_INVALID_SECY_INDEX;
		case Type::MACsecInvalidSaIndex:
			return MACSEC_INVALID_SA_INDEX;
		case Type::MACsecInvalidRuleIndex:
			return MACSEC_INVALID_RULE_INDEX;
		case Type::MACsecRekeyNotEnabled:
			return MACSEC_REKEY_NOT_ENABLED;
		case Type::MACsecNotSupported:
			return MACSEC_NOT_SUPPORTED;
		case Type::MACsecConfigMismatch:
			return MACSEC_CONFIG_MISMATCH;
		// Servd
		case Type::ServdBindError:
			return SERVD_BIND_ERROR;
		case Type::ServdNonblockError:
			return SERVD_NONBLOCK_ERROR;
		case Type::ServdTransceiveError:
			return SERVD_TRANSCEIVE_ERROR;
		case Type::ServdOutdatedError:
			return SERVD_OUTDATED_ERROR;
		case Type::ServdInvalidResponseError:
			return SERVD_INVALID_RESPONSE_ERROR;
		case Type::ServdLockError:
			return SERVD_LOCK_ERROR;
		case Type::ServdSendError:
			return SERVD_SEND_ERROR;
		case Type::ServdRecvError:
			return SERVD_RECV_ERROR;
		case Type::ServdPollError:
			return SERVD_POLL_ERROR;
		case Type::ServdNoDataError:
			return SERVD_NODATA_ERROR;
		case Type::ServdJoinMulticastError:
			return SERVD_JOIN_MULTICAST_ERROR;
		case Type::ServdNotReachable:
			return SERVD_NOT_REACHABLE;

		// DXX
		case Type::DXXErrorSys:
			return DXX_ERROR_SYS;
		case Type::DXXErrorInt:
			return DXX_ERROR_INT;
		case Type::DXXErrorOverflow:
			return DXX_ERROR_OVERFLOW;
		case Type::DXXErrorIO:
			return DXX_ERROR_IO;
		case Type::DXXErrorArg:
			return DXX_ERROR_ARG;

		// Other Errors
		case Type::NoErrorFound:
			return NO_ERROR_FOUND;
		case Type::TooManyEvents:
			return TOO_MANY_EVENTS;
		case Type::Unknown:
			return UNKNOWN;
		case Type::Any:
			break;
	}
	return INVALID;
}

std::string APIEvent::describe() const noexcept {
	std::stringstream ss;
	auto shared = device.lock();
	if(shared)
		ss << *shared; // Makes use of device.describe()
	else
		ss << "API";

	Severity severity = getSeverity();
	if(severity == Severity::EventInfo) {
		ss << " Info: ";
	} else if(severity == Severity::EventWarning) {
		ss << " Warning: ";
	} else if(severity == Severity::Error) {
		ss << " Error: ";
	} else {
		// Should never get here, since Severity::Any should only be used for filtering
		ss << " Any: ";
	}

	const char* description = DescriptionForType(getType());
	if(description == INVALID)
		ss << description << " (0x" << std::hex << eventStruct.eventNumber << ")";
	else
		ss << description;
	return ss.str();
}

bool EventFilter::match(const APIEvent& event) const noexcept {
	if(type != APIEvent::Type::Any && type != event.getType())
		return false;

	if(matchOnDevicePtr && !event.isForDevice(device))
		return false;

	if(severity != APIEvent::Severity::Any && severity != event.getSeverity())
		return false;

	if(serial.length() != 0 && !event.isForDevice(serial))
		return false;

	return true;
}