///|
fn entities_equal(left : Entity, right : Entity) -> Bool {
  if left.id != right.id || left.name.to_upper() != right.name.to_upper() {
    return false
  }
  if left.arguments.length() != right.arguments.length() {
    return false
  }
  for index in 0.. Bool {
  self.changes.length() == 0
}

///|
pub fn ModelDiff::changed_ids(self : ModelDiff) -> Array[Int] {
  self.changes.map(change => change.entity_id)
}

///|
pub fn ModelDiff::summary(self : ModelDiff) -> String {
  "added=" +
  self.added.to_string() +
  ", removed=" +
  self.removed.to_string() +
  ", modified=" +
  self.modified.to_string()
}

///|
pub fn diff_models(left : Model, right : Model) -> ModelDiff {
  let left_index = left.index()
  let right_index = right.index()
  let changes : Array[ModelChange] = []
  let mut added = 0
  let mut removed = 0
  let mut modified = 0
  for entity in left.entities {
    match right_index.entity(entity.id) {
      None => {
        changes.push(ModelChange::{
          kind: Removed,
          entity_id: entity.id,
          before: Some(entity),
          after: None,
        })
        removed += 1
      }
      Some(other) if !entities_equal(entity, other) => {
        changes.push(ModelChange::{
          kind: Modified,
          entity_id: entity.id,
          before: Some(entity),
          after: Some(other),
        })
        modified += 1
      }
      _ => ()
    }
  }
  for entity in right.entities {
    if !left_index.has(entity.id) {
      changes.push(ModelChange::{
        kind: Added,
        entity_id: entity.id,
        before: None,
        after: Some(entity),
      })
      added += 1
    }
  }
  { changes, added, removed, modified }
}

///|
pub fn compare_step(
  left : String,
  right : String,
) -> ModelDiff raise ParseError {
  diff_models(parse(left), parse(right))
}