///| Data-section decoding exposes passive and active initialisation segments

///| without copying their byte payloads. Keeping source spans makes the API

///|
/// suitable for large modules and for tools that need to hash only one blob.
pub enum DataMode {
  ActiveData(Int, Span)
  PassiveData
}

///|
pub struct DataSegment {
  mode : DataMode
  bytes : Span
}

///|
pub fn DataSegment::byte_length(self : DataSegment) -> Int {
  self.bytes.size()
}

///|
pub fn DataSegment::description(self : DataSegment) -> String {
  match self.mode {
    ActiveData(memory, offset) =>
      "active memory=" +
      memory.to_string() +
      " offset=" +
      span_text(offset) +
      " bytes=" +
      self.byte_length().to_string()
    PassiveData => "passive bytes=" + self.byte_length().to_string()
  }
}

///|
fn read_blob_span(cursor : Cursor) -> Result[Span, DecodeError] {
  let size = match cursor.read_var_u32() {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let start = cursor.position
  match cursor.skip(size) {
    Ok(_) => Ok({ start, end: start + size })
    Err(_) =>
      Err(ValidationError(start, "data payload extends past section boundary"))
  }
}

///|
pub fn decode_data_segments(
  binary : Module,
) -> Result[Array[DataSegment], DecodeError] {
  let cursor = match section_cursor(binary, Data) {
    Some(value) => value
    None => return Ok([])
  }
  let count = match cursor.read_var_u32() {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let segments : Array[DataSegment] = []
  for _ in 0.. value
      Err(error) => return Err(error)
    }
    let mode = match flags {
      0 => {
        let offset = match skip_const_expr(cursor) {
          Ok(value) => value
          Err(error) => return Err(error)
        }
        ActiveData(0, offset)
      }
      1 => PassiveData
      2 => {
        let memory = match cursor.read_var_u32() {
          Ok(value) => value
          Err(error) => return Err(error)
        }
        let offset = match skip_const_expr(cursor) {
          Ok(value) => value
          Err(error) => return Err(error)
        }
        ActiveData(memory, offset)
      }
      _ =>
        return Err(
          UnsupportedFeature(
            "data segment flag " + flags.to_string(),
            flag_position,
          ),
        )
    }
    let bytes = match read_blob_span(cursor) {
      Ok(value) => value
      Err(error) => return Err(error)
    }
    segments.push({ mode, bytes })
  }
  match expect_finished(cursor, Data) {
    Ok(_) => Ok(segments)
    Err(error) => Err(error)
  }
}

///|
pub fn data_segment_bytes(binary : Module, segment : DataSegment) -> Array[Int] {
  let output : Array[Int] = []
  for position in segment.bytes.start.. String {
  let mut text = "data segments: " + segments.length().to_string() + "\n"
  let mut index = 0
  for segment in segments {
    text = text +
      "  [" +
      index.to_string() +
      "] " +
      segment.description() +
      "\n"
    index = index + 1
  }
  text
}