use crate::weather::{
WeatherCondition, WeatherConditions, WeatherData, WeatherLocation, WeatherUnits,
format_precipitation, format_temperature, format_wind_speed,
};
use std::time::Instant;
pub struct AppState {
pub current_weather: Option<WeatherData>,
pub is_offline: bool,
pub weather_conditions: WeatherConditions,
pub loading_state: LoadingState,
pub cached_weather_info: String,
pub weather_info_needs_update: bool,
pub location: WeatherLocation,
pub hide_location: bool,
pub units: WeatherUnits,
}
impl AppState {
pub fn new(location: WeatherLocation, hide_location: bool, units: WeatherUnits) -> Self {
Self {
current_weather: None,
is_offline: false,
weather_conditions: WeatherConditions::default(),
loading_state: LoadingState::new(),
cached_weather_info: String::new(),
weather_info_needs_update: true,
location,
hide_location,
units,
}
}
pub fn update_weather(&mut self, weather: WeatherData) {
self.weather_conditions.is_thunderstorm = weather.condition.is_thunderstorm();
self.weather_conditions.is_snowing = weather.condition.is_snowing();
self.weather_conditions.is_raining =
weather.condition.is_raining() && !self.weather_conditions.is_thunderstorm;
self.weather_conditions.is_cloudy = weather.condition.is_cloudy();
self.weather_conditions.is_foggy = weather.condition.is_foggy();
self.weather_conditions.is_day = weather.is_day;
self.current_weather = Some(weather);
self.is_offline = false;
self.weather_info_needs_update = true;
}
pub fn set_offline_mode(&mut self, offline: bool) {
self.is_offline = offline;
self.weather_info_needs_update = true;
}
pub fn update_loading_animation(&mut self) {
if self.loading_state.should_update() {
self.loading_state.next_frame();
self.weather_info_needs_update = true;
}
}
pub fn get_condition_text(&self) -> &str {
if let Some(ref weather) = self.current_weather {
match weather.condition {
WeatherCondition::Clear => "Clear",
WeatherCondition::Cloudy => "Cloudy",
WeatherCondition::PartlyCloudy => "Partly Cloudy",
WeatherCondition::Overcast => "Overcast",
WeatherCondition::Fog => "Fog",
WeatherCondition::Drizzle => "Drizzle",
WeatherCondition::FreezingRain => "Freezing Rain",
WeatherCondition::Rain => "Rain",
WeatherCondition::Snow => "Snow",
WeatherCondition::SnowGrains => "Snow Grains",
WeatherCondition::RainShowers => "Rain Showers",
WeatherCondition::SnowShowers => "Snow Showers",
WeatherCondition::Thunderstorm => "Thunderstorm",
WeatherCondition::ThunderstormHail => "Thunderstorm with Hail",
}
} else {
"Loading"
}
}
pub fn update_cached_info(&mut self) {
if !self.weather_info_needs_update {
return;
}
let location_str = if self.hide_location {
String::new()
} else {
format!(
" | Location: {:.2}°N, {:.2}°E",
self.location.latitude, self.location.longitude
)
};
self.cached_weather_info = if let Some(ref weather) = self.current_weather {
let (temp, temp_unit) = format_temperature(weather.temperature, self.units.temperature);
let (wind, wind_unit) = format_wind_speed(weather.wind_speed, self.units.wind_speed);
let (precip, precip_unit) =
format_precipitation(weather.precipitation, self.units.precipitation);
let offline_indicator = if self.is_offline { "OFFLINE | " } else { "" };
format!(
"{}Weather: {} | Temp: {:.1}{} | Wind: {:.1}{} | Precip: {:.1}{}{} | Press 'q' to quit",
offline_indicator,
self.get_condition_text(),
temp,
temp_unit,
wind,
wind_unit,
precip,
precip_unit,
location_str
)
} else {
format!("Weather: Loading... {}", self.loading_state.current_char())
};
self.weather_info_needs_update = false;
}
pub fn should_show_sun(&self) -> bool {
if !self.weather_conditions.is_day {
return false;
}
if let Some(ref weather) = self.current_weather {
matches!(
weather.condition,
WeatherCondition::Clear | WeatherCondition::PartlyCloudy | WeatherCondition::Cloudy
)
} else {
false
}
}
pub fn should_show_fireflies(&self) -> bool {
if self.weather_conditions.is_day {
return false;
}
if let Some(ref weather) = self.current_weather {
let is_warm = weather.temperature > 15.0;
let is_clear_night = matches!(
weather.condition,
WeatherCondition::Clear | WeatherCondition::PartlyCloudy
);
is_warm
&& is_clear_night
&& !self.weather_conditions.is_raining
&& !self.weather_conditions.is_thunderstorm
&& !self.weather_conditions.is_snowing
} else {
false
}
}
}
pub struct LoadingState {
pub frame: usize,
pub last_update: Instant,
pub loading_chars: [char; 4],
}
impl LoadingState {
pub fn new() -> Self {
Self {
frame: 0,
last_update: Instant::now(),
loading_chars: ['|', '/', '-', '\\'],
}
}
pub fn should_update(&self) -> bool {
self.last_update.elapsed() >= std::time::Duration::from_millis(100)
}
pub fn next_frame(&mut self) {
self.frame = (self.frame + 1) % self.loading_chars.len();
self.last_update = Instant::now();
}
pub fn current_char(&self) -> char {
self.loading_chars[self.frame]
}
}