/* The Carcassonne Game 

   Players create a map from a heap of tiles. Each tile displays a number of 
   things, including castles, roads, abbeys, and grass. 

   A software version of a player needs a way to refer to a specific tile in
   the map. Let's use Coordinates for that, aka Posns. 

   Finally, on each tile there are positions on which players can place
   followers. To make things simple, let's assume each tile contains exactly 
   one follower. 

   The first step: translate this information into a data representation 
*/

/*
+--------------------+
| Tile               |
+--------------------+
| Posn p             |
| Follower f         |
| String description |
+--------------------+

+--------------+
| Follower     |
+--------------+
| String color |
| String name  |
+--------------+

+-------+
| Posn  |
+-------+
| int x |
| int y |
+-------+

*/


// a tile in a Carcassonne map 
class Tile {
 Posn p;
 Follower f;
 String description;
                 
 Tile(Posn p, Follower f, String description) {
  this.p = p;
  this.f = f;
  this.description = description;
 }
}
 
 // a Carcassonne follower 
class Follower {
 String color;
 String name;
 
 Follower(String color, String name) {
  this.color = color;
  this.name = name;
 }
}

// Cartesian coordinates
class Posn {
  int x;
  int y;

  Posn(int x, int y) {
    this.x = x;
    this.y = y;
  }
}

/* Next we need to figure out some of tasks that players and game administrators
   have to perform on the tiles of the map. Today we deal with these two: 

   -- given a list of tiles, extract the list of Followers 
   -- given a list of tiles, extract the list of Posns 

   Naturally, this suggests we need to formulate data definitions (aka, uml 
   diagrams) for three more classes: 

   -- list of Tiles 
   -- list of Followers
   -- list of Posns 
*/

/*
         +-------+             
         | LTile |<-----------+
         +-------+            |
         +-------+            |
            / \               |
            ---               |
             |                |
        ---------------       |
        |             |       |
  +------------+    +----+    |
  | TCons      |    | Mt |    |
  +------------+    +----+    |
  | Tile first |    +----+    |
  | LTile rest |-+            |
  +------------+ |            |
                 |            |
                 +------------+

*/

abstract class LTile {
}

class TCons extends LTile {
  Tile first;
  LTile rest;

  TCons(Tile first, LTile rest) {
    this.first = first;
    this.rest = rest;
  }
}

class MtT extends LTile {

  MtT() { }
}

/*
          +------------+             
          | LFollowers |<-----------+
          +------------+            |
          +------------+            |
               / \                  |
               ---                  |
                |                   |
           ------------------       |
           |                |       |
  +-----------------+    +-----+    |
  | FCons           |    | FMt |    |
  +-----------------+    +-----+    |
  | Follower first  |    +-----+    |
  | LFollowers rest |-+             |
  +-----------------+ |             |
                      |             |
                      +-------------+

*/

abstract class LFollowers {
}

class FCons extends LFollowers {
  Follower first;
  LFollowers rest;

  FCons(Follower first, LFollowers rest) {
    this.first = first;
    this.rest = rest;
  }
}

class FMt extends LFollowers {

  FMt() { }
}

/*
          +------------+             
          | LPosns     |<-----------+
          +------------+            |
          +------------+            |
               / \                  |
               ---                  |
                |                   |
           ------------------       |
           |                |       |
  +-----------------+    +-----+    |
  | PCons           |    | PMt |    |
  +-----------------+    +-----+    |
  | Posn first      |    +-----+    |
  | LPosns rest     |-+             |
  +-----------------+ |             |
                      |             |
                      +-------------+

*/

abstract class LPosns {
}

class PCons extends LPosns {
  Posn first;
  LPosns rest;

  PCons(Posn first, LPosns rest) {
    this.first = first;
    this.rest = rest;
  }
}

class PMt extends LPosns {

  PMt() { }
}

/* Well, even a blind woman can see that this is copy-and-paste code and we all 
   know that this is dangerous. So let's do better than this. Fortunately, we 
   have taken CSU 211 and know how to use the design recipe for abstraction: 
   -- circle the differences 
   -- use variables instead 
   -- add the variables to the parameter list of the function 

   In this case, the function consumes a Java type and produces a UML diagram 
   or in reality an entire bunch of classes: 
*/


/*

;; JavaType -> UML
;; create a generic list of _type_
(define (make-list type)

          +------------+             
          | LType      |<-----------+
          +------------+            |
          +------------+            |
               / \                  |
               ---                  |
                |                   |
           ------------------       |
           |                |       |
  +-----------------+    +-----+    |
  | TCons           |    | TMt |    |
  +-----------------+    +-----+    |
  | type first      |    +-----+    |
  | LType rest      |-+             |
  +-----------------+ |             |
                      |             |
                      +-------------+  )


  The key thing to note is that >>>type<<< shows up in the second box (TCons). 

  Okay so this is _not_ Java as we know it and hate it. Instead, it is the new
  and improved Java 1.5. [Note: software companies number products. The claim is
  that each release removes errors and problems. Instead, we all know that it 
  adds new features and thus introduces new bugs. So -- the number N really stands 
  for the N million bugs not found yet.]
*/

/* What are we to do? 

   We use the next best thing -- Object, the most general type in Java. We know
   that everything is an object so we make a list of objects and hope for the 
   best. 

          +------------+             
          | LObject    |<-----------+
          +------------+            |
          +------------+            |
               / \                  |
               ---                  |
                |                   |
           ------------------       |
           |                |       |
  +-----------------+    +-----+    |
  | OCons           |    | OMt |    |
  +-----------------+    +-----+    |
  | Object first    |    +-----+    |
  | LObject rest    |-+             |
  +-----------------+ |             |
                      |             |
                      +-------------+  
*/

abstract class LObjects {
}

class ConsO extends LObjects {
  Object first;
  LObjects rest;

  ConsO(Object first, LObjects rest) {
    this.first = first;
    this.rest = rest;
  }
}

class Mt extends LObjects {

  Mt() { }
}


// Okay, time to make examples:



class Examples {
  LObjects lot = // a list of tiles 
	new ConsO(new Tile(new Posn(1,2),new Follower("green","mf"),"castle"),
	 	  new ConsO(new Tile(new Posn(7,8),new Follower("red","mf"),"castle"),
			    new ConsO(new Tile(new Posn(-1,2),new Follower("red","jc"),"road"),
				      new Mt())));
  LObjects lof = // a list of followers
	new ConsO(new Follower("green","mf"),
	 	  new ConsO(new Follower("red","mf"),
			    new ConsO(new Follower("red","jc"),
				      new Mt())));
  LObjects lop = // a list of posns
	new ConsO(new Posn(1,2),
	 	  new ConsO(new Posn(7,8),
			    new ConsO(new Posn(-1,2),
				      new Mt())));

}
  
/* If lot is given, we know that 

    -- lot.getFollowers() // should produce lof
    -- lot.getPosn() // should produce lop

   So let's add those methods to the list of objects: 

   LObject: 
   // get the list of followers from this list of tiles 
   abstract LObject getFololowers()

   Mt: 
   LObject getFollowers() { 
     return this; // which is the empty list 
   }

   ConsO: 
   LObject getFollowers() { 
    return new ConsO(  ((Tile)this.first).f, 
                       this.rest.getFollowers() ); 
   }

   And the same for getPosn. 

   Of course, putting the cast to Tile in the middle of a class concerning a 
   list of generic objects defeats the purpose of the idea of abstraction. 

   We need to come up with something better than that. 

   Ken's proposal: let's write a generic get method: 

   LObject: 
   // get something from each item on this list
   abstract LObject get()

   Mt: 
   LObject get() { 
     return this; // which is the empty list 
   }

   ConsO: 
   LObject get() { 
    return new ConsO(  ... this.first ... 
                       this.rest.getFollowers() ); 
   }

   Hah, problem is we don't know what to do with this.first. Plus, we really 
   want to do different things with this.first at different times. 

   In Scheme, we would have just passed in a function that extracts the right 
   piece from this.first. Unfortunately, Java doesn't have functions. It has 
   ints, doubles, booleans, Strings, which are useless here, and objects. 

   So, let's say we have a class GetSomething and get receives an instance of 
   this class: 


   LObject: 
   // get something from each item on this list
   abstract LObject get(GetSomething x)

   Mt: 
   LObject get(GetSomething x) { 
     return this; // which is the empty list 
   }

   ConsO: 
   LObject get(GetSomething x) { 
    return new ConsO(  ... x ... this.first ... 
                       this.rest.getFollowers(x) ); 
   }
  
   Well we can't call objects but if they contain a method, we can call those
   methods. So assume that the class GetSomething contains the method 

     Object poke(Object o) { ... }

   Now we can complete ConsO in version 2 of 

   ConsO: 
   LObject get(GetSomething x) { 
    return new ConsO(  x.poke(this.first),
                       this.rest.getFollowers(x) ); 
   }

*/


abstract class LObjects_1 {
   // get something from each item on this list
   abstract LObjects_1 get(GetSomething x);
}

class ConsO_1 extends LObjects_1 {
  Object first;
  LObjects_1 rest;

  ConsO_1(Object first, LObjects_1 rest) {
    this.first = first;
    this.rest = rest;
  }

   LObjects_1 get(GetSomething x) { 
    return new ConsO_1(  x.poke(this.first), this.rest.get(x) ); 
   }
}

class Mt_1 extends LObjects_1 {

 Mt_1() { }

 LObjects_1 get(GetSomething x) { 
     return this; // which is the empty list 
   }
}

class GetSomething {
   // extract a follower from an object tile 
  Object poke(Object o) {
    return ((Tile)o).f;
  }
}

class Examples_1 {
  LObjects_1 lot = // a list of tiles 
	new ConsO_1(new Tile(new Posn(1,2),new Follower("green","mf"),"castle"),
	 	  new ConsO_1(new Tile(new Posn(7,8),new Follower("red","mf"),"castle"),
			    new ConsO_1(new Tile(new Posn(-1,2),new Follower("red","jc"),"road"),
				      new Mt_1())));
  LObjects_1 lof = // a list of followers
	new ConsO_1(new Follower("green","mf"),
	 	  new ConsO_1(new Follower("red","mf"),
			    new ConsO_1(new Follower("red","jc"),
				      new Mt_1())));
  LObjects_1 lop = // a list of posns
	new ConsO_1(new Posn(1,2),
	 	  new ConsO_1(new Posn(7,8),
			    new ConsO_1(new Posn(-1,2),
				      new Mt_1())));

  LObjects_1 lof_computed = // use the get method now:
	lot.get(new GetSomething());

}
  
/* Great. Evaluate 

     new Examples_1() 

   and you see that lof and lof_computed are the same list. But how are we going
   to extract the coordinates (posns) form this list now? We need to have a 
   _different_ class GetSomething whose poke method behaves very differently. 

   And indeed, we need a different class GetSomething with a different poke for
   everything we want to compute from a list of objects. 

   That doesn't work. What we do instead we construct a minimalistic description 
   of what get needs to consume -- an interface that describes poke. And then we
   define classes with real poke methods that have that signature. 
*/ 

interface IGetSomething {
  Object poke(Object o); 
}

class GetFollower implements IGetSomething {
  // extract a follower from an object tile 
  Object poke(Object o) {
    return ((Tile)o).f;
  }
}

class GetPosn implements IGetSomething {
   // extract a follower from an object tile 
  Object poke(Object o) {
    return ((Tile)o).p;
  }
}

// -----------------------------------------------------------------------------
// NOT COVERED

/* Of course an interface is really just an abstract class with absolutely 
   no methods, and with no variable fields. 'implements' means that the class
   overrides all of the interface's methods with concrete methods. */
   
// Here is how everything works: 

abstract class LObjects_2 {
   // get something from each item on this list
   abstract LObjects_2 get(IGetSomething x);
}

class ConsO_2 extends LObjects_2 {
  Object first;
  LObjects_2 rest;

  ConsO_2(Object first, LObjects_2 rest) {
    this.first = first;
    this.rest = rest;
  }

   LObjects_2 get(IGetSomething x) { 
    return new ConsO_2(  x.poke(this.first), this.rest.get(x) ); 
   }
}

class Mt_2 extends LObjects_2 {

 Mt_2() { }

 LObjects_2 get(IGetSomething x) { 
     return this; // which is the empty list 
   }
}

class Examples_2 {
  LObjects_2 lot = // a list of tiles 
	new ConsO_2(new Tile(new Posn(1,2),new Follower("green","mf"),"castle"),
	 	  new ConsO_2(new Tile(new Posn(7,8),new Follower("red","mf"),"castle"),
			    new ConsO_2(new Tile(new Posn(-1,2),new Follower("red","jc"),"road"),
				      new Mt_2())));
  LObjects_2 lof = // a list of followers
	new ConsO_2(new Follower("green","mf"),
	 	  new ConsO_2(new Follower("red","mf"),
			    new ConsO_2(new Follower("red","jc"),
				      new Mt_2())));
  LObjects_2 lop = // a list of posns
	new ConsO_2(new Posn(1,2),
	 	  new ConsO_2(new Posn(7,8),
			    new ConsO_2(new Posn(-1,2),
				      new Mt_2())));

  LObjects_2 lof_computed = // use the get method now:
	lot.get(new GetFollower());

  LObjects_2 lop_computed = 
	lot.get(new GetPosn());

}
  
// evaluate new Examples_2() and compare the lists 

