///|
/// Content-Disposition header (RFC 6266)
pub(all) struct ContentDisposition {
  disposition_type : DispositionType
  filename : String?
  filename_ext : String?
  name : String?
  parameters : Array[(String, String)]
} derive(Eq)

///|
pub fn ContentDisposition::parse(
  input : String,
) -> Result[ContentDisposition, ContentDispositionError] {
  let s = cd_trim_string(input)
  if s.length() == 0 {
    return Err(Empty)
  }
  let parts = cd_split_params(s)
  if parts.length() == 0 {
    return Err(InvalidFormat)
  }
  let type_str = cd_to_lower_case(cd_trim_string(parts[0]))
  let disp_type = cd_parse_disposition_type(type_str)
  match disp_type {
    Err(e) => return Err(e)
    Ok(dt) => {
      let mut cd = {
        disposition_type: dt,
        filename: None,
        filename_ext: None,
        name: None,
        parameters: [],
      }
      let mut part_idx = 1
      while part_idx < parts.length() {
        let part = cd_trim_string(parts[part_idx])
        if part.length() > 0 {
          let eq_pos = cd_find_char(part, 61) // =
          match eq_pos {
            Some(pos) => {
              let param_name = cd_to_lower_case(
                cd_trim_string(cd_string_slice(part, 0, pos)),
              )
              let param_value = cd_trim_string(
                cd_string_slice_from(part, pos + 1),
              )
              match param_name {
                "filename" => {
                  let parse_result = cd_parse_param_value(param_value)
                  match parse_result {
                    Ok(value) =>
                      cd = {
                        disposition_type: cd.disposition_type,
                        filename: Some(value),
                        filename_ext: cd.filename_ext,
                        name: cd.name,
                        parameters: cd.parameters,
                      }
                    Err(e) => return Err(e)
                  }
                }
                "filename*" => {
                  let parse_result = cd_parse_ext_value(param_value)
                  match parse_result {
                    Ok(value) =>
                      cd = {
                        disposition_type: cd.disposition_type,
                        filename: cd.filename,
                        filename_ext: Some(value),
                        name: cd.name,
                        parameters: cd.parameters,
                      }
                    Err(e) => return Err(e)
                  }
                }
                "name" => {
                  let parse_result = cd_parse_param_value(param_value)
                  match parse_result {
                    Ok(value) =>
                      cd = {
                        disposition_type: cd.disposition_type,
                        filename: cd.filename,
                        filename_ext: cd.filename_ext,
                        name: Some(value),
                        parameters: cd.parameters,
                      }
                    Err(e) => return Err(e)
                  }
                }
                _ => {
                  let parse_result = cd_parse_param_value(param_value)
                  match parse_result {
                    Ok(value) => {
                      let new_params = []
                      let mut j = 0
                      while j < cd.parameters.length() {
                        new_params.push(cd.parameters[j])
                        j = j + 1
                      }
                      new_params.push((param_name, value))
                      cd = {
                        disposition_type: cd.disposition_type,
                        filename: cd.filename,
                        filename_ext: cd.filename_ext,
                        name: cd.name,
                        parameters: new_params,
                      }
                    }
                    Err(e) => return Err(e)
                  }
                }
              }
            }
            None => {
              let _ = ()
            }
          }
        }
        part_idx = part_idx + 1
      }
      Ok(cd)
    }
  }
}

///|
pub fn ContentDisposition::new(
  disposition_type : DispositionType,
) -> ContentDisposition {
  {
    disposition_type,
    filename: None,
    filename_ext: None,
    name: None,
    parameters: [],
  }
}

///|
pub fn ContentDisposition::disposition_type(
  self : ContentDisposition,
) -> DispositionType {
  self.disposition_type
}

///|
pub fn ContentDisposition::filename(self : ContentDisposition) -> String? {
  match self.filename_ext {
    Some(fe) => Some(fe)
    None => self.filename
  }
}

///|
pub fn ContentDisposition::filename_ascii(self : ContentDisposition) -> String? {
  self.filename
}

///|
pub fn ContentDisposition::filename_ext(self : ContentDisposition) -> String? {
  self.filename_ext
}

///|
pub fn ContentDisposition::name(self : ContentDisposition) -> String? {
  self.name
}

///|
pub fn ContentDisposition::parameter(
  self : ContentDisposition,
  name : String,
) -> String? {
  let name_lower = cd_to_lower_case(name)
  let mut idx = 0
  while idx < self.parameters.length() {
    let (k, v) = self.parameters[idx]
    if k == name_lower {
      return Some(v)
    }
    idx = idx + 1
  }
  None
}

///|
pub fn ContentDisposition::is_inline(self : ContentDisposition) -> Bool {
  match self.disposition_type {
    Inline => true
    _ => false
  }
}

///|
pub fn ContentDisposition::is_attachment(self : ContentDisposition) -> Bool {
  match self.disposition_type {
    Attachment => true
    _ => false
  }
}

///|
pub fn ContentDisposition::is_form_data(self : ContentDisposition) -> Bool {
  match self.disposition_type {
    FormData => true
    _ => false
  }
}

///|
pub fn ContentDisposition::with_filename(
  self : ContentDisposition,
  filename : String,
) -> ContentDisposition {
  {
    disposition_type: self.disposition_type,
    filename: Some(filename),
    filename_ext: self.filename_ext,
    name: self.name,
    parameters: self.parameters,
  }
}

///|
pub fn ContentDisposition::with_filename_ext(
  self : ContentDisposition,
  filename_ext : String,
) -> ContentDisposition {
  {
    disposition_type: self.disposition_type,
    filename: self.filename,
    filename_ext: Some(filename_ext),
    name: self.name,
    parameters: self.parameters,
  }
}

///|
pub fn ContentDisposition::with_name(
  self : ContentDisposition,
  name : String,
) -> ContentDisposition {
  {
    disposition_type: self.disposition_type,
    filename: self.filename,
    filename_ext: self.filename_ext,
    name: Some(name),
    parameters: self.parameters,
  }
}

///|
pub fn ContentDisposition::to_string(self : ContentDisposition) -> String {
  let mut result = self.disposition_type.to_string()
  match self.name {
    Some(n) => result = result + "; name=\"" + cd_escape_quoted_string(n) + "\""
    None => {
      let _ = ()
    }
  }
  match self.filename {
    Some(f) =>
      result = result + "; filename=\"" + cd_escape_quoted_string(f) + "\""
    None => {
      let _ = ()
    }
  }
  match self.filename_ext {
    Some(fe) =>
      result = result + "; filename*=UTF-8''" + cd_encode_ext_value(fe)
    None => {
      let _ = ()
    }
  }
  let mut idx = 0
  while idx < self.parameters.length() {
    let (name, value) = self.parameters[idx]
    result = result +
      "; " +
      name +
      "=\"" +
      cd_escape_quoted_string(value) +
      "\""
    idx = idx + 1
  }
  result
}