import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

/**
 * A program to demonstrate the {@link Measurable} interface. This version of
 * the program is different from the earlier version in that it uses a version
 * of Measurable that has static methods and constants defined.
 *
 * @author Mark Young (A00000000)
 */
public class MeasuringStuff {

    public static void main(String[] args) {
        // create the shapes
        Circle c1 = new Circle(10.0);
        Circle c2 = new Circle(24.7);
        Rectangle r1 = new Rectangle(10.0, 20.0);
        Rectangle r2 = new Rectangle(0.4, 1000.0);
        Rectangle r3 = new Rectangle(10.4, 10.4);

        // print out their basic information
        printCircleInfo("c1", c1);
        printCircleInfo("c2", c2);
        printRectangleInfo("r1", r1);
        printRectangleInfo("r2", r2);
        printRectangleInfo("r3", r3);

        // print out their measurable information
        printMeasurableInfo("c1", c1);
        printMeasurableInfo("c2", c2);
        printMeasurableInfo("r1", r1);
        printMeasurableInfo("r2", r2);
        printMeasurableInfo("r3", r3);

        // print out their roundnesses
        // Note how in this modified version of the program, we need to say
        // that the roundness method is found in the Measurable interface
        System.out.println("The roundness of c1 is " 
                + Measurable.roundness(c1));
        System.out.println("The roundness of c2 is " 
                + Measurable.roundness(c2));
        System.out.println("The roundness of r1 is " 
                + Measurable.roundness(r1));
        System.out.println("The roundness of r2 is " 
                + Measurable.roundness(r2));
        System.out.println("The roundness of r3 is " 
                + Measurable.roundness(r3));

        // Now add some Lists and sorting
        List<Measurable> items = Arrays.asList(c1, c2, r1, r2, r3);
        
        System.out.println("Here is a List: ");
        printList(items);
        items.sort(Measurable.BY_AREA);
        System.out.println("Here sorted by area: ");
        printList(items, m -> m.getArea());
        items.sort(Measurable.BY_PERIMETER);
        System.out.println("Here sorted by perimeter: ");
        printList(items, m -> m.getPerimeter());
        items.sort((a, b) -> 
                Double.compare(Measurable.roundness(a), 
                        Measurable.roundness(b)));
        System.out.println("Here sorted by roundness: ");
        printList(items, m -> Measurable.roundness(m));
    }

    // this method accepts Circles, but not Rectangles
    public static void printCircleInfo(String name, Circle c) {
        System.out.println(name + " is a circle of radius " + c.getRadius());
    }

    // this method accepts Rectangles, but not Circles
    public static void printRectangleInfo(String name, Rectangle r) {
        System.out.println(name + " is a " + r.getLength() + "x" + r.getWidth()
                           + " rectangle");
    }

    // this method accepts any object that implements the Measurable interface
    // which includes both Circles and Rectangles
    public static void printMeasurableInfo(String name, Measurable m) {
        System.out.println("Measures of " + name + ":");
        System.out.println("\tArea:       " + m.getArea());
        System.out.println("\tPerimeter:  " + m.getPerimeter());
    }

    // This method prints a List of Measurable objects
    private static void printList(List<Measurable> items) {
        for (Measurable m : items) {
            System.out.printf("%30s%n", m);
        }
        System.out.println();
    }

    // This method prints a List of Measurable objects along with their sizes
    // accounding to the given function
    private static void printList(
            List<Measurable> items, 
            Function<Measurable, Double> map) {
        for (Measurable m : items) {
            System.out.printf("%30s%10.4f%n", m, map.apply(m));
        }
        System.out.println();
    }
}

