///|
enum NetworkConfig {
  LocalConfig(Map[(&Location, &Location), @channel.T[Json]])
  // HttpConfig
}

///|
pub fn make_local_config(locations : Array[&Location]) -> NetworkConfig {
  let channels = {}
  for from in locations {
    for to in locations {
      if from != to {
        let channel = @channel.make()
        channels[(from, to)] = channel
      }
    }
  }
  return LocalConfig(channels)
}

///|
pub async fn send[From : Location, To : Location, T : ToJson + @json.FromJson](
  self : NetworkConfig,
  from : From,
  to : To,
  msg : T
) -> Unit {
  match self {
    LocalConfig(channels) => {
      let channel = channels[(from, to)].unwrap()
      channel.push!(msg.to_json())
    }
  }
}

///|
pub async fn recv[From : Location, To : Location, T : ToJson + @json.FromJson](
  self : NetworkConfig,
  from : From,
  to : To
) -> T {
  match self {
    LocalConfig(channels) => {
      let channel = channels[(from, to)].unwrap()
      let msg = channel.pop!()
      try {
        let value = @json.from_json!(msg)
        return value
      } catch {
        _ => abort("Bug: deserialzing JSON failed. " + CONTACT_AUTHOR)
      }
    }
  }
}

///|
struct Unwrapper[_] {}

///|
pub fn Unwrapper::unwrap[T, L](_ : Unwrapper[L], v : Located[T, L]) -> T {
  unwrap(v)
}

///|
pub fn run[L : Location, T](
  self : NetworkConfig,
  loc : L,
  computation : (Unwrapper[L]) -> T
) -> T {
  let unwrapper = Unwrapper::{  }
  computation(unwrapper)
}

///|
pub async fn broadcast[From : Location, T : ToJson + @json.FromJson](
  self : NetworkConfig,
  from : From,
  msg : T
) -> Unit {
  match self {
    LocalConfig(channels) =>
      channels.iter2()
      |> into_aiter2
      |> AIter2::each!(async fn(from_to, channel) {
        if from_to.0 == from {
          channel.push!(msg.to_json())
        }
      })
  }
}