// Path: aula33-weather-async/src/main/java/util/IRequest.java
// public interface IRequest extends AutoCloseable{
//     CompletableFuture<Stream<String>> getContent(String path);
// 
//     @Override
//     public default void close() {
//     }
// }
// 
// Path: aula33-weather-async/src/main/java/weather/data/dto/LocationDto.java
// public class LocationDto {
//     private final ContainerDto[] country;
//     private final ContainerDto[] region;
//     private final double latitude;
//     private final double longitude;
// 
//     public LocationDto(ContainerDto[] country, ContainerDto[] region, double latitude, double longitude) {
//         this.country = country;
//         this.region = region;
//         this.latitude = latitude;
//         this.longitude = longitude;
//     }
// 
//     public String getCountry() {
//         return country[0].value;
//     }
// 
//     public String getRegion() {
//         return region[0].value;
//     }
// 
//     public double getLatitude() {
//         return latitude;
//     }
// 
//     public double getLongitude() {
//         return longitude;
//     }
// 
//     public static LocationDto valueOf(String line) {
//         String[] data = line.split("\t");
//         return new LocationDto(
//                 ContainerDto.arrayOf(data[1]),
//                 ContainerDto.arrayOf(data[2]),
//                 Double.parseDouble(data[3]),
//                 Double.parseDouble(data[4]));
//     }
// 
//     @Override
//     public String toString() {
//         return "LocationDto{" +
//                 "country='" + getCountry() + '\'' +
//                 ", region='" + getRegion() + '\'' +
//                 ", latitude=" + latitude +
//                 ", longitude=" + longitude +
//                 '}';
//     }
// 
//     static class ContainerDto {
//         final String value;
// 
//         public ContainerDto(String value) {
//             this.value = value;
//         }
// 
//         @Override
//         public String toString() {
//             return value;
//         }
// 
//         public static ContainerDto[] arrayOf(String s) {
//             ContainerDto dto = new ContainerDto(s);
//             return new ContainerDto[]{dto};
//         }
//     }
// }
// 
// Path: aula33-weather-async/src/main/java/weather/data/dto/WeatherInfoDto.java
// public class WeatherInfoDto {
//     private final LocalDate date;     // index 0
//     private final int tempC;          // index 2
//     private final String description; // index 10
//     private final double  precipMM;   // index 11
//     private final int feelsLikeC;     // index 24
// 
//     public WeatherInfoDto(LocalDate date, int tempC, String description, double precipMM, int feelsLikeC) {
//         this.date = date;
//         this.tempC = tempC;
//         this.description = description;
//         this.precipMM = precipMM;
//         this.feelsLikeC = feelsLikeC;
//     }
// 
//     public LocalDate getDate() {
//         return date;
//     }
// 
//     public int getTempC() {
//         return tempC;
//     }
// 
//     public String getDescription() {
//         return description;
//     }
// 
//     public double getPrecipMM() {
//         return precipMM;
//     }
// 
//     public int getFeelsLikeC() {
//         return feelsLikeC;
//     }
// 
//     @Override
//     public String toString() {
//         return "WeatherInfoDto{" +
//                 date +
//                 ", tempC=" + tempC +
//                 ", '" + description + '\'' +
//                 ", precipMM=" + precipMM +
//                 ", feelsLikeC=" + feelsLikeC +
//                 '}';
//     }
// 
//     /**
//      * Hourly information follows below the day according to the format of
//      * /past weather resource of the World Weather Online API
//      */
//     public static WeatherInfoDto valueOf(String line) {
//         String[] data = line.split(",");
//         return new WeatherInfoDto(
//                 LocalDate.parse(data[0]),
//                 Integer.parseInt(data[2]),
//                 data[10],
//                 Double.parseDouble(data[11]),
//                 Integer.parseInt(data[24]));
//     }
// }


import java.util.stream.Stream;
import util.IRequest;
import weather.data.dto.LocationDto;
import weather.data.dto.WeatherInfoDto;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UncheckedIOException;
import java.net.URL;
import java.time.LocalDate;
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;
            throw new UncheckedIOException(e);
        }
    }

    private final IRequest req;

    public WeatherWebApi(IRequest req) {
        this.req = req;
    }

    /**
     * E.g. http://api.worldweatheronline.com/free/v2/search.ashx?query=oporto&format=tab&key=*****
     */

    public CompletableFuture<Stream<LocationDto>> search(String query) {
        String path = WEATHER_HOST + WEATHER_SEARCH + WEATHER_SEARCH_ARGS;
        String url = String.format(path, query, WEATHER_TOKEN);
        return req.getContent(url)
                .thenApply(str -> str
                    .filter((String s) -> !s.startsWith("#"))
                    .map(LocationDto::valueOf));
    }

    /**
     * E.g. http://api.worldweatheronline.com/free/v2/search.ashx?query=oporto&format=tab&key=*****
     */
    public CompletableFuture<Stream<WeatherInfoDto>> pastWeather(
            double lat,
            double log,
            LocalDate from,
// Path: aula33-weather-async/src/main/java/util/IRequest.java
// public interface IRequest extends AutoCloseable{
//     CompletableFuture<Stream<String>> getContent(String path);
// 
//     @Override
//     public default void close() {
//     }
// }
// 
// Path: aula33-weather-async/src/main/java/weather/data/dto/LocationDto.java
// public class LocationDto {
//     private final ContainerDto[] country;
//     private final ContainerDto[] region;
//     private final double latitude;
//     private final double longitude;
// 
//     public LocationDto(ContainerDto[] country, ContainerDto[] region, double latitude, double longitude) {
//         this.country = country;
//         this.region = region;
//         this.latitude = latitude;
//         this.longitude = longitude;
//     }
// 
//     public String getCountry() {
//         return country[0].value;
//     }
// 
//     public String getRegion() {
//         return region[0].value;
//     }
// 
//     public double getLatitude() {
//         return latitude;
//     }
// 
//     public double getLongitude() {
//         return longitude;
//     }
// 
//     public static LocationDto valueOf(String line) {
//         String[] data = line.split("\t");
//         return new LocationDto(
//                 ContainerDto.arrayOf(data[1]),
//                 ContainerDto.arrayOf(data[2]),
//                 Double.parseDouble(data[3]),
//                 Double.parseDouble(data[4]));
//     }
// 
//     @Override
//     public String toString() {
//         return "LocationDto{" +
//                 "country='" + getCountry() + '\'' +
//                 ", region='" + getRegion() + '\'' +
//                 ", latitude=" + latitude +
//                 ", longitude=" + longitude +
//                 '}';
//     }
// 
//     static class ContainerDto {
//         final String value;
// 
//         public ContainerDto(String value) {
//             this.value = value;
//         }
// 
//         @Override
//         public String toString() {
//             return value;
//         }
// 
//         public static ContainerDto[] arrayOf(String s) {
//             ContainerDto dto = new ContainerDto(s);
//             return new ContainerDto[]{dto};
//         }
//     }
// }
// 
// Path: aula33-weather-async/src/main/java/weather/data/dto/WeatherInfoDto.java
// public class WeatherInfoDto {
//     private final LocalDate date;     // index 0
//     private final int tempC;          // index 2
//     private final String description; // index 10
//     private final double  precipMM;   // index 11
//     private final int feelsLikeC;     // index 24
// 
//     public WeatherInfoDto(LocalDate date, int tempC, String description, double precipMM, int feelsLikeC) {
//         this.date = date;
//         this.tempC = tempC;
//         this.description = description;
//         this.precipMM = precipMM;
//         this.feelsLikeC = feelsLikeC;
//     }
// 
//     public LocalDate getDate() {
//         return date;
//     }
// 
//     public int getTempC() {
//         return tempC;
//     }
// 
//     public String getDescription() {
//         return description;
//     }
// 
//     public double getPrecipMM() {
//         return precipMM;
//     }
// 
//     public int getFeelsLikeC() {
//         return feelsLikeC;
//     }
// 
//     @Override
//     public String toString() {
//         return "WeatherInfoDto{" +
//                 date +
//                 ", tempC=" + tempC +
//                 ", '" + description + '\'' +
//                 ", precipMM=" + precipMM +
//                 ", feelsLikeC=" + feelsLikeC +
//                 '}';
//     }
// 
//     /**
//      * Hourly information follows below the day according to the format of
//      * /past weather resource of the World Weather Online API
//      */
//     public static WeatherInfoDto valueOf(String line) {
//         String[] data = line.split(",");
//         return new WeatherInfoDto(
//                 LocalDate.parse(data[0]),
//                 Integer.parseInt(data[2]),
//                 data[10],
//                 Double.parseDouble(data[11]),
//                 Integer.parseInt(data[24]));
//     }
// }


// Path: aula33-weather-async/src/main/java/weather/data/WeatherWebApi.java
import java.util.stream.Stream;
import util.IRequest;
import weather.data.dto.LocationDto;
import weather.data.dto.WeatherInfoDto;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UncheckedIOException;
import java.net.URL;
import java.time.LocalDate;
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;

            throw new UncheckedIOException(e);
        }
    }

    private final IRequest req;

    public WeatherWebApi(IRequest req) {
        this.req = req;
    }

    /**
     * E.g. http://api.worldweatheronline.com/free/v2/search.ashx?query=oporto&format=tab&key=*****
     */

    public CompletableFuture<Stream<LocationDto>> search(String query) {
        String path = WEATHER_HOST + WEATHER_SEARCH + WEATHER_SEARCH_ARGS;
        String url = String.format(path, query, WEATHER_TOKEN);
        return req.getContent(url)
                .thenApply(str -> str
                    .filter((String s) -> !s.startsWith("#"))
                    .map(LocationDto::valueOf));
    }

    /**
     * E.g. http://api.worldweatheronline.com/free/v2/search.ashx?query=oporto&format=tab&key=*****
     */
    public CompletableFuture<Stream<WeatherInfoDto>> pastWeather(
            double lat,
            double log,
            LocalDate from,
            LocalDate to