import Foundation
import NIOCore

/**
 Encapsulates a response from a device.
 */
struct DeviceResponse {

    /// Shorthand property for a timeout event.
    static var deviceTimedOut: DeviceResponse {
        .init(event: .deviceTimedOut)
    }

    /// Shorthand property for a disconnected event.
    static var deviceNotConnected: DeviceResponse {
        .init(event: .deviceNotConnected)
    }

    /// Shorthand property for a connected event.
    static var deviceConnected: DeviceResponse {
        .init(event: .deviceConnected)
    }

    /// Shorthand property for an unexpected socket event.
    static var unexpectedSocketEvent: DeviceResponse {
        .init(event: .unexpectedSocketEvent)
    }

    /// Shorthand property for an invalid message.
    static var invalidMessageData: DeviceResponse {
        .init(event: .invalidMessageData)
    }

    /// Shorthand property for missing body data.
    static var noBodyData: DeviceResponse {
        .init(event: .noBodyData)
    }

    /// Shorthand property for a busy connection
    static var operationInProgress: DeviceResponse {
        .init(event: .operationInProgress)
    }

    /// The response to a key from the server
    let event: MessageResult

    /// The index of the next key to use
    let response: Message?

    /**
     Decode a message from a buffer.

     The buffer must contain `Message.length+1` bytes. The first byte denotes the event type,
     the remaining bytes contain the message.
     - Parameter buffer: The buffer where the message bytes are stored
     */
    init?(_ buffer: ByteBuffer) {
        guard let byte = buffer.getBytes(at: 0, length: 1) else {
            print("No bytes received from device")
            return nil
        }
        guard let event = MessageResult(rawValue: byte[0]) else {
            print("Unknown response \(byte[0]) received from device")
            return nil
        }
        self.event = event
        guard let data = buffer.getSlice(at: 1, length: Message.length) else {
            self.response = nil
            return
        }
        self.response = Message(decodeFrom: data)
    }

    /**
     Create a response from an event without a message from the device.
     - Parameter event: The response from the device.
     */
    init(event: MessageResult) {
        self.event = event
        self.response = nil
    }

    /// Get the reponse encoded in bytes.
    var encoded: Data {
        guard let message = response else {
            return Data([event.rawValue])
        }
        return Data([event.rawValue]) + message.encoded
    }
}
