/**
 * Lab 3: Point and MovablePoint - Inheritance with Behavior
 * PT821 - Object-Oriented Programming
 * State University of Zanzibar (SUZA)
 *
 * LEARNING OBJECTIVES:
 * - Add behavior to existing classes through inheritance
 * - Access private parent attributes through getters/setters
 * - Understand why subclass cannot directly access private parent variables
 * - Apply method overriding for extended functionality
 *
 * INSTRUCTIONS:
 * Complete the following exercises step by step.
 * Follow the TODO comments and implement the required functionality.
 */

// ============================================================
// PART A: The Point Class (Superclass)
// ============================================================

/*
 * TODO 1: Create a class called "Point" with:
 *
 * Private instance variables:
 *   - x (double, default 0.0)
 *   - y (double, default 0.0)
 *
 * Constructors:
 *   - Point() - default (0.0, 0.0)
 *   - Point(double x, double y)
 *
 * Public methods:
 *   - getX(), setX(double x)
 *   - getY(), setY(double y)
 *   - setXY(double x, double y) - sets both x and y
 *   - getXY() - returns double[] containing {x, y}
 *   - toString() - returns "(x, y)" e.g., "(3.0, 4.0)"
 */

// Write your Point class here:


// ============================================================
// PART B: The MovablePoint Class (Subclass of Point)
// ============================================================

/*
 * TODO 2: Create a class called "MovablePoint" that extends Point with:
 *
 * Private instance variables:
 *   - xSpeed (double, default 0.0)
 *   - ySpeed (double, default 0.0)
 *
 * Constructors:
 *   - MovablePoint() - default values
 *   - MovablePoint(double xSpeed, double ySpeed)
 *   - MovablePoint(double x, double y, double xSpeed, double ySpeed)
 *     Must call super(x, y)
 *
 * Public methods:
 *   - getXSpeed(), setXSpeed(double xSpeed)
 *   - getYSpeed(), setYSpeed(double ySpeed)
 *   - setSpeed(double xSpeed, double ySpeed)
 *   - getSpeed() - returns double[] containing {xSpeed, ySpeed}
 *
 *   - move() - moves the point by adding speed to position:
 *     IMPORTANT: You CANNOT write "x += xSpeed" because x is private in Point
 *     You MUST use: setX(getX() + xSpeed) and setY(getY() + ySpeed)
 *     Returns this (the MovablePoint itself) for method chaining
 *
 *   - @Override toString() - returns "(x, y) speed=(xSpeed, ySpeed)"
 *     e.g., "(3.0, 4.0) speed=(1.0, 2.0)"
 */

// Write your MovablePoint class here:


// ============================================================
// PART C: Test Driver
// ============================================================

public class Lab3_PointMovablePoint {
    public static void main(String[] args) {
        System.out.println("===============================================");
        System.out.println("  Lab 3: Point and MovablePoint");
        System.out.println("===============================================\n");

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

        // TODO 3: Create and test Point objects
        // Point p1 = new Point();
        // System.out.println("Default point: " + p1);
        //
        // Point p2 = new Point(3.0, 4.0);
        // System.out.println("Point at (3, 4): " + p2);
        //
        // p2.setX(5.0);
        // p2.setY(6.0);
        // System.out.println("After setX(5), setY(6): " + p2);
        //
        // double[] coords = p2.getXY();
        // System.out.println("getXY() = [" + coords[0] + ", " + coords[1] + "]");

        // ----- Section 2: MovablePoint Objects -----
        System.out.println("\n--- Section 2: MovablePoint Objects ---");

        // TODO 4: Create a MovablePoint and test basic properties
        // MovablePoint mp1 = new MovablePoint(0.0, 0.0, 2.0, 3.0);
        // System.out.println("Initial position: " + mp1);
        //
        // // Inherited methods from Point
        // System.out.println("X coordinate: " + mp1.getX());
        // System.out.println("Y coordinate: " + mp1.getY());

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

        // TODO 5: Demonstrate the move() method
        // System.out.println("Before move: " + mp1);
        // mp1.move();
        // System.out.println("After 1st move: " + mp1);
        // mp1.move();
        // System.out.println("After 2nd move: " + mp1);
        // mp1.move();
        // System.out.println("After 3rd move: " + mp1);

        // TODO 6: Change speed and continue moving
        // mp1.setSpeed(1.0, -1.0);
        // System.out.println("\nSpeed changed to (1.0, -1.0)");
        // mp1.move();
        // System.out.println("After move: " + mp1);
        // mp1.move();
        // System.out.println("After move: " + mp1);

        // ----- Section 4: Polymorphism with Point -----
        System.out.println("\n--- Section 4: Polymorphism ---");

        // TODO 7: A MovablePoint IS-A Point
        // Point p3 = new MovablePoint(1.0, 1.0, 0.5, 0.5);
        // System.out.println("p3 (Point ref): " + p3);
        // System.out.println("p3 class: " + p3.getClass().getSimpleName());
        //
        // // Can we call move() on p3? Why or why not?
        // // p3.move();  // Uncomment to test - does it compile?
        // // ANSWER: _______________________________________________
        //
        // // Downcast to call move()
        // if (p3 instanceof MovablePoint) {
        //     MovablePoint mp2 = (MovablePoint) p3;
        //     mp2.move();
        //     System.out.println("After downcast and move: " + mp2);
        //     // Notice: p3 also reflects the change (same object!)
        //     System.out.println("p3 also changed: " + p3);
        // }

        // ----- Section 5: Simulation -----
        System.out.println("\n--- Section 5: Simple Movement Simulation ---");

        // TODO 8: Create multiple moving points and simulate movement
        // MovablePoint[] points = {
        //     new MovablePoint(0.0, 0.0, 1.0, 1.0),   // moves diagonally
        //     new MovablePoint(10.0, 0.0, -1.0, 0.5),  // moves left and slightly up
        //     new MovablePoint(5.0, 5.0, 0.0, -2.0)    // moves straight down
        // };
        //
        // System.out.println("Starting positions:");
        // for (int i = 0; i < points.length; i++) {
        //     System.out.println("  Point " + (i + 1) + ": " + points[i]);
        // }
        //
        // // Simulate 5 steps of movement
        // for (int step = 1; step <= 5; step++) {
        //     System.out.println("\nStep " + step + ":");
        //     for (int i = 0; i < points.length; i++) {
        //         points[i].move();
        //         System.out.println("  Point " + (i + 1) + ": " + points[i]);
        //     }
        // }

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

/*
 * EXPECTED OUTPUT (after completing all TODOs):
 *
 * ===============================================
 *   Lab 3: Point and MovablePoint
 * ===============================================
 *
 * --- Section 1: Point Objects ---
 * Default point: (0.0, 0.0)
 * Point at (3, 4): (3.0, 4.0)
 * After setX(5), setY(6): (5.0, 6.0)
 * getXY() = [5.0, 6.0]
 *
 * --- Section 2: MovablePoint Objects ---
 * Initial position: (0.0, 0.0) speed=(2.0, 3.0)
 * X coordinate: 0.0
 * Y coordinate: 0.0
 *
 * --- Section 3: Movement ---
 * Before move: (0.0, 0.0) speed=(2.0, 3.0)
 * After 1st move: (2.0, 3.0) speed=(2.0, 3.0)
 * After 2nd move: (4.0, 6.0) speed=(2.0, 3.0)
 * After 3rd move: (6.0, 9.0) speed=(2.0, 3.0)
 *
 * Speed changed to (1.0, -1.0)
 * After move: (7.0, 8.0) speed=(1.0, -1.0)
 * After move: (8.0, 7.0) speed=(1.0, -1.0)
 *
 * --- Section 4: Polymorphism ---
 * p3 (Point ref): (1.0, 1.0) speed=(0.5, 0.5)
 * p3 class: MovablePoint
 * After downcast and move: (1.5, 1.5) speed=(0.5, 0.5)
 * p3 also changed: (1.5, 1.5) speed=(0.5, 0.5)
 *
 * --- Section 5: Simple Movement Simulation ---
 * Starting positions:
 *   Point 1: (0.0, 0.0) speed=(1.0, 1.0)
 *   Point 2: (10.0, 0.0) speed=(-1.0, 0.5)
 *   Point 3: (5.0, 5.0) speed=(0.0, -2.0)
 *
 * Step 1:
 *   Point 1: (1.0, 1.0) speed=(1.0, 1.0)
 *   Point 2: (9.0, 0.5) speed=(-1.0, 0.5)
 *   Point 3: (5.0, 3.0) speed=(0.0, -2.0)
 * ... (continues for 5 steps)
 *
 * QUESTIONS TO ANSWER:
 * 1. Why can't MovablePoint directly access x and y (e.g., x += xSpeed)?
 * 2. What is the advantage of using getters/setters to access parent fields?
 * 3. In Section 4, why does changing mp2 also change p3?
 * 4. Could you make move() work through a Point reference without downcasting?
 *    What design change would be needed?
 *
 * SUBMISSION:
 * - Complete all TODO sections
 * - Answer all questions above in comments
 * - Submit the completed .java file
 */
