/*  CSci 658, Software Language Engineering
    Philosphical Frog Trait Example from Odersky et al, Chapter 12
    H. Conrad Cunningham

1234567890123456789012345678901234567890123456789012345678901234567890

2010-03-09: (V1) Scala reimplementation
2010-03-12: (V1a) Added more comments. Changed hello/walk messages.
2018-02-13: (V1a) Reformat. Check compilation.

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.

*/


/*  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.")
  }
}
