//
// Copyright (c) 2006, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
//   30 Jan 06  Brian Frank  Creation
//

**
** Map is a hash map of key/value pairs.
**
** See [examples]`examples::sys-maps`.
**
@Serializable
final class Map
{

//////////////////////////////////////////////////////////////////////////
// Constructor
//////////////////////////////////////////////////////////////////////////

  **
  ** Constructor for type (must be Map type).
  **
  new make(Type type)

//////////////////////////////////////////////////////////////////////////
// Identity
//////////////////////////////////////////////////////////////////////////

  **
  ** Two Maps are equal if they have the same type and same number
  ** of equal key/value pairs.
  **
  ** Examples:
  **   a := Int:Str[1:"one", 2:"two"]
  **   b := Int:Str[2:"two", 1:"one"]
  **   c := Int:Str?[2:"two", 1:"one"]
  **   a == b  =>  true
  **   a == c  =>  false
  **
  override Bool equals(Obj? that)

  **
  ** Return platform dependent hashcode based on hash of the keys and values.
  **
  override Int hash()

//////////////////////////////////////////////////////////////////////////
// Methods
//////////////////////////////////////////////////////////////////////////

  **
  ** Return if size() == 0.  This method is readonly safe.
  **
  Bool isEmpty()

  **
  ** Get the number of key/value pairs in the list.  This
  ** method is readonly safe.
  **
  Int size()

  **
  ** Get the value for the specified key.  If key is not mapped,
  ** then return the value of the def parameter.  If def is omitted
  ** it defaults to the `def` field.  This method is readonly safe.
  ** Shortcut is 'a[key]'.
  **
  @Operator V? get(K key, V? def := this.def)

  **
  ** Get the value for the specified key.  If the key is not
  ** mapped then return null or raise UnknownKeyErr based on checked
  ** flag.  This method is readonly safe.
  **
  V? getChecked(K key, Bool checked := true)

  **
  ** Get the value for the specified key or if key is not mapped
  ** then raise UnknownKeyErr.  This method is readonly safe.
  **
  V getOrThrow(K key)

  **
  ** Return if the specified key is mapped.
  ** This method is readonly safe.
  **
  Bool containsKey(K key)

  **
  ** Get a list of all the mapped keys.  This method is readonly safe.
  **
  K[] keys()

  **
  ** Get a list of all the mapped values.  This method is readonly safe.
  **
  V[] vals()

  **
  ** Create a shallow duplicate copy of this map.  The keys and
  ** values themselves are not duplicated.  The resulting map is
  ** always read/write.  This method is readonly safe.
  **
  M dup()

  **
  ** Set the value for the specified key.  If the key is already
  ** mapped, this overwrites the old value.  If key is not yet mapped
  ** this adds the key/value pair to the map.  Return this.  If key
  ** does not return true for Obj.isImmutable, then throw NotImmutableErr.
  ** If key is null throw NullErr.  Throw ReadonlyErr if readonly.
  **
  @Operator M set(K key, V val)

  **
  ** Add the specified key/value pair to the map.  If the key is
  ** already mapped, then throw ArgErr.  Return this.  If key
  ** does not return true for Obj.isImmutable, then throw NotImmutableErr.
  ** If key is null throw NullErr.  Throw ReadonlyErr if readonly.
  **
  M add(K key, V val)

  ** Deprecated - use addNotNull
  @Deprecated { msg = "Use addNotNull" }
  M addIfNotNull(K key, V? val)

  **
  ** Call `add` if val is non-null otherwise do nothing.  Return this.
  **
  M addNotNull(K key, V? val)

  **
  ** Get the value for the specified key, or if it doesn't exist
  ** then automatically add it.  The value function is called to
  ** get the value to add, it is only called if the key is not
  ** mapped. Throw ReadonlyErr if readonly only if add is required.
  **
  V getOrAdd(K key, |K->V| valFunc)

  **
  ** Append the specified map to this map by setting every key/value in
  ** m in this map.  Keys in m not yet mapped are added and keys already
  ** mapped are overwritten.  Return this.  Throw ReadonlyErr if readonly.
  ** Also see `addAll`.  This method is semanatically equivalent to:
  **   m.each |v, k| { this.set(k, v) }
  **
  M setAll(M m)

  **
  ** Append the specified map to this map by adding every key/value in
  ** m in this map.  If any key in m is already mapped then this method
  ** will fail (any previous keys will remain mapped potentially leaving
  ** this map in an inconsistent state).  Return this.  Throw ReadonlyErr if
  ** readonly.  Also see `setAll`. This method is semanatically equivalent to:
  **   m.each |v, k| { this.add(k, v) }
  **
  M addAll(M m)

  **
  ** Add the specified list to this map where the values are the list items
  ** and the keys are derived by calling the specified function on each item.
  ** If the function is null, then the items themselves are used as the keys.
  ** If any key is already mapped then it is overwritten.  Return this.  Throw
  ** ReadonlyErr if readonly.  Also see `addList`.
  **
  ** Examples:
  **   m := [0:"0", 2:"old"]
  **   m.setList(["1","2"]) |Str s->Int| { return s.toInt }
  **   m  =>  [0:"0", 1:"1", 2:"2"]
  **
  M setList(V[] list, |V item, Int index->K|? c := null)

  **
  ** Add the specified list to this map where the values are the list items
  ** and the keys are derived by calling the specified function on each item.
  ** If the function is null, then the items themselves are used as the keys.
  ** If any key is already mapped then this method will fail (any previous keys
  ** will remain mapped, potentially leaving this map in an inconsistent state).
  ** Return this.  Throw ReadonlyErr if readonly.  Also see `setList`.
  **
  ** Examples:
  **   m := [0:"0"]
  **   m.addList(["1","2"]) |Str s->Int| { return s.toInt }
  **   m  =>  [0:"0", 1:"1", 2:"2"]
  **
  M addList(V[] list, |V item, Int index->K|? c := null)

  **
  ** Remove the key/value pair identified by the specified key
  ** from the map and return the value.   If the key was not mapped
  ** then return null.  Throw ReadonlyErr if readonly.
  **
  V? remove(K key)

  **
  ** Remove all key/value pairs from the map.  Return this.
  ** Throw ReadonlyErr if readonly.
  **
  M clear()

  **
  ** This field configures case sensitivity for maps with Str keys.  When
  ** set to true, Str keys are compared without regard to case for the following
  ** methods:  get, containsKey, set, add, setAll, addAll, and remove methods.
  ** Only ASCII character case is taken into account.  The original case
  ** is preserved (keys aren't made all lower or upper case).  This field
  ** defaults to false.
  **
  ** Getting this field is readonly safe.  If you attempt to set this method
  ** on a map which is not empty or not typed to use Str keys, then throw
  ** UnsupportedOperation.  Throw ReadonlyErr if set when readonly.  This
  ** mode cannot be used concurrently with `ordered`.
  **
  Bool caseInsensitive := false

  **
  ** When set to true, the map maintains the order in which key/value
  ** pairs are added to the map.  The implementation is based on using
  ** a linked list in addition to the normal hashmap.  This field defaults
  ** to false.
  **
  ** Getting this field is readonly safe.  If you attempt to set this method
  ** on a map which is not empty, then throw UnsupportedOperation.  Throw
  ** ReadonlyErr if set when readonly.  This mode cannot be used concurrently
  ** with `caseInsensitive`.
  **
  Bool ordered := false

  **
  ** The default value to use for `get` when a key isn't mapped.
  ** This field defaults to null.  The value of 'def' must be immutable
  ** or NotImmutableErr is thrown.  Getting this field is readonly safe.
  ** Throw ReadonlyErr if set when readonly.
  **
  V? def

//////////////////////////////////////////////////////////////////////////
// Str
//////////////////////////////////////////////////////////////////////////

  **
  ** Return a string representation of the Map.  This method is readonly safe.
  **
  override Str toStr()

  **
  ** Return a string by concatenating each key/value pair using
  ** the specified separator string.  If c is non-null then it
  ** is used to format each pair into a string, otherwise "$k: $v"
  ** is used.  This method is readonly safe.
  **
  ** Example:
  **   m := ["a" : 1, "b" : 2]
  **   m.join(" and ") |Int v, Str k->Str| { return "$v from $k" }
  **     => 1 from a and 2 from b
  **
  Str join(Str separator, |V val, K key->Str|? c := null)

  **
  ** Get this map as a Fantom expression suitable for code generation.
  ** The individual keys and values must all respond to the 'toCode' method.
  **
  Str toCode()

//////////////////////////////////////////////////////////////////////////
// Iterators
//////////////////////////////////////////////////////////////////////////

  **
  ** Call the specified function for every key/value pair in the map.
  ** This method is readonly safe.
  **
  Void each(|V val, K key| c)

  **
  ** Iterate every key/value pair in the map until the function
  ** returns non-null.  If function returns non-null, then break
  ** the iteration and return the resulting object.  Return null
  ** if the function returns null for every key/value pair.
  ** This method is readonly safe.
  **
  Obj? eachWhile(|V val, K key->Obj?| c)

  **
  ** Return the first value in the map for which c returns true.
  ** If c returns false for every pair, then return null.  This
  ** method is readonly safe.
  **
  V? find(|V val, K key->Bool| c)

  **
  ** Return a new map containing the key/value pairs for which c
  ** returns true.  If c returns false for every item, then return
  ** an empty map.  The inverse of this method is `exclude`.  If
  ** this map is `ordered` or `caseInsensitive`, then the resulting
  ** map is too.  This method is readonly safe.
  **
  M findAll(|V val, K key->Bool| c)

  **
  ** Return a new map containing all the key/value pairs where
  ** value is not null.  If this map is `ordered` or `caseInsensitive`,
  ** then the resulting map is too.  This method is readonly safe.
  **
  M findNotNull()

  **
  ** Return a new map containing the key/value pairs for which c
  ** returns false.  If c returns true for every item, then return
  ** an empty map.  The inverse of this method is `findAll`.  If
  ** this map is `ordered` or `caseInsensitive`, then the resulting
  ** map is too.  This method is readonly safe.
  **
  ** Example:
  **   map := ["off":0, "slow":50, "fast":100]
  **   map.exclude |Int v->Bool| { return v == 0 } => ["slow":50, "fast":100]
  **
  M exclude(|V val, K key->Bool| c)

  **
  ** Return true if c returns true for any of the key/value pairs
  ** in the map.  If the map is empty, return false.  This method
  ** is readonly safe.
  **
  Bool any(|V val, K key->Bool| c)

  **
  ** Return true if c returns true for all of the key/value pairs
  ** in the map.  If the list is empty, return true.  This method
  ** is readonly safe.
  **
  Bool all(|V val, K key->Bool| c)

  **
  ** Reduce is used to iterate through every key/value pair in the map
  ** to reduce the map into a single value called the reduction.
  ** The initial value of the reduction is passed in as the init
  ** parameter, then passed back to the closure along with each
  ** key/value pair.  This method is readonly safe.
  **
  ** Example:
  **   m := ["2":2, "3":3, "4":4]
  **   m.reduce(100) |Obj r, Int v->Obj| { return (Int)r + v } => 109
  **
  Obj? reduce(Obj? init, |Obj? reduction, V val, K key->Obj?| c)

  **
  ** Create a new map with the same keys, but apply the specified
  ** closure to generate new values.  The new map is typed based
  ** on the return type of c.  If this map is `ordered` or
  ** `caseInsensitive`, then the resulting map is too.  This method
  ** is readonly safe.
  **
  ** Example:
  **   m := [2:2, 3:3, 4:4]
  **   x := m.map |Int v->Int| { return v*2 }
  **   x => [2:4, 3:6, 4:8]
  **
  Obj:Obj? map(|V val, K key->Obj?| c)

  **
  ** Convenience for `map` and `findNotNull`.  Each key/value pair is
  ** mapped by the given function and if null is returned it is
  ** excluded from the result.  The resulting type is based on
  ** the return type of c but non-nullable.  This method is
  ** readonly safe.
  **
  Obj:Obj? mapNotNull(|V val, K key->Obj?| c)

//////////////////////////////////////////////////////////////////////////
// Readonly
//////////////////////////////////////////////////////////////////////////

  **
  ** Return if this Map is readonly.  A readonly Map is guaranteed
  ** to be immutable (although its values may be mutable themselves).
  ** Any attempt to modify a readonly Map will result in ReadonlyErr.
  ** Use `rw` to get a read-write Map from a readonly Map.  Methods
  ** documented as "readonly safe" may be used safely with a readonly Map.
  ** This method is readonly safe.
  **
  Bool isRO()

  **
  ** Return if this Map is read-write.  A read-write Map is mutable
  ** and may be modified.  Use `ro` to get a readonly Map from a
  ** read-write Map.  This method is readonly safe.
  **
  Bool isRW()

  **
  ** Get a readonly Map instance with the same contents as this
  ** Map (although its values may be mutable themselves).  If this
  ** Map is already readonly, then return this.  Only methods
  ** documented as "readonly safe" may be used safely with a readonly
  ** Map, all others will throw ReadonlyErr.  This method is
  ** readonly safe.
  **
  M ro()

  **
  ** Get a read-write, mutable Map instance with the same contents
  ** as this Map.  If this Map is already read-write, then return this.
  ** This method is readonly safe.
  **
  M rw()

}