/**
 * This program illustrates 
 * abstract classes, subclasses, and overriding 
 * Based on  \citet[Example 30, page 25]{sestoft:java2016}
 * 
 * @author Phil Molyneux
 * @version 1.0 (16 January 2020)
 */
class VesselEG {
  public static void main(String[] args) {
    Vessel v1 = new Barrel(3, 10) ;
    Vessel v2 = new Tank(10, 20, 12) ;
    Vessel v3 = new Cube(4) ;

    Vessel[] vs = { v1, v2, v3 } ;

    v1.fill(90) ; 
    v1.fill(10) ; 
    v2.fill(100) ; 
    v3.fill(80) ;

    double totalCap = 0 ;
    for (int i = 0; i < vs.length; i++) {
      totalCap = totalCap + vs[i].capacity() ;      
    }
    System.out.println("Total capacity is " 
                       + totalCap) ;

    double totalConts = 0 ;
    for (int i = 0; i < vs.length; i++) {
      totalConts = totalConts + vs[i].contents ;      
    }
    System.out.println("Total contents is " 
                       + totalConts) ;

    for (int i = 0; i < vs.length; i++) {
      System.out.println("vessel number " + i 
                         + ": " + vs[i]) ;
    }
  }
}

abstract class Vessel {
  double contents ;
  
  public Vessel() {
    contents = 0 ;
  }
  
  abstract double capacity() ;
    
  public void fill(double amount) {
    contents 
      = Math.min(contents + amount, capacity()) ;
  }
}

class Tank extends Vessel {
  double depth, width, height ;
  
  public Tank(double depth
             , double width, double height) {
    super() ;
    this.depth = depth ;
    this.width = width ;
    this.height = height ;
  }
  
  @Override
  double capacity() {
    return depth * width * height ;
  }
  
  @Override
  public String toString() {
    return ("tank (" + depth + "," 
            + width + "," + height + ")") ;
  }
}

class Cube extends Tank {

  public Cube(double side) {
    super(side, side, side) ;
  }
  
  @Override
  public String toString() {
    return ("cube (" + depth + ")") ;
  }
}

class Barrel extends Vessel {
  double radius, height ;

  Barrel(double radius, double height) { 
    super() ;
    this.radius = radius ; 
    this.height = height ; 
  }

  @Override
  double capacity() { 
    return (height * Math.PI * radius * radius) ; 
  }

  @Override
  public String toString() { 
    return ("barrel (" + radius + ", " + height + ")") ; 
  }
}
