import java.util.ArrayList;

// Part 1: Observer Pattern

// Observer interface
interface Observer {
    void update(float temperature, float humidity, float pressure);
}

// Subject interface
interface Subject {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();
}

// DisplayElement interface
interface DisplayElement {
    void display();
}

// Concrete Subject class
class WeatherData implements Subject {
    private final ArrayList<Observer> observers;
    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherData() {
        observers = new ArrayList<>();
    }

    public void registerObserver(Observer o) {
        observers.add(o);
    }

    public void removeObserver(Observer o) {
        int i = observers.indexOf(o);
        if (i >= 0) {
            observers.remove(i);
        }
    }

    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(temperature, humidity, pressure);
        }
    }

    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;

        notifyObservers();
    }
}

// Concrete Observer class
class CurrentConditionsDisplay implements Observer, DisplayElement {
    private float temperature;
    private float humidity;
    private Subject weatherData;

    public CurrentConditionsDisplay(Subject weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this); // Automatically register with Subject during construction
    }

    public void update(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        display();
    }

    public void display() {
        System.out.println("Current Conditions: " + temperature + "F degrees and " + humidity + "% humidity");
    }
}

// Concrete Observer class for statistics
class StatisticsDisplay implements Observer, DisplayElement {
    private float temperatureSum = 0;
    private float humiditySum = 0;
    private int numReadings;
    private Subject weatherData;

    public StatisticsDisplay(Subject weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }

    public void update(float temperature, float humidity, float pressure) {
        temperatureSum += temperature;
        humiditySum += humidity;
        numReadings++;
        display();
    }

    public void display() {
        System.out.println("Avg Temperature: " + (temperatureSum / numReadings) +
                           "F, Avg Humidity: " + (humiditySum / numReadings) + "%");
    }
}

// Part 2: Decorator Pattern

// Decorator interface
interface WeatherDataDecorator extends Observer, DisplayElement {
}

// Concrete Decorator class for temperature units
class TemperatureUnitsDecorator implements WeatherDataDecorator {
    private final WeatherDataDecorator decoratedObserver;
    private boolean useCelsius = true;

    public TemperatureUnitsDecorator(WeatherDataDecorator decoratedObserver) {
        this.decoratedObserver = decoratedObserver;
    }

    public void update(float temperature, float humidity, float pressure) {
        decoratedObserver.update(convertTemperature(temperature), humidity, pressure);
    }

    public void display() {
        decoratedObserver.display();
    }

    private float convertTemperature(float temperature) {
        // Convert temperature based on user preference (Celsius or Fahrenheit)
        return useCelsius ? temperature : (temperature * 9 / 5) + 32;
    }

    public void setUseCelsius(boolean useCelsius) {
        this.useCelsius = useCelsius;
    }
}

// Concrete Decorator class for wind speed
class WindSpeedDecorator implements WeatherDataDecorator {
    private final WeatherDataDecorator decoratedObserver;
    private float windSpeed;

    public WindSpeedDecorator(WeatherDataDecorator decoratedObserver) {
        this.decoratedObserver = decoratedObserver;
    }

    public void update(float temperature, float humidity, float pressure) {
        decoratedObserver.update(temperature, humidity, pressure);
        displayWindSpeed();
    }

    public void display() {
        decoratedObserver.display();
    }

    private void displayWindSpeed() {
        System.out.println("Wind Speed: " + windSpeed + " mph");
    }

    public void setWindSpeed(float windSpeed) {
        this.windSpeed = windSpeed;
    }
}

// Concrete Decorator class for precipitation
class PrecipitationDecorator implements WeatherDataDecorator {
    private final WeatherDataDecorator decoratedObserver;
    private String precipitationType;

    public PrecipitationDecorator(WeatherDataDecorator decoratedObserver) {
        this.decoratedObserver = decoratedObserver;
    }

    public void update(float temperature, float humidity, float pressure) {
        decoratedObserver.update(temperature, humidity, pressure);
        displayPrecipitation();
    }

    public void display() {
        decoratedObserver.display();
    }

    private void displayPrecipitation() {
        System.out.println("Precipitation: " + precipitationType);
    }

    public void setPrecipitationType(String precipitationType) {
        this.precipitationType = precipitationType;
    }
}

// Part 3: Integration
class WeatherStation {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();

        // Create observers
        CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
        StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);

        // Decorate observers
        TemperatureUnitsDecorator temperatureDecorator = new TemperatureUnitsDecorator(currentDisplay);
        WindSpeedDecorator windSpeedDecorator = new WindSpeedDecorator(statisticsDisplay);
        PrecipitationDecorator precipitationDecorator = new PrecipitationDecorator(windSpeedDecorator);

        // Set up initial weather data
        weatherData.setMeasurements(85, 68, 36);

        // Update and display decorated weather information
        temperatureDecorator.setUseCelsius(true); // Switch to Celsius
        windSpeedDecorator.setWindSpeed(10.5f);
        precipitationDecorator.setPrecipitationType("Rain");
        weatherData.setMeasurements(75, 70, 38);
    }
}