/*
--- CSU213 Spring 2006 Lecture Notes ---------
Copyright 2006  Viera K. Proulx

Lecture 14: Looking Down.

Goals:

 - Designing subclasses, restricting the visibility.

Introduction:

When designing super classes and unions we were looking for commonalities
between several variants and designed a super class to abstract over
them. We now look at situations when it becomes desirable to spawn off
a subclass that has most of the behavior of its super class, but is 
distinguished in some way that makes it behave distinctly differently 
from its super class.
-------------------------------------------------------------------------

Our problem concerns the design of a simplified 'Tetris' game. At any
moment there is a block falling from the top. The player can move the
block left or right using the arrow keys. When the block hits the bottom, 
or one of already 'resting' blocks, it stops there and becomes a 'resting'
block for the remainder of the game. At that time a new block starts
falling down. The game ends if one of the block ends up resting all the
way on the top. If you wish to keep the score, count the number of 
resting blocks at that time.

The key classes of data we need are the following:

class BlockWorld   -- to control the entire game
class Canvas       -- to display the progress of the game graphically
class Block        -- to represent the blocks in the game

classes IBlock, MTBlock, ConsBlock  
                   -- to represent the list of resting blocks

We will focus on the classes that represent blocks. We know that each
block needs to record its location on the Canvas - let us measure how
far right and down the block is from the top left corner. Of course,
we also need to be able to draw the block on the given Canvas.

The first design suggests that we define a class Block:

   +------------------------+
   | Block                  |
   +------------------------+
   | int down               |
   | int right              |
   +------------------------+
   | boolean draw(Canvas c) |
   +------------------------+
 
However, we also need to model the movement of the falling block,
while the resting blocks remain in one place for the rest of the 
game. It suggest that we design a separate subclass for the falling
blocks. There we include methods that allow us to erase the block
(so we can repaint it at the new location), drop the block a fixed 
distance down (on each tick of the clock) and steer the block left
or right in response to the key events. Finally, we need to be able
to tell when the dropping block came to its resting place - and 
produce a new resting block when that happens. The subclass we design 
is the following:

  +---------------------------------------+
  | DropBlock                             |
  +---------------------------------------+
  | int deltaY                            |
  +---------------------------------------+
  | boolean erase(Canvas c)               |
  | DropBlock drop()                      |
  | boolean landed(IBlocks r)             |
  | DropBlock steer(String ke, IBlocks r) |
  +---------------------------------------+

The code fore these two class definitions is as follows:

// to represent a block in a Tetris-like game
class Block{ 
  int down ;
  int right;
  int size = 10;
  Color color = new Black();

  Block(int down, int right){
    this.down = down;
    this.right = right;
  }

  // draw this block on the given Canvas
  boolean draw(Canvas c){ 
    return c.drawRect(new Posn(this.right, this.down), 
           this.size, 
           this.color);
}

// to represent a dropping block in a Tetris-like game
class DropBlock extends Block{
  int deltaY = 3;

  DropBlock(int down, int right){
    super(down, right);
  }

  // erase this block on the given Canvas
  boolean erase(Canvas c){ 
    return c.drawRect(new Posn(this.right, this.down), 
           this.size, 
           new White());  // should be the background color
}
  
  // drop this block at each time tick
  DropBlock drop(){ return this; }

  // did this block land on the ground or on the given resting blocks?
  boolean landed(IBlocks r){ return false; }

  // produce a block moved in response to the left-right keys,
  // avoiding the resting blocks
  DropBlock steer(String ke, IBlocks r){ return this; }
}

At this point we can construct a new DropBlock anywhere on the Canvas,
even thought in the game a new block always drops from the top. On the
other hand, both of the methods 'drop' and 'steer' need to produce 
a new DropBlock, moved from its previous location. To prevent the 
misuse of the constructor, we 'overload' the constructor --- i.e.
define two versions of the constructor - one for constructing the
initial dropping block, the second one for constructing the block 
that has moved from the previous position. We distinguish between
the two alternatives by using a different set of arguments. When the
block drops from the top, the user has no control over the location
where it will be --- and so does not supply any arguments to the 
constructor. The second constructor is used by the methods 'drop' and 
'steer' and consumes two arguments representing its location.

This is not enough. Now that we have two variants of the constructor,
we want to prevent the outside classes from ever using the second
variant - we want it to be restricted for our 'private' use --- within
the class definition for the class DropBlock. To do so, we use the
privacy modifier 'private' for the second constructor and leave the
first one as 'public':

// to represent a dropping block in a Tetris-like game
class DropBlock extends Block{
  int deltaY = 3;
  
  public DropBlock(){
    this.down = 0;
    // should be a random number between 0 and the canvas width
    this.right = new Random().nextInt(200);
  }

  private DropBlock(int down, int right){
    super(down, right);
  }
...
}

------------------------------------------------------------------
We have another concern. The list of resting blocks consists of
instances of the class Block. However, there is nothing that prevents
the programmer to include an instance of a DropBlock in the list of
resting blocks --- it is an instance of a legitimate subclass. That 
suggests that we need a common abstract class with two subclasses ---
one that represents the resting blocks and one that represents the
dropping block. 

Before we draw the diagram, and complete the class definitions, 
we address two additional concerns.

Further analysis shows that the two methods 'draw' and 'erase' look
very similar - the only difference is in the color they use to paint.
We can abstract over this property as well and design a new method
'paint' that consumes an additional argument --- the color for the
drawing:

  // draw this block on the given Canvas in the given color
  boolean paint(Canvas cv, Color co){ 
    return cv.drawRect(new Posn(this.right, this.down), this.size, co);
  }

  // draw this block on the given Canvas
  boolean draw(Canvas c){ 
    return this.paint(c, this.COLOR); 
  }

  // erase this block on the given Canvas
  boolean erase(Canvas c){ 
    return this.paint(c, this.BACKG); // using the background color
  }
  
However, the outside world should never use out 'paint' method directly.
It should only ask to draw or erase a block. Again, we use the privacy
modifiers to hide the 'paint' method as private and make the other two
methods public.

Our second concern is that the user of this class should not be 
concerned with the color used to draw the blocks or its size, but the 
subclasses need to be able to determine the block's location. A third 
type of privacy modifier 'protected' allows the subclasses the access
to the 'protected' fields and methods, but prevents other unrelated 
classes from seeing their values.

Our final design that addresses all of the above concerns is below.
If we try to construct an instance of Block, we fail. Entering
> Block b = new Block(20, 30);

in the Interactions window results in the error:

class Block is abstract. Abstract classes may not be instantiated. in: Block

We do not include examples - make them and complete the code for the game.
*/

import draw.*;

// to represent a block in a Tetris-like game
abstract class Block{ 
  protected int down ;
  protected int right;
  private int size = 10;
  private Color COLOR = new Black();
  private Color BACKG = new White();

  Block(int down, int right){
    this.down = down;
    this.right = right;
  }

  // draw this block on the given Canvas in the given color
  private boolean paint(Canvas cv, Color co){ 
    return cv.drawRect(new Posn(this.right, this.down), this.size, this.size, co);
  }

  // draw this block on the given Canvas
  public boolean draw(Canvas c){ 
    return this.paint(c, this.COLOR); 
  }

  // erase this block on the given Canvas
  public boolean erase(Canvas c){ 
    return this.paint(c, this.BACKG); // using the background color
  }
}

// to represent a resting block
class RestingBlock extends Block{
  RestingBlock(int down, int right){
    super(down, right);
  }
}

// to represent a dropping block in a Tetris-like game
class DropBlock extends Block{
  int deltaY = 3;

  DropBlock(int down, int right){
    super(down, right);
  }
  
  // drop this block at each time tick
  DropBlock drop(){ return this; }

  // did this block land on the ground or on the given resting blocks?
  boolean landed(IBlocks r){ return false; }

  // produce a block moved in response to the left-right keys,
  // avoiding the resting blocks
  DropBlock steer(String ke, IBlocks r){ return this; }
}

// a stub of the class definition given, so that the code compiles
class IBlocks{
  IBlocks(){}
}

