/*  CSci 658, Software Language Engineering 
    Computer Configuration Semantic Model
    H. Conrad Cunningham

1234567890123456789012345678901234567890123456789012345678901234567890

2009-04-11: (V1) Original
2009-04-13: (V2) Made "theDisks" val of type ListBuffer.
                 Changed from case objects to Enumerations.
2009-05-13: (V2a) Minor updates to Scala programming style.
2018-02-02: (V2b) Updated comments. Created compile script.

This is a Scala implementation of a Semantic Model compatible with two
of Martin Fowler's Computer Configuration DSLs given in his book
Domain-Specific Languages.  In particular, it is designed to be
compatible with the examples given in both the Method Chaining and
Nested Closure chapters.

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")
  }
}
