/** Note: All the classes are grouped together here for simplicity.
 *  Java can only have one public class per file (which must be named the same
 *  as the file, but may have many non-public classes that are accessible to all
 *  members of the enclosing package.
 */
package Generics;

/**
 * Interfaces simply describe the methods that all implementing class must
 * support. An interface is a class that describes the parameter and return types
 * of various methods, but does not actually implement them.
 *
 * @author Adam J. Conover
 */
public interface Food {

    /** All food has calories. */
    public float getTotalCalories();

    /** All Food comes from someplace. */
    public String getHabitat();
}

/**
 * The top level class which implements Food and is Comparable to other Fruits.
 * The class is abstract because -- without knowing more about the type of fruit --
 * We can really know any of the specifics necessary to construct an instance
 * of the class.
 *
 *  @author Adam J. Conover
 */
abstract class Fruit implements Food, Comparable<Fruit> {

    float calories; // calories per gram
    float grams;    // weight in grams

    public Fruit(float weightInGrams) {
        this.grams = weightInGrams;
    }

    public float getTotalCalories() {
        return this.calories * this.grams;
    }

    public int compareTo(Fruit obj) {
        // Use Float objects, so we can use the prebuilt comparator.
        // Otherwise we would have to create determin the return value ourselves.
        Float cal1 = this.calories * this.grams;
        Float cal2 = obj.calories * obj.grams;

        // Compare the two floats and return the result.
        return cal1.compareTo(cal2);
    }
}

/**
 * An apple is a type of fruit with it own properties, but there are many varieties
 * of apple.  So, the class is abstract and we rely on derived classes to provide
 * the specifics of the type of apple.
 * 
 * @author Adam J. Conover
 */
abstract class Apple extends Fruit {

    public Apple(float weightInGrams) {
        super(weightInGrams);
        super.calories = .47f;
    }

    public String getHabitat() {
        return "Temperate";
    }
}

/**
 * Assume specific varieties of fruits originated in specific locations. But these
 * don't have to be the same place they actually grow. So we should be able the
 * "getOrigin()" of the specific varieties.
 *  
 * @author Adam J. Conover
 */
interface Origin {

    public String getOrigin();
}

/**
 * A specific variety of Apple...
 * @author Adam J. Conover
 */
class Fuji extends Apple implements Origin {

    public Fuji(float weightInGrams) {
        super(weightInGrams);
    }

    public String getOrigin() {
        return "Japan";
    }
}

/**
 * A specific variety of apple.
 * @author Adam J. Conover
 */
class Gala extends Apple implements Origin {

    public Gala(float weightInGrams) {
        super(weightInGrams);
    }

    public String getOrigin() {
        return "New Zealand";
    }
}

/**
 * A specific variety of apple.
 * @author Adam J. Conover
 */
class HoneyCrisp extends Apple implements Origin {

    public HoneyCrisp(float weightInGrams) {
        super(weightInGrams);
    }

    public String getOrigin() {
        return "Minnesota";
    }
}

/**
 * Though there are specific varieties of Oranges too, for this example we'll
 * treat an Orange as it own concrete class.
 *
 * @author Adam J. Conover
 */
class Orange extends Fruit {

    // The average in normal seeded oranges.
    private static final float CALORIES_PER_GRAM = .37f;

    public Orange(float weightInGrams) {
        super(weightInGrams);
        super.calories = CALORIES_PER_GRAM;
    }

    public String getHabitat() {
        return "Tropical";
    }
}

/**
 * A trademarked variety of seedless orange.
 * @author Adam J. Conover
 */
class Sunkist extends Orange implements Origin {

    // According the Sunkist site:
    private static final float AVG_WEIGHT = 154;
    private static final float CALORIES_PER_GRAM = .519f;

    public Sunkist() {
        super(AVG_WEIGHT);
        super.calories = CALORIES_PER_GRAM;
    }

    public String getOrigin() {
        return "California";
    }
}