///|
pub fn PEImage::diff(self : Self, other : Self) -> Array[DiffItem] {
  let changes = []
  compare_field(
    changes,
    "format",
    format_text(self.format),
    format_text(other.format),
  )
  compare_field(
    changes,
    "machine",
    architecture_name(self.machine),
    architecture_name(other.machine),
  )
  compare_field(
    changes,
    "subsystem",
    subsystem_name(self.subsystem),
    subsystem_name(other.subsystem),
  )
  compare_field(
    changes,
    "image-base",
    self.image_base.to_string(),
    other.image_base.to_string(),
  )
  compare_field(
    changes,
    "entry-point",
    self.entry_point.to_string(),
    other.entry_point.to_string(),
  )
  compare_field(
    changes,
    "image-size",
    self.size_of_image.to_string(),
    other.size_of_image.to_string(),
  )
  compare_field(
    changes,
    "dll-characteristics",
    self.dll_characteristics.to_string(),
    other.dll_characteristics.to_string(),
  )
  compare_field(
    changes,
    "sections",
    sections_signature(self),
    sections_signature(other),
  )
  compare_field(
    changes,
    "imports",
    imports_signature(self),
    imports_signature(other),
  )
  compare_field(
    changes,
    "exports",
    exports_signature(self),
    exports_signature(other),
  )
  compare_field(
    changes,
    "directories",
    directories_signature(self),
    directories_signature(other),
  )
  compare_field(
    changes,
    "security-diagnostics",
    diagnostics_signature(self),
    diagnostics_signature(other),
  )
  changes
}

///|
fn compare_field(
  changes : Array[DiffItem],
  field : String,
  before : String,
  after : String,
) -> Unit {
  if before != after {
    changes.push({ field, before, after, })
  }
}

///|
fn sections_signature(image : PEImage) -> String {
  let values = []
  for section in image.sections {
    values.push(
      "\{section.name}:\{section.virtual_size}/\{section.raw_size}/\{section.characteristics}",
    )
  }
  values.join(",")
}

///|
fn imports_signature(image : PEImage) -> String {
  try {
    let values = []
    for library in image.imports() {
      values.push("\{library.name}(\{library.symbols.length()})")
    }
    values.join(",")
  } catch {
    PeError(_, _) => ""
  }
}

///|
fn exports_signature(image : PEImage) -> String {
  try {
    let values = []
    for symbol in image.exports() {
      values.push(symbol.name)
    }
    values.join(",")
  } catch {
    PeError(_, _) => ""
  }
}

///|
fn directories_signature(image : PEImage) -> String {
  let values = []
  for directory in image.directories {
    if directory.virtual_address != 0UL && directory.size > 0 {
      values.push(directory.name)
    }
  }
  values.join(",")
}

///|
fn diagnostics_signature(image : PEImage) -> String {
  let values = []
  for diagnostic in image.security_diagnostics() {
    values.push(diagnostic.code)
  }
  values.join(",")
}

///|
fn format_text(format : PEFormat) -> String {
  if format is PE32 {
    "PE32"
  } else {
    "PE32+"
  }
}