/**
 * Lab 4: Shape, Circle, Rectangle, and Square - Multi-level Inheritance
 * PT821 - Object-Oriented Programming
 * State University of Zanzibar (SUZA)
 *
 * LEARNING OBJECTIVES:
 * - Build a multi-level inheritance hierarchy (Shape -> Circle/Rectangle -> Square)
 * - Understand the "is-a" relationship: A Square IS-A Rectangle
 * - Override setters to maintain class invariants
 * - Use polymorphism with a common superclass
 *
 * INSTRUCTIONS:
 * Complete the following exercises step by step.
 * Follow the TODO comments and implement the required functionality.
 */

// ============================================================
// PART A: The Shape Class (Top-level Superclass)
// ============================================================

/*
 * TODO 1: Create a class called "Shape" with:
 *
 * Private instance variables:
 *   - color (String, default "red")
 *   - filled (boolean, default true)
 *
 * Constructors:
 *   - Shape() - default values
 *   - Shape(String color, boolean filled)
 *
 * Public methods:
 *   - getColor(), setColor(String color)
 *   - isFilled(), setFilled(boolean filled)
 *   - toString() - returns "Shape[color=?, filled=?]"
 */

// Write your Shape class here:


// ============================================================
// PART B: The Circle Class (Subclass of Shape)
// ============================================================

/*
 * TODO 2: Create a class called "Circle" that extends Shape with:
 *
 * Private instance variable:
 *   - radius (double, default 1.0)
 *
 * Constructors:
 *   - Circle() - default values
 *   - Circle(double radius)
 *   - Circle(double radius, String color, boolean filled)
 *     Must call super(color, filled)
 *
 * Public methods:
 *   - getRadius(), setRadius(double radius)
 *   - getArea() - returns Math.PI * radius * radius
 *   - getPerimeter() - returns 2 * Math.PI * radius
 *   - @Override toString() - returns "Circle[Shape[color=?, filled=?], radius=?]"
 */

// Write your Circle class here:


// ============================================================
// PART C: The Rectangle Class (Subclass of Shape)
// ============================================================

/*
 * TODO 3: Create a class called "Rectangle" that extends Shape with:
 *
 * Private instance variables:
 *   - width (double, default 1.0)
 *   - length (double, default 1.0)
 *
 * Constructors:
 *   - Rectangle() - default values
 *   - Rectangle(double width, double length)
 *   - Rectangle(double width, double length, String color, boolean filled)
 *     Must call super(color, filled)
 *
 * Public methods:
 *   - getWidth(), setWidth(double width)
 *   - getLength(), setLength(double length)
 *   - getArea() - returns width * length
 *   - getPerimeter() - returns 2 * (width + length)
 *   - @Override toString() - returns "Rectangle[Shape[color=?, filled=?], width=?, length=?]"
 */

// Write your Rectangle class here:


// ============================================================
// PART D: The Square Class (Subclass of Rectangle)
// ============================================================

/*
 * TODO 4: Create a class called "Square" that extends Rectangle with:
 *
 * KEY CONCEPT: A Square is a special Rectangle where width == length
 *
 * Constructors:
 *   - Square() - default values (uses super() which gives 1.0 x 1.0)
 *   - Square(double side) - calls super(side, side)
 *   - Square(double side, String color, boolean filled) - calls super(side, side, color, filled)
 *
 * Public methods:
 *   - getSide() - returns getWidth() (since width == length)
 *   - setSide(double side) - calls setWidth(side) AND setLength(side)
 *
 *   IMPORTANT: Override both setWidth and setLength to maintain the square invariant!
 *   - @Override setWidth(double side) - must also set length to same value
 *     Use: super.setWidth(side); super.setLength(side);
 *   - @Override setLength(double side) - must also set width to same value
 *     Use: super.setWidth(side); super.setLength(side);
 *
 *   - @Override toString() - returns "Square[Rectangle[Shape[...], width=?, length=?]]"
 */

// Write your Square class here:


// ============================================================
// PART E: Test Driver
// ============================================================

public class Lab4_ShapeHierarchy {
    public static void main(String[] args) {
        System.out.println("=============================================");
        System.out.println("  Lab 4: Shape Hierarchy");
        System.out.println("=============================================\n");

        // ----- Section 1: Creating Shape Objects -----
        System.out.println("--- Section 1: Creating Objects ---");

        // TODO 5: Create and test objects
        // Shape s1 = new Shape("yellow", false);
        // System.out.println(s1);
        //
        // Circle c1 = new Circle(5.0, "blue", true);
        // System.out.println(c1);
        // System.out.println("  Area: " + c1.getArea());
        // System.out.println("  Perimeter: " + c1.getPerimeter());
        //
        // Rectangle r1 = new Rectangle(4.0, 6.0, "green", true);
        // System.out.println(r1);
        // System.out.println("  Area: " + r1.getArea());
        // System.out.println("  Perimeter: " + r1.getPerimeter());
        //
        // Square sq1 = new Square(5.0, "orange", false);
        // System.out.println(sq1);
        // System.out.println("  Area: " + sq1.getArea());
        // System.out.println("  Perimeter: " + sq1.getPerimeter());

        // ----- Section 2: Square Invariant -----
        System.out.println("\n--- Section 2: Square Invariant ---");

        // TODO 6: Test that Square maintains equal sides
        // Square sq2 = new Square(3.0);
        // System.out.println("Initial: " + sq2);
        // System.out.println("Width: " + sq2.getWidth() + ", Length: " + sq2.getLength());
        //
        // sq2.setWidth(7.0);
        // System.out.println("\nAfter setWidth(7.0):");
        // System.out.println("Width: " + sq2.getWidth() + ", Length: " + sq2.getLength());
        // System.out.println("Both should be 7.0!");
        //
        // sq2.setLength(9.0);
        // System.out.println("\nAfter setLength(9.0):");
        // System.out.println("Width: " + sq2.getWidth() + ", Length: " + sq2.getLength());
        // System.out.println("Both should be 9.0!");

        // ----- Section 3: Polymorphism -----
        System.out.println("\n--- Section 3: Polymorphism ---");

        // TODO 7: Use Shape references for all objects
        // Shape[] shapes = {
        //     new Circle(3.0, "red", true),
        //     new Rectangle(4.0, 5.0, "blue", false),
        //     new Square(6.0, "green", true),
        //     new Circle(7.0, "purple", true),
        //     new Rectangle(2.0, 8.0, "yellow", false)
        // };
        //
        // double totalArea = 0;
        // for (Shape shape : shapes) {
        //     System.out.println(shape);
        //     if (shape instanceof Circle) {
        //         Circle c = (Circle) shape;
        //         System.out.println("  -> Circle area: " + c.getArea());
        //         totalArea += c.getArea();
        //     } else if (shape instanceof Square) {
        //         // NOTE: Check Square BEFORE Rectangle because Square IS-A Rectangle
        //         Square sq = (Square) shape;
        //         System.out.println("  -> Square area: " + sq.getArea() + " (side=" + sq.getSide() + ")");
        //         totalArea += sq.getArea();
        //     } else if (shape instanceof Rectangle) {
        //         Rectangle r = (Rectangle) shape;
        //         System.out.println("  -> Rectangle area: " + r.getArea());
        //         totalArea += r.getArea();
        //     }
        // }
        // System.out.println("\nTotal area of all shapes: " + totalArea);

        // ----- Section 4: Inheritance Chain -----
        System.out.println("\n--- Section 4: Inheritance Chain ---");

        // TODO 8: Demonstrate the full inheritance chain
        // Square sq3 = new Square(4.0, "cyan", true);
        // System.out.println("sq3 instanceof Square:    " + (sq3 instanceof Square));
        // System.out.println("sq3 instanceof Rectangle: " + (sq3 instanceof Rectangle));
        // System.out.println("sq3 instanceof Shape:     " + (sq3 instanceof Shape));
        // System.out.println("sq3 instanceof Object:    " + (sq3 instanceof Object));
        //
        // // All these assignments are valid (upcasting)
        // Rectangle r2 = sq3;      // Square IS-A Rectangle
        // Shape s2 = sq3;           // Square IS-A Shape
        // Object o = sq3;           // Everything IS-A Object
        // System.out.println("\nAll upcasts work! Square -> Rectangle -> Shape -> Object");

        System.out.println("\n=============================================");
        System.out.println("  End of Lab 4");
        System.out.println("=============================================");
    }
}

/*
 * EXPECTED OUTPUT (after completing all TODOs):
 *
 * =============================================
 *   Lab 4: Shape Hierarchy
 * =============================================
 *
 * --- Section 1: Creating Objects ---
 * Shape[color=yellow, filled=false]
 * Circle[Shape[color=blue, filled=true], radius=5.0]
 *   Area: 78.53981633974483
 *   Perimeter: 31.41592653589793
 * Rectangle[Shape[color=green, filled=true], width=4.0, length=6.0]
 *   Area: 24.0
 *   Perimeter: 20.0
 * Square[Rectangle[Shape[color=orange, filled=false], width=5.0, length=5.0]]
 *   Area: 25.0
 *   Perimeter: 20.0
 *
 * --- Section 2: Square Invariant ---
 * Initial: Square[Rectangle[Shape[color=red, filled=true], width=3.0, length=3.0]]
 * Width: 3.0, Length: 3.0
 *
 * After setWidth(7.0):
 * Width: 7.0, Length: 7.0
 * Both should be 7.0!
 *
 * After setLength(9.0):
 * Width: 9.0, Length: 9.0
 * Both should be 9.0!
 *
 * --- Section 3: Polymorphism ---
 * ... (shapes with their areas)
 *
 * --- Section 4: Inheritance Chain ---
 * sq3 instanceof Square:    true
 * sq3 instanceof Rectangle: true
 * sq3 instanceof Shape:     true
 * sq3 instanceof Object:    true
 *
 * All upcasts work! Square -> Rectangle -> Shape -> Object
 *
 * QUESTIONS TO ANSWER:
 * 1. Why is it important to override setWidth() and setLength() in Square?
 * 2. What would go wrong if Square didn't override these setters?
 * 3. In Section 3, why must we check "instanceof Square" before "instanceof Rectangle"?
 * 4. Is it a good design for Square to extend Rectangle? What are the pros and cons?
 *    (Hint: Research the "Liskov Substitution Principle")
 *
 * SUBMISSION:
 * - Complete all TODO sections (create Shape, Circle, Rectangle, Square + uncomment test code)
 * - Answer all questions above in comments
 * - Submit the completed .java file
 */
