// Public protocol model for a performant, transport-independent TELNET core.
//
// This file intentionally starts with data types only. The goal is to let the
// test suite lock down parser, encoder, and negotiation behavior before the
// implementation fills in executable state transitions.

///|
pub(all) enum Command {
  SE
  NOP
  DM
  BRK
  IP
  AO
  AYT
  EC
  EL
  GA
  SB
  WILL
  WONT
  DO
  DONT
  IAC
} derive(Eq, Debug)

///|
pub(all) enum NegotiationVerb {
  Will
  Wont
  Do
  Dont
} derive(Eq, Debug)

///|
pub(all) enum OptionSide {
  Local
  Remote
} derive(Eq, Debug)

///|
pub(all) enum QueueBit {
  Empty
  Opposite
} derive(Eq, Debug)

///|
pub(all) enum HalfState {
  No
  Yes
  WantNo(QueueBit)
  WantYes(QueueBit)
} derive(Eq, Debug)

///|
pub(all) struct OptionCode {
  value : Byte
} derive(Eq, Debug)

///|
pub(all) enum KnownOption {
  Binary
  Echo
  Reconnection
  SuppressGoAhead
  ApproxMessageSizeNegotiation
  Status
  TimingMark
  RemoteControlledTransAndEcho
  OutputLineWidth
  OutputPageSize
  OutputCarriageReturnDisposition
  OutputHorizontalTabStops
  OutputHorizontalTabDisposition
  OutputFormfeedDisposition
  OutputVerticalTabStops
  OutputVerticalTabDisposition
  OutputLinefeedDisposition
  ExtendedAscii
  Logout
  ByteMacro
  DataEntryTerminal
  Supdup
  SupdupOutput
  SendLocation
  TerminalType
  EndOfRecord
  TacacsUserIdentification
  OutputMarking
  TerminalLocationNumber
  Telnet3270Regime
  X3Pad
  Naws
  TerminalSpeed
  RemoteFlowControl
  Linemode
  XDisplayLocation
  Environment
  Authentication
  Encryption
  NewEnvironment
  Tn3270e
  Xauth
  Charset
  TelnetRemoteSerialPort
  ComPortControl
  TelnetSuppressLocalEcho
  TelnetStartTls
  Kermit
  SendUrl
  ForwardX
  ExtendedOptionsList
  Unknown(OptionCode)
} derive(Eq, Debug)

///|
pub(all) struct ByteSpan {
  bytes : Bytes
  start : Int
  length : Int
} derive(Eq, Debug)

///|
pub(all) struct DataEvent {
  span : ByteSpan
} derive(Eq, Debug)

///|
pub(all) struct CommandEvent {
  command : Command
} derive(Eq, Debug)

///|
pub(all) struct NegotiationEvent {
  verb : NegotiationVerb
  option : OptionCode
} derive(Eq, Debug)

///|
pub(all) struct SubnegotiationEvent {
  option : OptionCode
  payload : ByteSpan
} derive(Eq, Debug)

///|
pub(all) enum TelnetErrorKind {
  IncompleteIac
  IncompleteNegotiation
  IncompleteSubnegotiation
  InvalidCommandByte
  InvalidOptionByte
  SubnegotiationTooLarge
  UnexpectedSubnegotiationEnd
  PolicyRejected
  UnsupportedOption
} derive(Eq, Debug)

///|
pub(all) struct TelnetError {
  kind : TelnetErrorKind
  offset : Int
  byte : Byte?
  option : OptionCode?
} derive(Eq, Debug)

///|
pub(all) enum Event {
  Data(DataEvent)
  Command(CommandEvent)
  Negotiation(NegotiationEvent)
  Subnegotiation(SubnegotiationEvent)
  Error(TelnetError)
} derive(Eq, Debug)

///|
pub(all) enum ParserMode {
  Normal
  AfterIac
  AfterNegotiationVerb(NegotiationVerb)
  AfterSubnegotiationStart
  InSubnegotiation(OptionCode)
  InSubnegotiationAfterIac(OptionCode)
} derive(Eq, Debug)

///|
pub(all) enum CrPolicy {
  Preserve
  ValidateNvt
  NormalizeToLf
} derive(Eq, Debug)

///|
pub(all) enum DataCoalescing {
  EmitImmediately
  CoalesceUntilCommand
  CoalesceUntilCapacity
} derive(Eq, Debug)

///|
pub(all) struct ParserConfig {
  max_data_chunk_bytes : Int
  max_subnegotiation_bytes : Int
  cr_policy : CrPolicy
  data_coalescing : DataCoalescing
  strict_commands : Bool
} derive(Eq, Debug)

///|
pub(all) struct ParserCheckpoint {
  mode : ParserMode
  absolute_offset : Int
  buffered_data_length : Int
  buffered_subnegotiation_length : Int
} derive(Eq, Debug)

///|
pub(all) struct ParserState {
  config : ParserConfig
  checkpoint : ParserCheckpoint
} derive(Eq, Debug)

///|
pub(all) enum EncodeItem {
  RawData(ByteSpan)
  EscapedData(ByteSpan)
  Command(Command)
  Negotiation(NegotiationEvent)
  Subnegotiation(SubnegotiationEvent)
} derive(Eq, Debug)

///|
pub(all) enum EncodeErrorKind {
  CommandRequiresIacEscape
  InvalidSubnegotiationPayload
  OutputBufferTooSmall
  UnsupportedEncoding
} derive(Eq, Debug)

///|
pub(all) struct EncodeError {
  kind : EncodeErrorKind
  offset : Int
  required_capacity : Int
} derive(Eq, Debug)

///|
pub(all) enum BufferOwnership {
  BorrowedInput
  Copied
  Scratch
} derive(Eq, Debug)

///|
pub(all) struct BufferSlice {
  span : ByteSpan
  ownership : BufferOwnership
} derive(Eq, Debug)

///|
pub(all) struct OptionHalf {
  option : OptionCode
  side : OptionSide
  state : HalfState
} derive(Eq, Debug)

///|
pub(all) struct OptionState {
  option : OptionCode
  local_state : HalfState
  remote_state : HalfState
} derive(Eq, Debug)

///|
pub(all) enum PolicyDecision {
  Accept
  Reject
  Ignore
} derive(Eq, Debug)

///|
pub(all) struct PolicyQuestion {
  side : OptionSide
  option : OptionCode
  requested_by_peer : Bool
} derive(Eq, Debug)

///|
pub(all) enum NegotiationAction {
  Send(NegotiationEvent)
  MarkEnabled(OptionSide, OptionCode)
  MarkDisabled(OptionSide, OptionCode)
  NotifyApplication(OptionState)
  NoAction
} derive(Eq, Debug)

///|
pub(all) struct NegotiationTransition {
  before : OptionState
  input : NegotiationEvent
  decision : PolicyDecision
  after : OptionState
  actions : Array[NegotiationAction]
} derive(Eq, Debug)

///|
pub(all) enum TerminalTypeMessage {
  Send
  Is(String)
} derive(Eq, Debug)

///|
pub(all) struct NawsSize {
  columns : UInt16
  rows : UInt16
} derive(Eq, Debug)

///|
pub(all) struct WindowSize {
  width : UInt16
  height : UInt16
} derive(Eq, Debug)

///|
pub(all) enum EnvironmentVariableKind {
  Var
  UserVar
} derive(Eq, Debug)

///|
pub(all) struct EnvironmentVariable {
  kind : EnvironmentVariableKind
  name : String
  value : String?
} derive(Eq, Debug)

///|
pub(all) enum EnvironmentMessage {
  Is(Array[EnvironmentVariable])
  Send(Array[String])
  Info(Array[EnvironmentVariable])
} derive(Eq, Debug)

///|
pub(all) enum CharsetMessage {
  Request(Array[String])
  Accepted(String)
  Rejected
  TTableIs(String)
  TTableRejected
  TTableAck
  TTableNak
} derive(Eq, Debug)

///|
pub(all) enum LinemodeModeFlag {
  Edit
  TrapSig
  ModeAck
  SoftTab
  LitEcho
} derive(Eq, Debug)

///|
pub(all) struct LinemodeMode {
  flags : Array[LinemodeModeFlag]
} derive(Eq, Debug)

///|
pub(all) enum LinemodeForwardMask {
  Default
  None
  Bytes(ByteSpan)
} derive(Eq, Debug)

///|
pub(all) enum LinemodeMessage {
  Mode(LinemodeMode)
  ForwardMask(LinemodeForwardMask)
  Slc(ByteSpan)
} derive(Eq, Debug)

///|
pub(all) enum StartTlsMessage {
  StartTls
  Follows
} derive(Eq, Debug)

///|
pub(all) enum OptionPayload {
  Empty
  Raw(OptionCode, ByteSpan)
  TerminalType(TerminalTypeMessage)
  Naws(NawsSize)
  Environment(EnvironmentMessage)
  Charset(CharsetMessage)
  Linemode(LinemodeMessage)
  StartTls(StartTlsMessage)
} derive(Eq, Debug)

///|
pub(all) enum SessionTextPolicy {
  Preserve
  ValidateNvt
  NormalizeToLf
} derive(Eq, Debug)

///|
pub(all) enum StartTlsState {
  Cleartext
  StartTlsRequested
  StartTlsFollows
  TlsActive
  TlsRejected
} derive(Eq, Debug)

///|
pub(all) struct OptionPolicyRule {
  option : OptionCode
  side : OptionSide
  decision : PolicyDecision
} derive(Eq, Debug)

///|
pub(all) struct SessionPolicy {
  rules : Array[OptionPolicyRule]
  default_local : PolicyDecision
  default_remote : PolicyDecision
} derive(Eq, Debug)

///|
pub(all) struct SessionConfig {
  parser_config : ParserConfig
  policy : SessionPolicy
  incoming_text_policy : SessionTextPolicy
  outgoing_text_policy : SessionTextPolicy
  decode_known_options : Bool
  emit_raw_subnegotiation : Bool
  require_enabled_for_subnegotiation : Bool
  reject_malformed_known_payloads : Bool
  max_outbound_bytes : Int
} derive(Eq, Debug)

///|
pub(all) enum SessionEvent {
  Data(ByteSpan)
  Command(Command)
  NegotiationReceived(NegotiationEvent)
  NegotiationSent(NegotiationEvent)
  OptionEnabled(OptionSide, OptionCode)
  OptionDisabled(OptionSide, OptionCode)
  OptionRejected(OptionSide, OptionCode)
  Subnegotiation(SubnegotiationEvent)
  OptionPayload(OptionPayload)
  StartTlsRequested
  StartTlsFollows
  TransportUpgradeRequired
  Error(TelnetError)
} derive(Eq, Debug)

///|
pub(all) enum SessionErrorKind {
  OutputBufferTooSmall
  EncodeFailed
  MalformedOptionPayload
  PolicyRejected
} derive(Eq, Debug)

///|
pub(all) struct SessionError {
  kind : SessionErrorKind
  offset : Int
  required_capacity : Int
  telnet_error : TelnetError?
  encode_error : EncodeError?
} derive(Eq, Debug)