///|
/// Authentication choice for the bind operation (RFC 4511 4.2).
pub(all) enum AuthenticationChoice {
  Simple(String)
  Sasl(SaslCredentials)
} derive(Eq, @debug.Debug)

///|
pub struct SaslCredentials {
  mechanism : String
  credentials : Bytes
} derive(Eq, @debug.Debug)

///|
pub fn SaslCredentials::new(
  mechanism : String,
  credentials : Bytes,
) -> SaslCredentials {
  { mechanism, credentials }
}

///|
pub struct BindRequest {
  version : Int
  name : String
  authentication : AuthenticationChoice
} derive(Eq, @debug.Debug)

///|
pub fn BindRequest::simple(
  version : Int,
  name : String,
  password : String,
) -> BindRequest {
  { version, name, authentication: Simple(password) }
}

///|
pub fn BindRequest::sasl(
  version : Int,
  name : String,
  creds : SaslCredentials,
) -> BindRequest {
  { version, name, authentication: Sasl(creds) }
}

///|
pub struct BindResponse {
  result : LdapResult
  server_sasl_creds : Bytes?
} derive(Eq, @debug.Debug)

///|
pub fn BindResponse::new(result : LdapResult) -> BindResponse {
  { result, server_sasl_creds: None }
}

///|
/// Search scope (RFC 4511 4.5.1.2).
pub(all) enum SearchScope {
  Base
  OneLevel
  Subtree
} derive(Eq, @debug.Debug)

///|
pub fn SearchScope::to_int(self : SearchScope) -> Int {
  match self {
    Base => 0
    OneLevel => 1
    Subtree => 2
  }
}

///|
pub fn SearchScope::from_int(v : Int) -> SearchScope? {
  match v {
    0 => Some(Base)
    1 => Some(OneLevel)
    2 => Some(Subtree)
    _ => None
  }
}

///|
/// Alias dereferencing policy (RFC 4511 4.5.1.3).
pub(all) enum DerefAliases {
  Never
  InSearching
  FindingBase
  Always
} derive(Eq, @debug.Debug)

///|
pub fn DerefAliases::to_int(self : DerefAliases) -> Int {
  match self {
    Never => 0
    InSearching => 1
    FindingBase => 2
    Always => 3
  }
}

///|
pub fn DerefAliases::from_int(v : Int) -> DerefAliases? {
  match v {
    0 => Some(Never)
    1 => Some(InSearching)
    2 => Some(FindingBase)
    3 => Some(Always)
    _ => None
  }
}

///|
pub struct SearchRequest {
  base_object : String
  scope : SearchScope
  deref_aliases : DerefAliases
  size_limit : Int
  time_limit : Int
  types_only : Bool
  attributes : Array[String]
  filter : LdapFilter
} derive(Eq, @debug.Debug)

///|
pub fn SearchRequest::new(
  base_object : String,
  scope : SearchScope,
  filter : String,
  deref_aliases? : DerefAliases,
  size_limit? : Int,
  time_limit? : Int,
  types_only? : Bool,
  attributes? : Array[String],
) -> Result[SearchRequest, LdapError] {
  let parsed = match parse_filter(filter) {
    Ok(f) => f
    Err(e) => return Err(e)
  }
  Ok({
    base_object,
    scope,
    deref_aliases: match deref_aliases {
      Some(d) => d
      None => Never
    },
    size_limit: match size_limit {
      Some(v) => v
      None => 0
    },
    time_limit: match time_limit {
      Some(v) => v
      None => 0
    },
    types_only: match types_only {
      Some(v) => v
      None => false
    },
    attributes: match attributes {
      Some(a) => a
      None => []
    },
    filter: parsed,
  })
}

///|
pub struct SearchResultEntry {
  object_name : String
  attributes : Array[PartialAttribute]
} derive(Eq, @debug.Debug)

///|
pub fn SearchResultEntry::new(
  object_name : String,
  attributes : Array[PartialAttribute],
) -> SearchResultEntry {
  { object_name, attributes }
}

///|
/// `SearchResultReference` is the list of referral URIs (RFC 4511 4.5.3).
pub struct SearchResultReference {
  uris : Array[String]
} derive(Eq, @debug.Debug)

///|
pub fn SearchResultReference::new(
  uris : Array[String],
) -> SearchResultReference {
  { uris, }
}

///|
/// A request control (RFC 4511 4.1.11).
pub struct Control {
  control_type : String
  criticality : Bool
  control_value : Bytes
} derive(Eq, @debug.Debug)

///|
pub fn Control::new(
  control_type : String,
  criticality : Bool,
  control_value : Bytes,
) -> Control {
  { control_type, criticality, control_value }
}

///|
/// The protocolOp CHOICE of `LDAPMessage` (RFC 4511 4.1.1).
pub(all) enum ProtocolOp {
  BindRequest(BindRequest)
  BindResponse(BindResponse)
  UnbindRequest
  SearchRequest(SearchRequest)
  SearchResultEntry(SearchResultEntry)
  SearchResultDone(LdapResult)
  SearchResultReference(SearchResultReference)
  ModifyRequest(ModifyRequest)
  ModifyResponse(ModifyResponse)
  AddRequest(AddRequest)
  AddResponse(AddResponse)
  DelRequest(DelRequest)
  DelResponse(DelResponse)
  ModifyDnRequest(ModifyDnRequest)
  ModifyDnResponse(ModifyDnResponse)
  CompareRequest(CompareRequest)
  CompareResponse(CompareResponse)
  ExtendedRequest(ExtendedRequest)
  ExtendedResponse(ExtendedResponse)
  AbandonRequest(Int)
} derive(Eq, @debug.Debug)

///|
/// ExtendedRequest ::= [APPLICATION 23] SEQUENCE { requestName [0] LDAPOID,
/// requestValue [1] OCTET STRING OPTIONAL } (RFC 4511 4.12).
pub struct ExtendedRequest {
  request_name : String
  request_value : Bytes?
} derive(Eq, @debug.Debug)

///|
pub fn ExtendedRequest::new(
  request_name : String,
  request_value : Bytes?,
) -> ExtendedRequest {
  { request_name, request_value }
}

///|
/// ExtendedResponse ::= [APPLICATION 24] SEQUENCE { COMPONENTS OF LDAPResult,
/// responseName [10] LDAPOID OPTIONAL, responseValue [11] OPTIONAL }
/// (RFC 4511 4.12).
pub struct ExtendedResponse {
  result : LdapResult
  mut response_name : String?
  mut response_value : Bytes?
} derive(Eq, @debug.Debug)

///|
pub fn ExtendedResponse::new(result : LdapResult) -> ExtendedResponse {
  { result, response_name: None, response_value: None }
}

///|
/// A modify operation kind (RFC 4511 4.6): add(0), delete(1), replace(2).
pub(all) enum ModifyOperation {
  Add
  Delete
  Replace
} derive(Eq, @debug.Debug)

///|
pub fn ModifyOperation::to_int(self : ModifyOperation) -> Int {
  match self {
    Add => 0
    Delete => 1
    Replace => 2
  }
}

///|
pub fn ModifyOperation::from_int(v : Int) -> ModifyOperation? {
  match v {
    0 => Some(Add)
    1 => Some(Delete)
    2 => Some(Replace)
    _ => None
  }
}

///|
/// One change entry of a ModifyRequest (RFC 4511 4.6).
pub struct ModifyRequestChange {
  operation : ModifyOperation
  modification : PartialAttribute
} derive(Eq, @debug.Debug)

///|
pub fn ModifyRequestChange::new(
  operation : ModifyOperation,
  modification : PartialAttribute,
) -> ModifyRequestChange {
  { operation, modification }
}

///|
/// ModifyRequest ::= [APPLICATION 6] SEQUENCE { object, changes } (RFC 4511 4.6).
pub struct ModifyRequest {
  object : String
  changes : Array[ModifyRequestChange]
} derive(Eq, @debug.Debug)

///|
pub fn ModifyRequest::new(
  object : String,
  changes : Array[ModifyRequestChange],
) -> ModifyRequest {
  { object, changes }
}

///|
/// The response to a modify operation (RFC 4511 4.7).
pub struct ModifyResponse {
  result : LdapResult
} derive(Eq, @debug.Debug)

///|
pub fn ModifyResponse::new(result : LdapResult) -> ModifyResponse {
  { result, }
}

///|
/// AddRequest ::= [APPLICATION 8] SEQUENCE { entry, attributes } (RFC 4511 4.8).
pub struct AddRequest {
  entry : String
  attributes : Array[Attribute]
} derive(Eq, @debug.Debug)

///|
pub fn AddRequest::new(
  entry : String,
  attributes : Array[Attribute],
) -> AddRequest {
  { entry, attributes }
}

///|
/// The response to an add operation (RFC 4511 4.9).
pub struct AddResponse {
  result : LdapResult
} derive(Eq, @debug.Debug)

///|
pub fn AddResponse::new(result : LdapResult) -> AddResponse {
  { result, }
}

///|
/// DelRequest ::= [APPLICATION 10] LDAPDN (primitive, RFC 4511 4.10).
pub struct DelRequest {
  entry : String
} derive(Eq, @debug.Debug)

///|
pub fn DelRequest::new(entry : String) -> DelRequest {
  { entry, }
}

///|
/// The response to a delete operation (RFC 4511 4.11).
pub struct DelResponse {
  result : LdapResult
} derive(Eq, @debug.Debug)

///|
pub fn DelResponse::new(result : LdapResult) -> DelResponse {
  { result, }
}

///|
/// ModifyDNRequest (RFC 4511 4.9): rename/move an entry.
pub struct ModifyDnRequest {
  entry : String
  newrdn : String
  delete_old_rdn : Bool
  new_superior : String?
} derive(Eq, @debug.Debug)

///|
pub fn ModifyDnRequest::new(
  entry : String,
  newrdn : String,
  delete_old_rdn : Bool,
  new_superior : String?,
) -> ModifyDnRequest {
  { entry, newrdn, delete_old_rdn, new_superior }
}

///|
/// The response to a modifyDN operation (RFC 4511 4.10).
pub struct ModifyDnResponse {
  result : LdapResult
} derive(Eq, @debug.Debug)

///|
pub fn ModifyDnResponse::new(result : LdapResult) -> ModifyDnResponse {
  { result, }
}

///|
/// AttributeValueAssertion used by CompareRequest (RFC 4511 4.11).
pub struct AttributeValueAssertion {
  attribute_desc : String
  assertion_value : Bytes
} derive(Eq, @debug.Debug)

///|
pub fn AttributeValueAssertion::new(
  attribute_desc : String,
  assertion_value : Bytes,
) -> AttributeValueAssertion {
  { attribute_desc, assertion_value }
}

///|
/// CompareRequest (RFC 4511 4.11): assert an attribute value on an entry.
pub struct CompareRequest {
  entry : String
  ava : AttributeValueAssertion
} derive(Eq, @debug.Debug)

///|
pub fn CompareRequest::new(
  entry : String,
  ava : AttributeValueAssertion,
) -> CompareRequest {
  { entry, ava }
}

///|
/// The response to a compare operation; compareTrue / compareFalse carry the
/// assertion outcome (RFC 4511 4.12).
pub struct CompareResponse {
  result : LdapResult
} derive(Eq, @debug.Debug)

///|
pub fn CompareResponse::new(result : LdapResult) -> CompareResponse {
  { result, }
}