setup.weathers = {
	sunnny:{
		desc: 'The sun shines without a cloud in the sky.',
		clouds: 0
	}
}

setup.weatherSituations = {
	autumnNormal:{
		temperature:{
			day: 18,
			night: 8,
			variance: 3
		},
		weather:{
			sunnny: 10,
			sunnyClouds: 30,
			clouds: 40,
			rain: 20
		}
	},
	summerHot:{
		temperature:{
			day: 35,
			night: 25,
			variance: 3
		},
		weather:{
			sunnny: 50,
			sunnyClouds: 50
		}
	},
	summerWarm:{
		temperature:{
			day: 30,
			night: 20,
			variance: 3
		},
		weather:{
			sunnny: 30,
			sunnyClouds: 45,
			clouds: 20,
			rain: 5
		}
	},
	summerNormal:{
		temperature:{
			day: 25,
			night: 15,
			variance: 3
		},
		weather:{
			sunnny: 20,
			sunnyClouds: 20,
			clouds: 30,
			rain: 30
		}
	},
	summerRainy:{
		temperature:{
			day: 20,
			night: 15,
			variance: 3
		},
		weather:{
			sunnyClouds: 10,
			clouds: 30,
			rain: 60
		}
	},
}

setup.weatherByMonth = {
	8:{
		summerHot: 10,
		summerWarm: 30,
		summerNormal: 40,
		summerRainy:20
	},
	9:{
		summerHot: 5,
		summerWarm: 20,
		summerNormal: 40,
		summerRainy:35
	},
	10:{
		autumnNormal: 100
	}
}

setup.getWeatherByMonth = function(month){
	return (setup.weatherByMonth[month] ?? {summerNormal:100});
}

class Weather{

	_weatherSituationsForecast = {};

	get clouds(){
		return setup.weathers[this.weather]?.clouds ?? 50;
	}

	get desc(){
		var result = setup.weathers[this.weather]?.desc ?? "";

		result += "<br/>The temperature is "+this.temperature+"°C.";
		
		const hoursTilRain = this.hoursTilRain ?? -1;
		if(hoursTilRain == -1){
			result += "<hr>It doesn't look like it will rain in the next few hours.";
		}else if(hoursTilRain == 0){
			result += "<hr>It's raining.";
		}else if(hoursTilRain == 1){
			result += "<hr>It will start raining in a few minutes.";
		}else if(hoursTilRain < 3){
			result += "<hr>It will start raining in 1-3 hours.";
		}
		return result;
	}

	get hoursTilRain(){
		return this.hoursTilWeather('rain');
	}

	hoursTilWeather(weather){
		const hourNow = State.variables.time.hour;
		for (let hour = hourNow; hour < hourNow+24; hour++) {
			if(this.weatherOfHour(hour) == weather)
				return hour - hourNow;
		}
		return undefined;
	}

	get isRaining(){
		return (this.hoursTilRain == 0);
	}

	get weatherForecastToday(){
		return this.weatherForecastOfDayoffset(0);
	}

	weatherForecastOfDayoffset(offset){
		if(offset >= 5){
			console.error('Detailed weather offset is only available for 5 days');
		}
		const today = State.variables.time.daystart;
		const requestedDay = today + offset;

		var forecastForRequestedDay = this.weatherSituationsForecast[requestedDay];

		var dirty = false;

		if(!forecastForRequestedDay.weather){
			dirty = true;
			forecastForRequestedDay.weather = [];
			let weathersForSituationAsWeightedArray = Object.entries(setup.weatherSituations[forecastForRequestedDay.situation].weather).map(([key,v])=>({id:key,weight:v}));
			for (let hour = 0; hour <= 23; hour++) {
				let weather = weathersForSituationAsWeightedArray.getRandomWeighted().id;
				forecastForRequestedDay.weather.push(weather);
			}
		}

		if(!forecastForRequestedDay.temperature){
			dirty = true;
			forecastForRequestedDay.temperature = [];
			
			let temperatureDay = setup.weatherSituations[forecastForRequestedDay.situation].temperature.day;
			let temperatureNight = setup.weatherSituations[forecastForRequestedDay.situation].temperature.night;
			let temperatureVariance = setup.weatherSituations[forecastForRequestedDay.situation].temperature.variance;

			let todaysVariance = rand(-temperatureVariance,temperatureVariance);

			for (let hour = 0; hour <= 23; hour++) {
				let distanceFromNoon = Math.abs(12-hour);
				let percentageOfDayTemperature;
				switch(distanceFromNoon){
					case 0:
					case 1:
					case 2:
						percentageOfDayTemperature = 100;break;
					case 3:
					case 4:
						percentageOfDayTemperature = 80;break;
					case 5:
					case 6:
						percentageOfDayTemperature = 60;break;
					case 7:
					case 8:
						percentageOfDayTemperature = 40;break;
					case 9:
					case 10:
						percentageOfDayTemperature = 20;break;
					case 11:
					case 12:
						percentageOfDayTemperature = 0;break;
				}
				let temperature = ((temperatureDay * percentageOfDayTemperature) + (temperatureNight * (100-percentageOfDayTemperature)))/100 + todaysVariance+rand(-1,1);

				forecastForRequestedDay.temperature.push(temperature);
			}
		}

		if(dirty)
			this.weatherSituationsForecast[requestedDay] = forecastForRequestedDay;

		return forecastForRequestedDay;
	}

	get weatherSituationsForecast(){
		const today = State.variables.time.daystart;
		if(!Object.keys(this._weatherSituationsForecast).includes((today+5).toString())){
			const now = State.variables.time.now;
			let currentDay = today;
			while(currentDay <= today + 5){
				if(Object.keys(this._weatherSituationsForecast).includes(currentDay.toString())){
					currentDay += 1;
					continue;
				}
				let dayOffset = currentDay - today;
				let dateOfCurrentDay = (new Date(now.getTime()));
				dateOfCurrentDay.setUTCDate(now.getUTCDate()+dayOffset);
				let monthOfCurrentDay = dateOfCurrentDay.getUTCMonth()+1;
				let lengthOfSituation = rand(2,4)+rand(2,4);
				let weightedSituationsObject = setup.getWeatherByMonth(monthOfCurrentDay);

				if(!weightedSituationsObject)
					console.error("weightedSituationsObject not found for month",monthOfCurrentDay);

				let weightedSituationArray = Object.entries(weightedSituationsObject).map(([key,v])=>({id:key,weight:v}));
				
				let weatherSituation = weightedSituationArray.getRandomWeighted().id;
				for (let index = 0; index < lengthOfSituation; index++) {
					this._weatherSituationsForecast[currentDay+index] = {situation:weatherSituation};
				}
				
				currentDay += lengthOfSituation;
			}

			console.log("Weather Forecast generated",this._weatherSituationsForecast);
			this.weatherSituationsForecastClean();

		}
		return this._weatherSituationsForecast;
	}

	weatherSituationsForecastClean(){
		const today = State.variables.time.daystart;
		this._weatherSituationsForecast = Object.fromEntries(Object.entries(this._weatherSituationsForecast).filter(([day,v])=>day>=today));
	}

	get temperature(){
		const hour = State.variables.time.hour;
		return this.weatherForecastToday.temperature[hour];
	}

	get weather(){
		const hour = State.variables.time.hour;
		return this.weatherForecastToday.weather[hour];
	}

	weatherOfHour(hour){
		const dayOffset = Math.floor(hour / 24);
		hour = hour % 24;
		const weatherForecast = this.weatherForecastOfDayoffset(dayOffset);
		return weatherForecast.weather[hour];
	}

    constructor(){}

    _init(weather){
        Object.keys(weather).forEach(function (pn) {
            this[pn] = clone(weather[pn]);
        }, this);

        return this;
    }

    clone = function () {
        return (new setup.Weather())._init(this);
    };

    toJSON = function () {
        var ownData = {};
        Object.keys(this).forEach(function (pn) {
            if(typeof this[pn] !== "function")
                ownData[pn] = clone(this[pn]);
        }, this);
        return JSON.reviveWrapper('(new setup.Weather())._init($ReviveData$)', ownData);
    };
}

setup.Weather = Weather;
