import Accessor from "@arcgis/core/core/Accessor.js";
import { property } from "@arcgis/core/core/accessorSupport/decorators.js";
import WeatherResponse from "../model/weather-response";
import esriRequest from "@arcgis/core/request.js";

//Interface for the parameters
export interface IWeatherWidgetParameters {
    weatherAPI: string | undefined;
    weatherURL: string | undefined;
    view: __esri.MapView | __esri.SceneView | undefined;
}

//constant for weather types in Esri JSAPI
export const weatherType = {
    SUNNY: 'sunny',
    FOGGY: 'foggy',
    CLOUDY: 'cloudy',
    RAINY: 'rainy',
    SNOWY: 'snowy'
}

//interface for conditions returned from weatherAPI.com
interface IWeatherCondition {
    code: number,
    day: string,
    night: string,
    icon: number,
    esriCode: string
}

//Constant that matches result codes from weatherAPI.com to esri weather type
//rainy,snowy,foggy,sunny,cloudy
export const weatherMatch: Array<IWeatherCondition> = [
    {
        "code": 1000,
        "day": "Sunny",
        "night": "Clear",
        "icon": 113,
        "esriCode": weatherType.SUNNY
    },
    {
        "code": 1003,
        "day": "Partly cloudy",
        "night": "Partly cloudy",
        "icon": 116,
        "esriCode": weatherType.SUNNY
    },
    {
        "code": 1006,
        "day": "Cloudy",
        "night": "Cloudy",
        "icon": 119,
        "esriCode": weatherType.CLOUDY

    },
    {
        "code": 1009,
        "day": "Overcast",
        "night": "Overcast",
        "icon": 122,
        "esriCode": weatherType.CLOUDY
    },
    {
        "code": 1030,
        "day": "Mist",
        "night": "Mist",
        "icon": 143,
        "esriCode": weatherType.FOGGY
    },
    {
        "code": 1063,
        "day": "Patchy rain possible",
        "night": "Patchy rain possible",
        "icon": 176,
        "esriCode": weatherType.RAINY
    },
    {
        "code": 1066,
        "day": "Patchy snow possible",
        "night": "Patchy snow possible",
        "icon": 179,
        "esriCode": weatherType.SNOWY
    },
    {
        "code": 1069,
        "day": "Patchy sleet possible",
        "night": "Patchy sleet possible",
        "icon": 182,
        "esriCode": weatherType.SNOWY
    },
    {
        "code": 1072,
        "day": "Patchy freezing drizzle possible",
        "night": "Patchy freezing drizzle possible",
        "icon": 185,
        "esriCode": weatherType.SNOWY
    },
    {
        "code": 1087,
        "day": "Thundery outbreaks possible",
        "night": "Thundery outbreaks possible",
        "icon": 200,
        "esriCode": weatherType.RAINY
    },
    {
        "code": 1114,
        "day": "Blowing snow",
        "night": "Blowing snow",
        "icon": 227,
        "esriCode": weatherType.SNOWY
    },
    {
        "code": 1117,
        "day": "Blizzard",
        "night": "Blizzard",
        "icon": 230,
        "esriCode": weatherType.SNOWY
    },
    {
        "code": 1135,
        "day": "Fog",
        "night": "Fog",
        "icon": 248,
        "esriCode": weatherType.FOGGY
    },
    {
        "code": 1147,
        "day": "Freezing fog",
        "night": "Freezing fog",
        "icon": 260,
        "esriCode": weatherType.FOGGY
    },
    {
        "code": 1150,
        "day": "Patchy light drizzle",
        "night": "Patchy light drizzle",
        "icon": 263,
        "esriCode": weatherType.RAINY
    },
    {
        "code": 1153,
        "day": "Light drizzle",
        "night": "Light drizzle",
        "icon": 266,
        "esriCode": weatherType.RAINY
    },
    {
        "code": 1168,
        "day": "Freezing drizzle",
        "night": "Freezing drizzle",
        "icon": 281,
        "esriCode": weatherType.SNOWY
    },
    {
        "code": 1171,
        "day": "Heavy freezing drizzle",
        "night": "Heavy freezing drizzle",
        "icon": 284,
        "esriCode": weatherType.SNOWY
    },
    {
        "code": 1180,
        "day": "Patchy light rain",
        "night": "Patchy light rain",
        "icon": 293,
        "esriCode": weatherType.RAINY
    },
    {
        "code": 1183,
        "day": "Light rain",
        "night": "Light rain",
        "icon": 296,
        "esriCode": weatherType.RAINY
    },
    {
        "code": 1186,
        "day": "Moderate rain at times",
        "night": "Moderate rain at times",
        "icon": 299,
        "esriCode": weatherType.RAINY
    },
    {
        "code": 1189,
        "day": "Moderate rain",
        "night": "Moderate rain",
        "icon": 302,
        "esriCode": weatherType.RAINY
    },
    {
        "code": 1192,
        "day": "Heavy rain at times",
        "night": "Heavy rain at times",
        "icon": 305,
        "esriCode": weatherType.RAINY
    },
    {
        "code": 1195,
        "day": "Heavy rain",
        "night": "Heavy rain",
        "icon": 308,
        "esriCode": weatherType.RAINY
    },
    {
        "code": 1198,
        "day": "Light freezing rain",
        "night": "Light freezing rain",
        "icon": 311,
        "esriCode": weatherType.RAINY
    },
    {
        "code": 1201,
        "day": "Moderate or heavy freezing rain",
        "night": "Moderate or heavy freezing rain",
        "icon": 314,
        "esriCode": weatherType.RAINY
    },
    {
        "code": 1204,
        "day": "Light sleet",
        "night": "Light sleet",
        "icon": 317,
        "esriCode": weatherType.SNOWY
    },
    {
        "code": 1207,
        "day": "Moderate or heavy sleet",
        "night": "Moderate or heavy sleet",
        "icon": 320,
        "esriCode": weatherType.SNOWY
    },
    {
        "code": 1210,
        "day": "Patchy light snow",
        "night": "Patchy light snow",
        "icon": 323,
        "esriCode": weatherType.SNOWY
    },
    {
        "code": 1213,
        "day": "Light snow",
        "night": "Light snow",
        "icon": 326,
        "esriCode": weatherType.SNOWY
    },
    {
        "code": 1216,
        "day": "Patchy moderate snow",
        "night": "Patchy moderate snow",
        "icon": 329,
        "esriCode": weatherType.SNOWY
    },
    {
        "code": 1219,
        "day": "Moderate snow",
        "night": "Moderate snow",
        "icon": 332,
        "esriCode": weatherType.SNOWY
    },
    {
        "code": 1222,
        "day": "Patchy heavy snow",
        "night": "Patchy heavy snow",
        "icon": 335,
        "esriCode": weatherType.SNOWY
    },
    {
        "code": 1225,
        "day": "Heavy snow",
        "night": "Heavy snow",
        "icon": 338,
        "esriCode": weatherType.SNOWY
    },
    {
        "code": 1237,
        "day": "Ice pellets",
        "night": "Ice pellets",
        "icon": 350,
        "esriCode": weatherType.SNOWY
    },
    {
        "code": 1240,
        "day": "Light rain shower",
        "night": "Light rain shower",
        "icon": 353,
        "esriCode": weatherType.RAINY
    },
    {
        "code": 1243,
        "day": "Moderate or heavy rain shower",
        "night": "Moderate or heavy rain shower",
        "icon": 356,
        "esriCode": weatherType.RAINY
    },
    {
        "code": 1246,
        "day": "Torrential rain shower",
        "night": "Torrential rain shower",
        "icon": 359,
        "esriCode": weatherType.RAINY
    },
    {
        "code": 1249,
        "day": "Light sleet showers",
        "night": "Light sleet showers",
        "icon": 362,
        "esriCode": weatherType.RAINY
    },
    {
        "code": 1252,
        "day": "Moderate or heavy sleet showers",
        "night": "Moderate or heavy sleet showers",
        "icon": 365,
        "esriCode": weatherType.RAINY
    },
    {
        "code": 1255,
        "day": "Light snow showers",
        "night": "Light snow showers",
        "icon": 368,
        "esriCode": weatherType.RAINY
    },
    {
        "code": 1258,
        "day": "Moderate or heavy snow showers",
        "night": "Moderate or heavy snow showers",
        "icon": 371,
        "esriCode": weatherType.RAINY
    },
    {
        "code": 1261,
        "day": "Light showers of ice pellets",
        "night": "Light showers of ice pellets",
        "icon": 374,
        "esriCode": weatherType.RAINY
    },
    {
        "code": 1264,
        "day": "Moderate or heavy showers of ice pellets",
        "night": "Moderate or heavy showers of ice pellets",
        "icon": 377,
        "esriCode": weatherType.RAINY
    },
    {
        "code": 1273,
        "day": "Patchy light rain with thunder",
        "night": "Patchy light rain with thunder",
        "icon": 386,
        "esriCode": weatherType.RAINY
    },
    {
        "code": 1276,
        "day": "Moderate or heavy rain with thunder",
        "night": "Moderate or heavy rain with thunder",
        "icon": 389,
        "esriCode": weatherType.SNOWY
    },
    {
        "code": 1279,
        "day": "Patchy light snow with thunder",
        "night": "Patchy light snow with thunder",
        "icon": 392,
        "esriCode": weatherType.SNOWY
    },
    {
        "code": 1282,
        "day": "Moderate or heavy snow with thunder",
        "night": "Moderate or heavy snow with thunder",
        "icon": 395,
        "esriCode": weatherType.SNOWY
    }
]

//The viewmodel, extends from Accessor
export default class WeatherWidgetViewModel extends Accessor {

    @property()
    currentWeather: WeatherResponse | undefined;

    @property()
    weatherAPI: string | undefined;

    @property()
    weatherURL: string | undefined;

    @property()
    environment: {} | undefined;

    @property()
    view: __esri.MapView | __esri.SceneView | undefined;


    @property()
    time: string;

    constructor(params?: IWeatherWidgetParameters) {
        super();
        this.currentWeather = undefined;
        this.weatherAPI = params?.weatherAPI;
        this.weatherURL = params?.weatherURL;
        this.view = params?.view;
        this.time = "";
    }


    //Set the environment, only available on a web scene
    setEnvironment = () => {
        //Use the current time to set the lighting condtions
        let lighting = {
            type: "sun",
            date: this.currentWeather ? new Date(this.currentWeather.location.localtime_epoch * 1000) : new Date(),
            directShadowsEnabled: true
        };
        //set the environment based on the weather.
        this.environment = {
            lighting: lighting,
            weather: this.getWeather()
        }
        //we can only set the environment on a scene.
        if (this.view?.type === "3d") {
            let sceneView = this.view as __esri.SceneView;
            sceneView.environment = this.environment;
        }
    }

    //Get the percentage of cloud cover
    getCloud = () => {
        if (this.currentWeather?.current.cloud) {
            return this.currentWeather?.current.cloud / 100
        }
        return 0;
    }

    getFogCover = () => {
        //TODO translate visibility in to a percentage...

        return 0.50
    }

    //get the level of rainfall based on the result from the weatherAPI call.
    getPrecipitation = (weatherCondition: IWeatherCondition) => {

        if (weatherCondition.day.toLowerCase().indexOf('torrential') > -1 || weatherCondition.night.toLowerCase().indexOf('torrential') > -1) {
            return 100;
        }
        if (weatherCondition.day.toLowerCase().indexOf('heavy') > -1 || weatherCondition.night.toLowerCase().indexOf('heavy') > -1) {
            return 90;
        }
        if (weatherCondition.day.toLowerCase().indexOf('moderate') > -1 || weatherCondition.night.toLowerCase().indexOf('moderate') > -1) {
            return 60;
        }
        if (weatherCondition.day.toLowerCase().indexOf('light') > -1 || weatherCondition.night.toLowerCase().indexOf('light') > -1) {
            return 30;
        }
        if (weatherCondition.day.toLowerCase().indexOf('patchy') > -1 || weatherCondition.night.toLowerCase().indexOf('patchy') > -1) {
            return 30;
        }
        if (weatherCondition.day.toLowerCase().indexOf('drizzle') > -1 || weatherCondition.night.toLowerCase().indexOf('drizzle') > -1) {
            return 30;
        }
        if (weatherCondition.day.toLowerCase().indexOf('thundery') > -1 || weatherCondition.night.toLowerCase().indexOf('thundery') > -1) {
            return 30;
        }
        if (weatherCondition.day.toLowerCase().indexOf('blizzard') > -1 || weatherCondition.night.toLowerCase().indexOf('blizzard') > -1) {
            return 100;
        }
        if (weatherCondition.day.toLowerCase().indexOf('blowing') > -1 || weatherCondition.night.toLowerCase().indexOf('blowing') > -1) {
            return 30;
        }
        if (weatherCondition.day.toLowerCase().indexOf('pellets') > -1 || weatherCondition.night.toLowerCase().indexOf('pellets') > -1) {
            return 30;
        }
        return 0;
    }

    //get the weather conditons based on the current weather
    getWeather = () => {
        let weatherCondition = weatherMatch.find(f => {
            if (this.currentWeather?.current.condition.code === f.code) {
                return f;
            }
            return undefined;
        });

        if (!weatherCondition) {
            return {
                type: weatherType.SUNNY,
                cloudCover: this.getCloud()
            }
        }

        switch (weatherCondition.esriCode) {
            case weatherType.CLOUDY:
                return {
                    type: weatherType.CLOUDY,
                    cloudCover: this.getCloud()
                }
            case weatherType.FOGGY:
                return {
                    type: weatherType.FOGGY,
                    fogStrength: this.getFogCover()
                }
            case weatherType.RAINY:
                return {
                    type: weatherType.RAINY,
                    cloudCover: this.getCloud(),
                    precipitation: this.getPrecipitation(weatherCondition)
                }
            case weatherType.SNOWY:
                return {
                    type: weatherType.SNOWY,
                    snowCover: "enabled",
                    cloudCover: this.getCloud(),
                    precipitation: this.getPrecipitation(weatherCondition)
                }
            case weatherType.SUNNY:
                return {
                    type: weatherType.SUNNY,
                    cloudCover: this.getCloud()
                }
            default:
                return {
                    type: weatherType.SUNNY,
                    cloudCover: this.getCloud()
                }
        }
    }

    //call the weather api to get the latest weather
    getLatestWeather = (lat: number, long: number): Promise<boolean> => {

        let request = `${this.weatherURL}/forecast.json?key=${this.weatherAPI}&q=${lat},${long}&aqi=yes`;
        return esriRequest(request, { responseType: "json" }).then(response => {
            this.currentWeather = new WeatherResponse(response.data);
            this.time = this.currentWeather.location.localtime.split(' ')[1];
            this.setEnvironment();
            return true;
        }).catch(error => {
            console.error(error)
            return false;
        })
    }
}


