object Geom {
  trait Shape
  case class Point(x: Int, y: Int) extends Shape
  case class Rectangle(ll: Point, ur: Point) extends Shape {
    def inset(delta: Int) = 
      Rectangle(Point(ll.x - delta, ll.y - delta), Point(ur.x + delta, ur.y + delta));
  }
}

object Color {
  type Color = Int
  val black = 0x000000
  val grey  = 0x808080
}

trait Screen {
  type Color = Int
  def drawRect(r: Geom.Rectangle, c: Color): Unit
  def fillRect(r: Geom.Rectangle, c: Color): Unit
}

object DummyScreen extends Screen {
  def drawRect(r: Geom.Rectangle, c: Color) { 
    Console.println("draw " + r + " with " + c)
  }
  def fillRect(r: Geom.Rectangle, c: Color) {
    Console.println("fill " + r + " with " + c)
  }
}

object GUI {

  object Controller {
    def addMouseCtl(c: MouseCtl) = ()
  }

  trait Glyph {
    def getRect: Geom.Rectangle
    def setLoc(p: Geom.Point): Unit
    def draw() { Console.println("draw " + this) }
  }

  class Label(scr: Screen, p: Geom.Point, name: String) extends Glyph {
    private var origin = p
    def getRect = Geom.Rectangle(origin, origin).inset(10);
    def setLoc(p: Geom.Point) = { origin = p }
  }

  trait Ctl {
    def getGlyph: Glyph
    def enable(b: Boolean): this.type
  }

  trait MouseCtl extends Ctl {
    def mouseDown(p: Geom.Point): Unit
  }

  abstract class Button(scr: Screen, p: Geom.Point, name: String) 
  extends Glyph with MouseCtl {
    var enabled: Boolean = false
    val label = new Label(scr, p, name)

    /* Glyph methods */
    override def draw() {
      if (enabled) scr.drawRect(getRect, Color.black)
      else scr.fillRect(getRect, Color.grey);
      label.draw();
    }
    def setLoc(p: Geom.Point) = label.setLoc(p);
    def getRect = label.getRect.inset(-2);

    /* Ctl methods */
    def enable(b: Boolean): this.type = { enabled = b; draw(); this }
    def getGlyph = label
    final def mouseDown(p: Geom.Point) {
      if (enabled) doit() else Console.println("button is disabled");
    }
    /* deferred method to be specified by client */
    def doit(): Unit
  }
}

object GUIClient {

  class App {
    def quit() { Console.println("application exited") }
  }

  class QuitButton (scr: Screen, p: Geom.Point, name: String, a: App) 
  extends GUI.Button(scr, p, name) {
    def doit() { a.quit() }
  }

  def main(args: Array[String]) {
    val b = new QuitButton(
      DummyScreen, Geom.Point(1, 1), "quit", new App);
    b.draw();
    b.enable(true).mouseDown(Geom.Point(1, 2));
  }
}

