
// Abstract base class for Boat
abstract class Boat {
    private String skipperName;
    private NavigationCoord coordinates;
    private double speed;
    private int rank; // Starts at 0
    private double distanceToLeader; // Starts at 0
    private int id; // Unique identifier

    // Constructor
    public Boat(String skipperName, NavigationCoord coordinates, int id) {
        this.skipperName = skipperName;
        this.coordinates = coordinates;
        this.speed = 0;
        this.rank = 0; // Default
        this.distanceToLeader = 0; // Default
        this.id = id;
    }

    // Getters for characteristic data
    public String getSkipperName() {
        return skipperName;
    }

    public NavigationCoord getCoordinates() {
        return coordinates;
    }

    public double getSpeed() {
        return speed;
    }

    public int getRank() {
        return rank;
    }

    public double getDistanceToLeader() {
        return distanceToLeader;
    }

    public int getId() {
        return id;
    }

    // update
    public void update(int rank, double distance, NavigationCoord coords
		       ) {
        this.rank = rank;
	//...
    }

    

    // Abstract draw method
    public abstract void draw(Canvas canvas);
}

// Monohull class
class Monohull extends Boat {
    public Monohull(String skipperName, NavigationCoord coordinates, int id) {
        super(skipperName, coordinates, speed, id);
    }

    @Override
    public void draw(Canvas canvas) {
        // Implementation specific to Monohull
        coordinates.draw(canvas);
    }
}

// Catamaran class
class Catamaran extends Boat {
    private boolean hasSails;

    public Catamaran(String skipperName, NavigationCoord coordinates, int id, boolean hasSails) {
        super(skipperName, coordinates, speed, id);
        this.hasSails = hasSails;
    }


    @Override
    public void draw(Canvas canvas) {
        // Implementation specific to Catamaran
        coordinates.draw(canvas);
    }
}

// Map class
class Map {
    private Set<Boat> boats;
    private NavigationCoord center;

    // Constructor
    public Map(Set<Boat> boats, NavigationCoord center) {
   
    }
 public Map(Set<Boat> boats) {
     //center -> API.DEFAULT_CENTER
 }
    // Accessor for center
    public NavigationCoord getCenter() {
        return center;
    }

    // Change the center of the map
    public void setCenter(NavigationCoord center) {
        this.center = center;
    }

    // Remove a boat by ID
    public void removeBoat(int id) {
        boats.removeIf(boat -> boat.getId() == id);
    }

    // Draw the map and all boats
    public void draw(Canvas canvas) {
        for (Boat boat : boats) {
            boat.draw(canvas);
        }
    }

    // Update boats' data using the API
    public void update(API api) {
        for (Boat boat : boats) {
	    boat.update(api.getRankOfBoat(boat.getId()),
			api.getDistanceToFirstBoat(boat.getId()),
			api.getPositionOfBoat(boat.getId()));
	    // ou avec des setters:
	    // boat.setCoordinates(api.getPositionOfBoat(boat.getId()));
            //boat.setRank(api.getRankOfBoat(boat.getId()));
            //boat.setDistanceToLeader(api.getDistanceToFirstBoat(boat.getId()));
        }
    }
}
//// Q2 ///
// Abstract base class for Field
abstract class Field {
    protected NavigationCoord topRightCorner; // Top-right corner coordinate
    protected int size; // Size of the square grid
    protected Data[][] grid; // Grid to hold the data

    // Constructor
    public Field(NavigationCoord topRightCorner, int size) {
        this.topRightCorner = topRightCorner;
        this.size = size;
        this.grid = new Data[size][size]; // Initialize the grid with empty cells
    }

    // Method to get the center coordinate of a grid cell [i][j]
    public NavigationCoord getCellCenter(int i, int j) {
        if (i < 0 || i >= size || j < 0 || j >= size) {
            throw new IndexOutOfBoundsException("Invalid cell coordinates");
        }
        // Calculate the center coordinate (assuming cells are equally spaced)
        // Replace this calculation with actual logic based on NavigationCoord
        return new NavigationCoord(); // Placeholder: Adjust based on coordinate logic
    }

    // Abstract method to fill the grid with data (to be implemented by subclasses)
    public abstract void fillGrid(RestrictedAPI api);

    // Abstract method to update the grid (to be implemented by subclasses)
    public abstract void update(RestrictedAPI api);
}

// Restricted API providing access only to field-specific methods
class RestrictedAPI {
    private API api;

    public RestrictedAPI(API api) {
        this.api = api;
    }

    public WindData getWindData(NavigationCoord coord) {
        return (WindData) api.getWindData(coord);
    }

    public OceanCurrentData getOceanCurrentData(NavigationCoord coord) {
        return (OceanCurrentData) api.getOceanCurrentData(coord);
    }
}

// WindField class
class WindField extends Field {

    public WindField(NavigationCoord topRightCorner, int size) {
        super(topRightCorner, size);
    }

    @Override
    public void fillGrid(RestrictedAPI api) {
        for (int i = 0; i < size; i++) {
            for (int j = 0; j < size; j++) {
                NavigationCoord cellCoord = getCellCenter(i, j); // Get the center of the cell
                grid[i][j] = api.getWindData(cellCoord); // Fill with WindData
            }
        }
    }

    @Override
    public void update(RestrictedAPI api) {
        // Clear and refill the grid with the latest WindData
        System.out.println("Updating WindField grid...");
        fillGrid(api);
    }
}

// OceanCurrentField class
class OceanCurrentField extends Field {

    public OceanCurrentField(NavigationCoord topRightCorner, int size) {
        super(topRightCorner, size);
    }

    @Override
    public void fillGrid(RestrictedAPI api) {
        for (int i = 0; i < size; i++) {
            for (int j = 0; j < size; j++) {
                NavigationCoord cellCoord = getCellCenter(i, j); // Get the center of the cell
                grid[i][j] = api.getOceanCurrentData(cellCoord); // Fill with OceanCurrentData
            }
        }
    }

    @Override
    public void update(RestrictedAPI api) {
        // Clear and refill the grid with the latest OceanCurrentData
        System.out.println("Updating OceanCurrentField grid...");
        fillGrid(api);
    }
}





// Example usage

public class Main {
    public static void main(String[] args) {
        // Create a RestrictedAPI instance
        API api = new API();
        RestrictedAPI restrictedAPI = new RestrictedAPI(api);

        // Create a WindField and fill it
        NavigationCoord topRight = new NavigationCoord();
        WindField windField = new WindField(topRight, 10); // 10x10 grid
        windField.fillGrid(restrictedAPI); // Initial fill
        windField.update(restrictedAPI); // Update with new data

        // Create an OceanCurrentField and fill it
        OceanCurrentField oceanField = new OceanCurrentField(topRight, 10); // 10x10 grid
        oceanField.fillGrid(restrictedAPI); // Initial fill
        oceanField.update(restrictedAPI); // Update with new data
    }
}


/// Q3
// To ensure that field types are extensible (as asked in the question)

// Enum to define field types
enum FieldType {
    WIND, OCEAN_CURRENT;
}


// Updated abstract boat class

abstract class Boat {
    private String skipperName;
    private NavigationCoord coordinates;
    private double speed;
    private int rank; // Starts at 0
    private double distanceToLeader; // Starts at 0
    private int id; // Unique identifier

    // Constructor
    public Boat(String skipperName, NavigationCoord coordinates, double speed, int id) {
        this.skipperName = skipperName;
        this.coordinates = coordinates;
        this.speed = speed;
        this.rank = 0; // Default
        this.distanceToLeader = 0; // Default
        this.id = id;
    }

    // Abstract method to calculate optimal trajectory
    // Takes a variable set of fields (can be empty)
    public abstract Trajectory calculateOptimalTrajectory(Set<Field> fields);

    // Getters and Setters (same as before)
}


// Example implementation in Monohull

class Monohull extends Boat {
    public Monohull(String skipperName, NavigationCoord coordinates, double speed, int id) {
        super(skipperName, coordinates, speed, id);
    }

    @Override
    public Trajectory calculateOptimalTrajectory(Set<Field> fields) {
        // Example logic for calculating trajectory
        // Combine data from the provided fields (wind, ocean current, etc.)
        System.out.println("Calculating optimal trajectory for Monohull...");

        // Apply Monohull-specific algorithm using field data
        for (Field field : fields) {
            if (field instanceof WindField) {
                // Process WindField data
                System.out.println("Using wind data for Monohull trajectory...");
            } else if (field instanceof OceanCurrentField) {
                // Process OceanCurrentField data
                System.out.println("Using ocean current data for Monohull trajectory...");
            }
        }

        // Return the computed trajectory
        return new Trajectory();
    }
}

// Example implementation in Catamaran

class Catamaran extends Boat {
    private boolean hasSails;

    public Catamaran(String skipperName, NavigationCoord coordinates, double speed, int id, boolean hasSails) {
        super(skipperName, coordinates, speed, id);
        this.hasSails = hasSails;
    }

    @Override
    public Trajectory calculateOptimalTrajectory(Set<Field> fields) {
        // Example logic for calculating trajectory
        System.out.println("Calculating optimal trajectory for Catamaran...");

        for (Field field : fields) {
            if (field instanceof WindField && hasSails) {
                // Process WindField data (sails affect wind trajectory)
                System.out.println("Using wind data for Catamaran with sails...");
            } else if (field instanceof OceanCurrentField) {
                // Process OceanCurrentField data
                System.out.println("Using ocean current data for Catamaran...");
            }
        }

        // Return the computed trajectory
        return new Trajectory();
    }
}

// example usage

public class Main {
    public static void main(String[] args) {
        // Create a RestrictedAPI instance
        API api = new API();
        RestrictedAPI restrictedAPI = new RestrictedAPI(api);

        // Create fields
        NavigationCoord topRight = new NavigationCoord();
        WindField windField = new WindField(topRight, 10); // 10x10 grid
        windField.fillGrid(restrictedAPI);

        OceanCurrentField oceanField = new OceanCurrentField(topRight, 10); // 10x10 grid
        oceanField.fillGrid(restrictedAPI);

        // Add fields to a set
        Set<Field> fields = new HashSet<>();
        fields.add(windField);
        fields.add(oceanField);

        // Create boats
        Boat monohull = new Monohull("Skipper A", new NavigationCoord(), 10.5, 1);
        Boat catamaran = new Catamaran("Skipper B", new NavigationCoord(), 12.0, 2, true);

        // Calculate trajectories
        Trajectory monoTrajectory = monohull.calculateOptimalTrajectory(fields);
        Trajectory catTrajectory = catamaran.calculateOptimalTrajectory(fields);

        // Draw the trajectories
        Canvas canvas = new Canvas();
        monoTrajectory.draw(canvas);
        catTrajectory.draw(canvas);
    }
}
