/*
 * Bookstore list: abstracting over Object 
 *                 book class implements IsSame
 */

import tester.Tester;

/*
                                                                
             +---------+                        
             |  ILoObj |<--------+
             +---------+         |
             +---------+         |
                  |              |
                  |              |
                 /_\             |
                  |              |
        +--------------+         |
        v              v         |  
   +---------+  +--------------+ |    
   | MTLoObj |  | ConsLoObj    | |  
   +---------+  +--------------+ |  
              +-| Object first | |
              | | ILoObj rest  |-+
              | +--------------+
              v
   +---------------+
   | Book          |
   +---------------+
   | String title  |
   | String author |
   | int year      |
   +---------------+  
*/
                          
// to represent a book in a bookstore
class Book implements IsSame{
  String title;
  String author;
  int year;
  
  Book(String title, String author, int year){
    this.title = title;
    this.author = author;
    this.year = year;
  }

/* TEMPLATE:
   FIELDS:
    ... this.title ...              -- String
    ... this.author ...             -- String
    ... this.year ...               -- int

    METHODS FOR FIELDS:
    ... this.name.equals(String) ...   -- boolean 
    ... this.author.equals(String) ... -- boolean   
  
    METHODS FOR THIS CLASS:
    ... this.same(Book) ...       -- boolean
*/

  // is this the same book as the given book?
  public boolean same(Object that){
  	Book tmp = (Book)that;
    return this.title.equals(tmp.title) &&
           this.author.equals(tmp.author) &&
           this.year == tmp.year;
  }

} 

// to represent a method to verify equality of this and that object
interface IsSame{
	boolean same(Object that);
}

// to represent a list of books
interface ILoObj{
  
  // compute the total size of this list
  int size();
  
  // does this list of books contain the given book?
  boolean contains(Object b);
}                                

// to represent an empty list of books
class MtLoObj implements ILoObj{
  MtLoObj(){}
  
  // compute the total size of this list
  public int size(){
    return 0;
  }
  
  // does this list of books contain the given book?
  public boolean contains(Object b){
  	return false;
  }
}                                              

// to represent a nonempty list of books
class ConsLoObj implements ILoObj{
  Object first;
  ILoObj rest;
  
  ConsLoObj(Object first, ILoObj rest){
    this.first = first;
    this.rest = rest;
  }
  

/* TEMPLATE:
   FIELDS:
    ... this.first ...              -- Book
    ... this.rest ...               -- ILoB

    METHODS FOR FIELDS:
    ... this.first.same(Book) ...       -- boolean
  
    ... this.rest.size() ...            -- int
    ... this.rest.contains(Book) ...    -- boolean

*/
  
  // compute the total size of this list
  public int size(){
    return 1 + this.rest.size();
  }
  
  // does this list of books contain the given book?
  public boolean contains(Object b){
  	return ((IsSame)this.first).same(b) ||
  	       this.rest.contains(b);
  }
 
/*
  // produce a list of all books published before the given year
  public ILoB allBefore(int limit){
    if (this.first.year < limit){
      return new ConsLoB(this.first, this.rest.allBefore(limit));
    }
    else{
      return this.rest.allBefore(limit);
    }
  }
*/

}                                                    

// Examples and tests for books and lists of books
class Examples{
  Examples(){}

  Book oms = new Book("Old Man and the Sea", "Hemingway", 1952);
  Book eos = new Book("Elements of Style", "EBW", 1927);
  Book htdp = new Book("HtDP", "MF", 2001);
  Book ll = new Book("Little Lisper", "MF", 1995);
  
  ILoObj mtlob = new MtLoObj();
  ILoObj blist2 = new ConsLoObj(this.oms, 
  		               new ConsLoObj(this.eos, this.mtlob));
  ILoObj blist3 = new ConsLoObj(this.htdp, this.blist2);
  
  // test the method same in the classes that implement ILoB
  boolean testSame(Tester t){
    return
    t.checkExpect(
    		this.oms.same(new Book("Old Man and the Sea", "Hemingway", 1952)),  
    		true) &&
    t.checkExpect(this.eos.same(this.htdp),  false) &&
    t.checkExpect(this.htdp.same(this.ll),  false) ;}

  // test the method size in the classes that implement ILoB
  boolean testSize(Tester t){
    return
    t.checkExpect(this.mtlob.size(),  0) &&
    t.checkExpect(this.blist2.size(),  2) &&
    t.checkExpect(this.blist3.size(),  3);}
    
  // test the method contains in the classes that implement ILoB
  boolean testContains(Tester t){
    return
    t.checkExpect(this.mtlob.contains(this.htdp),  false) &&
    t.checkExpect(this.blist2.contains(this.htdp),  false) &&
    t.checkExpect(this.blist3.contains(this.htdp),  true);}

}