/* CookieJar ADT, Immutable Method-Chaining Functions
   Using Immutable Scala HashMaps
   CSci 555: Functional Programming, Spring 2016
   H. Conrad Cunningham, Professor
   Computer and Information Science
   University of Mississippi

1234567890123456789012345678901234567890123456789012345678901234567890

2016-03-09: Developed immutable Scala version from 2010-12
            mutable Scala version
2022-04-18: Scala 3 compatibility check, minor comment changes

*/

/*
Class ICookieJarMap implements the CookieJar ADT using Scala's
immutable HashMap to represent the jar (mathematical bag) of cookies.
The keys of the Map are the cookie types and the value is the count of
cookies of that type in the jar.  If the count is zero, then that key
does not appear in the map.

   IMPLEMENTATION INVARIANT: 
     (ForAll c, i : c IN CookieType && i IN Int ::
       i == OCCURRENCES(c,Bag(this))  <=> (
         (i > 0  => jar(c) = i) && (i == 0 => !jar.contains(c))) )

See the comments in ICookieJar.scala and ICookieJarList.scala for more
information on this family of ADTs.

TODO: Consider whether the primary constructor should be "private".

*/

import scala.collection.immutable.HashMap

class ICookieJarMap[CookieType] 
  (private val jar: HashMap[CookieType,Int])
  extends ICookieJar[CookieType] {

  def putIn(cookie: CookieType): ICookieJar[CookieType] =
    if (jar.contains(cookie))
      new ICookieJarMap(jar.updated(cookie,jar(cookie)+1))
    else 
      new ICookieJarMap(jar.updated(cookie,1))
 
  def eat(cookie: CookieType): ICookieJar[CookieType] =
    if (jar.contains(cookie)) {
      val nc = jar(cookie)
      if (nc > 1)
        new ICookieJarMap(jar.updated(cookie,nc-1))
      else
        new ICookieJarMap(jar-cookie)
    }
    else 
      sys.error("Attempt to eat cookie \"" + cookie +
                "\", which is not in the jar.")
      
  def isEmpty: Boolean = jar.isEmpty

  def has(cookie: CookieType): Boolean = jar.contains(cookie)

  /* Redefine toString to return form "ICookieJar(e1,e2,e3,...,en)"
     with all elements of bag in arbitrary order.
  */

  override def toString = "ICookieJar(" + jarList.mkString(",") + ")"


  /* Private method jarList converts the map jar to a list with with
     appropriate duplicates.
  */

  private def jarList: List[CookieType] = 
    for ((k,c) <- jar.toList; i <- 1 to c) yield k 

}

