extern crate random_number;
extern crate sdl2;
use sdl2::event::Event;
use sdl2::image::{InitFlag, LoadTexture};
use sdl2::keyboard::Keycode;
use std::path::{Path}; use std::time::Duration; use std::collections::HashMap;
use std::sync::{Arc, Mutex, Condvar};
use std::thread;
mod audio_player;
pub use crate::main_loop::audio_player::{play_sound, play_sound_blocking};
mod intent_receiver;
pub use crate::main_loop::intent_receiver::listen;
mod chronometer;
pub use crate::main_loop::chronometer::{get_time, display_chronometer};
mod weather;
use crate::main_loop::weather::show_weather;
pub struct State {
pub current_intent : String, pub audio_finished : bool, pub new_intent : bool }
impl State {
pub fn new() -> State {
State { current_intent : "default".to_owned(), audio_finished : false, new_intent : false }
}
}
pub type StateMutex = Arc<Mutex<State>>;
const CHRONOMETER_STATE : &str = "chronometer";
const WEATHER_STATE : &str = "weather";
pub fn run( address : String, port : String,
res_width : u32, res_height : u32,
api_key : Option<String>, location : Option<String>,
country : Option<String>,
intent_faces : HashMap<String, Vec<Vec<u8>>>,
intent_audio : HashMap<String, Vec<Vec<u8>>>,
intent_timings : HashMap<String, u64> ) -> Result<(), String> {
let sdl_context = sdl2::init()?;
let video_subsystem = sdl_context.video()?;
let _image_context = sdl2::image::init(InitFlag::PNG | InitFlag::JPG)?;
let window = video_subsystem
.window("rust-sdl2 demo: Video", res_width, res_height)
.position_centered()
.opengl()
.build()
.map_err(|e| e.to_string())?;
let mut canvas = window
.into_canvas()
.build()
.map_err(|e| e.to_string())?;
let texture_creator = canvas.texture_creator();
let ttf_context = sdl2::ttf::init().map_err(|e| e.to_string())?;
let state : StateMutex = Arc::new(Mutex::new(State::new()));
let new_intent_available = Arc::new((Mutex::new(false), Condvar::new()));
let new_intent_available_clone = Arc::clone(&new_intent_available);
let state_clone = Arc::clone(&state);
thread::spawn(move || {intent_receiver::listen(address, port, state_clone, new_intent_available_clone).unwrap(); });
let sleep_time = 100;
let mut time_limit = 0; let mut time_slept = u64::MAX; let mut audio_available = false; let mut played_audio = false; let mut loaded_face = false; let mut time_limit_loaded = false;
let mut current_face : &Vec<u8> = &Vec::new(); let mut current_intent_clone = "default".to_owned();
'mainloop: loop {
if let Ok(mut state) = state.lock() { if audio_available && played_audio && state.audio_finished ||
!audio_available && time_slept > time_limit {
if ! state.new_intent {
current_intent_clone = "default".to_owned();
} else {
if state.current_intent == CHRONOMETER_STATE || state.current_intent == WEATHER_STATE {
current_intent_clone = state.current_intent.to_owned();
} else {
match intent_faces.get(&state.current_intent) {
Some(_) => {
current_intent_clone = state.current_intent.to_owned();
}
None => {current_intent_clone = "default".to_owned();},
};
}
state.new_intent = false;
let (lock, _) = &*new_intent_available;
let mut n_i = lock.lock().unwrap();
*n_i = false;
println!("---------------------------");
println!("new intent!, changing to {}", current_intent_clone);
println!("---------------------------");
}
time_slept = 0;
audio_available = false;
played_audio = false;
loaded_face = false;
time_limit_loaded = false;
state.audio_finished = false;
}
}
if current_intent_clone == CHRONOMETER_STATE { let duration = get_time(res_width, res_height, &mut canvas, &ttf_context, &texture_creator, Arc::clone(&state),
Arc::clone(&new_intent_available))?;
display_chronometer(res_width, res_height, &mut canvas, &ttf_context, &texture_creator, duration)?;
current_intent_clone = "default".to_owned(); } else if current_intent_clone == WEATHER_STATE { if let Some(ref key) = api_key {
if let Some(ref location) = location {
if let Some(ref country) = country {
show_weather(res_width, res_height, &key, &location, &country, &mut canvas,
&ttf_context, &texture_creator, Arc::clone(&state),
Arc::clone(&new_intent_available))?;
}
}
} else {
eprintln!("Asked for weather, but didn't provide enough arguments at launch: ignoring");
}
current_intent_clone = "default".to_owned(); }
for event in sdl_context.event_pump()?.poll_iter() {
match event {
Event::Quit { .. }
| Event::KeyDown {
keycode: Option::Some(Keycode::Escape),
..
} => break 'mainloop,
_ => { }
}
}
canvas.clear();
if ! loaded_face || audio_available { current_face = match intent_faces.get(¤t_intent_clone) {
Some(x) => &x[random_number::random!(0, x.len() - 1)],
None => panic!("No faces found for intent {}.", current_intent_clone),
};
loaded_face = true;
}
if ! time_limit_loaded {
time_limit = match intent_timings.get(¤t_intent_clone) {
Some(x) => *x,
None => panic!("No timing found for intent {}.", current_intent_clone),
};
time_limit_loaded = true;
}
if ! played_audio {
if let Some(current_audio) = intent_audio.get(¤t_intent_clone) {
let state_clone = Arc::clone(&state);
play_sound(current_audio[random_number::random!(0, current_audio.len() - 1)].clone(), state_clone);
played_audio = true;
audio_available = true;
} else {
played_audio = true; audio_available = false;
}
}
let texture = texture_creator.load_texture_bytes(current_face)?;
canvas.copy(&texture, None, None)?;
canvas.present();
::std::thread::sleep(Duration::from_millis(sleep_time));
time_slept += sleep_time;
}
Ok(())
}