/* CookieJarMap implementation of the CookieJar ADT
   H. Conrad Cunningham
   Version #1:  15 March 2010
   Version #1a: 22 March 2010 Correct Implementation Invariant statement

123456789012345678901234567890123456789012345678901234567890123456789012345678

*/

import scala.collection.mutable._


/* Class CookieJarMap implements the CookieJar ADT using Scala's
   default mutable Map (which is a HashMap) to implement 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)  <=> (
         (i > 0  => jar(c) = i) && (i == 0 => !jar.contains(c))) )
*/

class CookieJarMap[CookieType] extends CookieJar[CookieType] {

  // CookieJar's bag model is implemented by Map jar.
  private val jar = Map[CookieType,Int]()
 
  def putIn(cookie: CookieType) { 
    if (jar.contains(cookie)) 
      jar(cookie) += 1
    else 
      jar(cookie) = 1
  }
 
  def eat(cookie: CookieType) {
    if (jar.contains(cookie)) {
      if (jar(cookie) > 1)
        jar(cookie) -= 1
      else
        jar -= cookie
    }
    else 
      throw new RuntimeException(
        "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 "CookieJar(e1,e2,e3,...,en)" with all 
     elements of bag in arbitrary order. 
  */

  override def toString = "CookieJar(" + 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 

}

