/* Philosphical Frog Trait Example from Odersky et al, Chapter 12
   H. Conrad Cunningham
   Version #1:   9 March 2010
   Version #1a: 12 March 2010 Added more comments, changed hello/walk messages

   This is an adaptation of the Philosphical Frog trait example from
   section 12.1 of the book _Programming in Scala_ by Martin Odersky,
   Lex Spoon, and Bill Venners.  This example renames the four Frog
   classes as Frog1, Frog2, Frog3, and Frog4 in the order of
   appearance in the chapter.  It also adds methods to the class
   Animal and trait HasLegs to illustrate the inheritance and
   polymorphism.  A simple script is included in PhilFrog.main to show
   a few method calls.

123456789012345678901234567890123456789012345678901234567890123456789012345678

*/


/* Trait Philosphical can be mixed in to other classes.  Traits do not
   have constructors.  Traits extend AnyRef by default, so methods like 
   toString are defined.
*/

trait Philosophical {
  def philosophize() { 
    println("I consume memory, therefore I am!") 
  }
}


/* Class Frog1 does not have any explicit subclass inheritance, but it
   extends AnyRef by default.  It also mixes in trait Philosphical
*/

class Frog1 extends Philosophical {
  override def toString = "green"
}


/* Class Animal here has a method hello not defined in the Odersky et al 
   book example.
*/

class Animal {
  def hello: String = "Hello, I am a wild animal!"
}


/* Class Frog2 is like Frog1 except that it extends class Animal as well 
   as mixing in trait Philosphical.
*/

class Frog2 extends Animal with Philosophical {
  override def toString = "green"
}


/* Trait HasLegs adds method walk to Odersky et al book code.  
*/

trait HasLegs {
  def walk: String = "I can walk!"
}


/* Class Frog3 is like Frog2 except that it also mixes in trait HasLegs,
   as well as extending class Animal and mixing in trait Philosophical.
*/

class Frog3 extends Animal with Philosophical with HasLegs {
    override def toString = "green"
}


/* Class Frog4 is like Frog2 except that it overrides method
   philosophize() from trait Philosophical as well as toString.
*/

class Frog4 extends Animal with Philosophical {
  override def toString = "green"
  override def philosophize() {
    println("It ain't easy being "+ toString +"!")
  }
}


/* PhilFrog.main is a program entry point that illustrates use of the
   various Frog classes and the traits.  It uses polymorphic variables 
   of the base class Animal and trait Philosophical and HasLegs types.

*/

object PhilFrog {

  def main(args: Array[String]) {

    println("PhilFrog program beginning.")

    println("\nUsing Frog1, which mixes in Philosphical")
    val frog1 = new Frog1
    println("frog1                = " + frog1)
    print  ("frog1.philosophize() = "); frog1.philosophize()

    var phil: Philosophical = frog1
    println("phil: Philosphical   = frog1")
    println("phil                 = " + phil)
    print  ("phil.philosophize()  = "); phil.philosophize()

    println("\nUsing Frog2, which extends Animal and mixes in Philosphical")
    val frog2 = new Frog2
    println("frog2                = " + frog2)
    print  ("frog2.philosophize() = "); frog2.philosophize()
    println("frog2.hello          = " + frog2.hello)

    phil = frog2
    println("phil: Philosphical   = frog2")
    println("phil                 = " + phil)
    print  ("phil.philosophize()  = "); phil.philosophize()
    // phil.hello does not pass the compiler type checker

    var animal: Animal = frog2
    println("animal: Animal       = frog2")
    println("animal.hello         = " + animal.hello)
    // animal.phlosophize() does not pass the compiler type checker

    println("\nUsing Frog3, which extends Animal and mixes in Philosphical" +
            " and HasLegs")
    val frog3 = new Frog3
    println("frog3                = " + frog3)
    print  ("frog3.philosophize() = "); frog3.philosophize()
    println("frog3.hello          = " + frog3.hello)
    println("frog3.walk           = " + frog3.walk)

    phil = frog3
    println("phil: Philosphical   = frog3")
    println("phil                 = " + phil)
    print  ("phil.philosophize()  = "); phil.philosophize()
    // phil.hello does not pass the type checker
    // phil.walk does not pass the type checker

    animal = frog3
    println("animal: Animal = frog3")
    println("animal.hello         = " + animal.hello)
    // animal.phlosophize() does not pass the compiler type checker
    // animal.walk does not pass the compiler type checker

    var legs: HasLegs = frog3
    println("legs: HasLegs = frog3")
    println("legs.walk            = " + legs.walk)
    // legs.phlosophize() does not pass the compiler type checker
    // legs.hello does not pass the compiler type checker

    println("\nUsing Frog4, which extends Animal and mixes in Philsophical" +
            " overriding its philosphize method")
    val frog4 = new Frog4
    println("frog4                = " + frog4)
    print  ("frog4.philosophize() = "); frog4.philosophize()
    println("frog4.hello          = " + frog4.hello)

    phil = frog4
    println("phil: Philosphical   = frog4")
    println("phil                 = " + phil)
    print  ("phil.philosophize()  = "); phil.philosophize()

    animal = frog4
    println("animal: Animal       = frog4")
    println("animal.hello         = " + animal.hello)

    println("\nPhilFrog program ending.")
  }
}
