///|
fn valid_transform_name(value : String) -> Bool {
  if value.length() == 0 || !is_identifier_start(value[0].to_int()) {
    return false
  }
  for index in 1.. Bool {
  for node in database.nodes() {
    if node.name() == name {
      return true
    }
  }
  false
}

///|
fn renamed_receivers(
  values : Array[String],
  before : String,
  after : String,
) -> Array[String] {
  let result : Array[String] = []
  for value in values {
    result.push(if value == before { after } else { value })
  }
  result
}

///|
pub fn rename_node(
  database : DbcDatabase,
  before : String,
  after : String,
) -> Result[DbcDatabase, DbcError] {
  if !valid_transform_name(after) {
    return Err(
      DbcError::new(
        InvalidIdentifier,
        "replacement node name is invalid",
        1,
        1,
        context=after,
      ),
    )
  }
  if !has_node(database, before) {
    return Err(
      DbcError::new(
        InvalidIdentifier,
        "node to rename does not exist",
        1,
        1,
        context=before,
      ),
    )
  }
  if before != after && has_node(database, after) {
    return Err(
      DbcError::new(
        InvalidIdentifier,
        "replacement node already exists",
        1,
        1,
        context=after,
      ),
    )
  }
  let nodes : Array[DbcNode] = []
  for node in database.nodes() {
    nodes.push(
      DbcNode::new(if node.name() == before { after } else { node.name() }),
    )
  }
  let messages : Array[DbcMessage] = []
  for original in database.messages() {
    let transmitter = if original.transmitter() == before {
      after
    } else {
      original.transmitter()
    }
    let signals : Array[DbcSignal] = []
    for signal in original.signals() {
      signals.push(
        signal.with_receivers(
          renamed_receivers(signal.receivers(), before, after),
        ),
      )
    }
    messages.push(original.with_transmitter(transmitter).with_signals(signals))
  }
  Ok(
    DbcDatabase::new(
      database.version(),
      nodes,
      messages,
      ignored_statement_count=database.ignored_statement_count(),
    ),
  )
}

///|
pub fn rename_message(
  database : DbcDatabase,
  id : Int,
  after : String,
) -> Result[DbcDatabase, DbcError] {
  if !valid_transform_name(after) {
    return Err(
      DbcError::new(
        InvalidIdentifier,
        "replacement message name is invalid",
        1,
        1,
        context=after,
      ),
    )
  }
  if database.find_message_by_id(id) is None {
    return Err(
      DbcError::new(
        UnknownMessage,
        "message to rename does not exist",
        1,
        1,
        context=id.to_string(),
      ),
    )
  }
  match database.find_message_by_name(after) {
    Some(existing) if existing.id() != id =>
      return Err(
        DbcError::new(
          DuplicateMessageName,
          "replacement message name already exists",
          1,
          1,
          context=after,
        ),
      )
    _ => ()
  }
  let messages : Array[DbcMessage] = []
  for message in database.messages() {
    messages.push(
      if message.id() == id {
        message.with_name(after)
      } else {
        message
      },
    )
  }
  Ok(
    DbcDatabase::new(
      database.version(),
      database.nodes(),
      messages,
      ignored_statement_count=database.ignored_statement_count(),
    ),
  )
}

///|
pub fn rename_signal(
  database : DbcDatabase,
  message_id : Int,
  before : String,
  after : String,
) -> Result[DbcDatabase, DbcError] {
  if !valid_transform_name(after) {
    return Err(
      DbcError::new(
        InvalidIdentifier,
        "replacement signal name is invalid",
        1,
        1,
        context=after,
      ),
    )
  }
  let target = match database.find_message_by_id(message_id) {
    Some(value) => value
    None =>
      return Err(
        DbcError::new(UnknownMessage, "signal message does not exist", 1, 1),
      )
  }
  if target.find_signal(before) is None {
    return Err(
      DbcError::new(
        UnknownSignal,
        "signal to rename does not exist",
        target.source_line(),
        1,
        context=before,
      ),
    )
  }
  if after != before && target.find_signal(after) is Some(_) {
    return Err(
      DbcError::new(
        DuplicateSignalName,
        "replacement signal name already exists",
        target.source_line(),
        1,
        context=after,
      ),
    )
  }
  let messages : Array[DbcMessage] = []
  for message in database.messages() {
    if message.id() != message_id {
      messages.push(message)
    } else {
      let signals : Array[DbcSignal] = []
      for signal in message.signals() {
        signals.push(
          if signal.name() == before {
            signal.with_name(after)
          } else {
            signal
          },
        )
      }
      messages.push(message.with_signals(signals))
    }
  }
  Ok(
    DbcDatabase::new(
      database.version(),
      database.nodes(),
      messages,
      ignored_statement_count=database.ignored_statement_count(),
    ),
  )
}

///|
fn node_used(messages : Array[DbcMessage], name : String) -> Bool {
  for message in messages {
    if message.transmitter() == name {
      return true
    }
    for signal in message.signals() {
      if signal_has_receiver(signal, name) {
        return true
      }
    }
  }
  false
}

///|
pub fn select_database_messages(
  database : DbcDatabase,
  ids : Array[Int],
) -> Result[DbcDatabase, DbcError] {
  let messages : Array[DbcMessage] = []
  for index, id in ids {
    for previous in 0.. messages.push(message)
      None =>
        return Err(
          DbcError::new(
            UnknownMessage,
            "selected message does not exist",
            1,
            1,
            context=id.to_string(),
          ),
        )
    }
  }
  let nodes : Array[DbcNode] = []
  for node in database.nodes() {
    if node_used(messages, node.name()) {
      nodes.push(node)
    }
  }
  Ok(DbcDatabase::new(database.version(), nodes, messages))
}

///|
fn combined_nodes(
  left : Array[DbcNode],
  right : Array[DbcNode],
) -> Array[DbcNode] {
  let result = left.copy()
  for node in right {
    let mut exists = false
    for current in result {
      if current.name() == node.name() {
        exists = true
        break
      }
    }
    if !exists {
      result.push(node)
    }
  }
  result
}

///|
pub fn merge_databases(
  left : DbcDatabase,
  right : DbcDatabase,
) -> Result[DbcDatabase, DbcError] {
  let messages = left.messages()
  for message in right.messages() {
    if left.find_message_by_id(message.id()) is Some(_) {
      return Err(
        DbcError::new(
          DuplicateMessageId,
          "merged database repeats a message id",
          message.source_line(),
          1,
          context=message.id().to_string(),
        ),
      )
    }
    if left.find_message_by_name(message.name()) is Some(_) {
      return Err(
        DbcError::new(
          DuplicateMessageName,
          "merged database repeats a message name",
          message.source_line(),
          1,
          context=message.name(),
        ),
      )
    }
    messages.push(message)
  }
  let version = if left.version() == right.version() {
    left.version()
  } else {
    ""
  }
  Ok(
    DbcDatabase::new(
      version,
      combined_nodes(left.nodes(), right.nodes()),
      messages,
      ignored_statement_count=left.ignored_statement_count() +
        right.ignored_statement_count(),
    ),
  )
}