/*  Engr 691-6, Special Topics, Software Language Engineering
    Fowler's Computer Configuration Semantic Model
    H. C. Cunningham
    Version 1:  11 April 2009
    Version 2:  12 April 2009 Made "theDisks" val of type ListBuffer.
                              Changed from case objects to Enumerations.
    Version 2a: 13 May 2009   Minor updates to Scala programming style.

123456789012345678901234567890123456789012345678901234567890123456789012345678

This is a Scala implementation of a Semantic Model compatible with two
of Martin Fowler's Computer Configuration DSLs given in his
in-progress DSL book.  In particular, it is designed to be compatible
with the examples given in both the Method Chaining and Nested Closure
chapters of http://www.martinfowler.com/dslwip/.

The overall implementation in Scala can probably be redesigned to take
better advantage of Scala features.

*/

/* Semantic Model classes "Computer", "Processor", and "Disk" hold the
   information on the configuration of those entities.  The companion
   objects for "Processor" and "Disk" mainly hold constants associated
   with those classes.

   This Semantic Model is a superset of the model used in the simpler
   CompConfigChaining example (Method Chaining chapter), adding
   auxiliary constructors, mutators (setters), and accessors (getters)
   for the attributes of the objects as needed in the
   CompConfigClosure example (Nested Closures chapter).

*/

import scala.collection.mutable.ListBuffer

class Computer(proc: Processor, disks: Disk*) {

  private var theProc  = proc
  private val theDisks = new ListBuffer[Disk]
  theDisks.insertAll(0,disks)

  // Auxiliary parameterless constructor
  def this() = this(null)

  // Mutator methods
  def setProcessor(proc: Processor) { theProc  =  proc  }
  def addDisk(disk: Disk)           { theDisks += disk  }

  // Accessor methods
  def getProcessor: Processor = theProc
  def getDisks: Array[Disk]   = theDisks.toArray

  override def toString = 
    "Computer configured with \n  " + theProc + 
     "\nand " + theDisks.length + " disks\n" +
     theDisks.mkString("  ", "\n  ", "\n")
}


class Processor(cores: Int, ptype: Processor.Type.Value, pspeed: Double) {

  private var theCores = cores
  private var theType  = ptype
  private var theSpeed = pspeed

  // Auxiliary parameterless constructor
  def this() { this(Processor.DEFAULT_CORES,Processor.Type.UNKNOWN,
                    Processor.UNKNOWN_SPEED) }

  // Auxiliary constructor with first two arguments
  def this(cores: Int, ptype: Processor.Type.Value) {
    this(cores, ptype, Processor.UNKNOWN_SPEED) }

  // Mutator methods
  def setCores(cores: Int)                 { theCores = cores  }
  def setType(ptype: Processor.Type.Value) { theType  = ptype  }
  def setSpeed(pspeed: Double)             { theSpeed = pspeed }

  // Accessor methods
  def getCores: Int                 = theCores
  def getType: Processor.Type.Value = theType
  def getSpeed: Double              = theSpeed

  override def toString = 
    "Processor of type " + theType + " with " + theCores + 
    " cores and speed " + theSpeed 
}

object Processor {
  val DEFAULT_CORES = 1
  val UNKNOWN_SPEED = 0.0
  object Type extends Enumeration {
    val i386    = Value("i386")
    val UNKNOWN = Value("UNKNOWN")
  }
}


class Disk(size: Int, speed: Int, iface: Disk.Interface.Value) {

  private var theSize  = size
  private var theSpeed = speed
  private var theIface = iface

  // Auxiliary parameterless constructor
  def this() { this(Disk.UNKNOWN_SIZE, Disk.UNKNOWN_SPEED, 
                    Disk.Interface.UNKNOWN) }

  // Mutator methods
  def setSize(size: Int)   { theSize = size   }
  def setSpeed(speed: Int) { theSpeed = speed }
  def setIface(iface: Disk.Interface.Value) { theIface = iface }

  // Accessor methods
  def getSize: Int                   = theSize
  def getSpeed: Int                  = theSpeed
  def getIface: Disk.Interface.Value = theIface

  override def toString = 
    "Disk with size " + theSize + ", speed " + theSpeed + 
    ", and interface " + theIface
}

object Disk {
  val UNKNOWN_SIZE  = -1
  val UNKNOWN_SPEED = -1
  object Interface extends Enumeration {
    val SATA    = Value("SATA")
    val UNKNOWN = Value("UNKNOWN")
  }
}
