/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.camel.component.weather;

import org.apache.camel.component.weather.geolocation.GeoLocation;
import org.apache.camel.component.weather.geolocation.GeoLocationProvider;

import static org.apache.camel.component.weather.WeatherMode.JSON;
import static org.apache.camel.util.ObjectHelper.isEmpty;

/**
 * Logic for determining the query based on the provided
 * configuration.
 */
public class WeatherQuery {
    private final WeatherConfiguration weatherConfiguration;
    private GeoLocationProvider geoLocationProvider;

    public WeatherQuery(WeatherConfiguration weatherConfiguration) {
        this.weatherConfiguration = weatherConfiguration;
    }

    public String getQuery() throws Exception {
        return getQuery(weatherConfiguration.getLocation());
    }

    public String getQuery(String location) throws Exception {
        String answer = "http://api.openweathermap.org/data/2.5/";
        if (weatherConfiguration.getLat() != null && weatherConfiguration.getLon() != null
                && weatherConfiguration.getRightLon() == null && weatherConfiguration.getTopLat() == null) {
            location = createLatLonQueryString();
        } else if (weatherConfiguration.getLat() != null && weatherConfiguration.getLon() != null
                && weatherConfiguration.getRightLon() != null && weatherConfiguration.getTopLat() != null) {
            location = "bbox=" + weatherConfiguration.getLon() + ","
                    + weatherConfiguration.getLat() + ","
                    + weatherConfiguration.getRightLon() + ","
                    + weatherConfiguration.getTopLat() + ","
                    + weatherConfiguration.getZoom() + "&cluster=yes";
        } else if (!isEmpty(weatherConfiguration.getZip())) {
            location = "zip=" + weatherConfiguration.getZip();
        } else if (weatherConfiguration.getIds() != null && weatherConfiguration.getIds().size() > 0) {
            location = "id=" + String.join(",", weatherConfiguration.getIds());
        } else if (isEmpty(location) || "current".equals(location)) {
            GeoLocation geoLocation = getCurrentGeoLocation();
            weatherConfiguration.setLat(geoLocation.getLatitude());
            weatherConfiguration.setLon(geoLocation.getLongitude());
            location = createLatLonQueryString();
        } else {
            // assuming the location is a town or country
            location = "q=" + location;
        }

        location = location + "&lang=" + weatherConfiguration.getLanguage();

        String context = createContext();
        answer += context + location;

        if (!isEmpty(weatherConfiguration.getPeriod())) {
            answer += "&cnt=" + weatherConfiguration.getPeriod();
        } else if (weatherConfiguration.getCnt() != null) {
            answer += "&cnt=" + weatherConfiguration.getCnt();
        }

        // append the desired measurement unit if not the default (which is metric)
        if (weatherConfiguration.getUnits() != null) {
            answer += "&units=" + weatherConfiguration.getUnits().name().toLowerCase();
        }

        // append the desired output mode if not the default (which is json)
        if (weatherConfiguration.getMode() != JSON) {
            answer += "&mode=" + weatherConfiguration.getMode().name().toLowerCase();
        }

        if (weatherConfiguration.getAppid() != null) {
            answer += "&APPID=" + weatherConfiguration.getAppid();
        }

        return answer;
    }

    private String createContext() {
        String answer;
        if (isBoxedQuery()) {
            if (weatherConfiguration.getWeatherApi() == WeatherApi.Station) {
                answer = "box/station?";
            } else {
                answer = "box/city?";
            }
        } else if (isGeoLocation() && weatherConfiguration.getCnt() != null) {
            if (weatherConfiguration.getWeatherApi() == WeatherApi.Station) {
                answer = "station/find?";
            } else {
                answer = "find?";
            }
        } else if (weatherConfiguration.getIds() != null && weatherConfiguration.getIds().size() > 0) {
            if (weatherConfiguration.getIds().size() == 1) {
                if (!isEmpty(weatherConfiguration.getPeriod())) {
                    if (weatherConfiguration.getWeatherApi() == WeatherApi.Hourly) {
                        answer = "forecast?";
                    } else {
                        answer = "forecast/daily?";
                    }
                } else if (weatherConfiguration.getWeatherApi() == WeatherApi.Station) {
                    answer = "station?";
                } else {
                    answer = "weather?";
                }
            } else {
                answer = "group?";
            }
        } else if (isEmpty(weatherConfiguration.getPeriod())) {
            answer = "weather?";
        } else {
            if (weatherConfiguration.getWeatherApi() == WeatherApi.Hourly) {
                answer = "forecast?";
            } else {
                answer = "forecast/daily?";
            }
        }
        return answer;
    }

    private boolean isGeoLocation() {
        return weatherConfiguration.getLat() != null && weatherConfiguration.getLon() != null;
    }

    private String createLatLonQueryString() {
        return "lat=" + weatherConfiguration.getLat() + "&lon=" + weatherConfiguration.getLon();
    }

    private boolean isBoxedQuery() {
        return weatherConfiguration.getTopLat() != null && weatherConfiguration.getRightLon() != null;
    }

    GeoLocation getCurrentGeoLocation() throws Exception {
        return geoLocationProvider.getCurrentGeoLocation();
    }

    void setGeoLocationProvider(GeoLocationProvider geoLocationProvider) {
        this.geoLocationProvider = geoLocationProvider;
    }
}
