use std::time::Instant;
use std::cell::RefCell;
use server;
use renderer_process::RendererProcess;
use animation::{Animation, MoveAnimation, RotateAnimation, AnimationStatus};
use state::{TurtleState, DrawingState, Path};
use query::{Query, Request, StateUpdate, DrawingCommand, Response};
use radians::Radians;
use {Point, Distance, Event};
use self::DrawingCommand::*;
pub struct TurtleWindow {
renderer: RefCell<RendererProcess>,
}
impl TurtleWindow {
pub fn new() -> TurtleWindow {
server::start();
Self {
renderer: RefCell::new(RendererProcess::new()),
}
}
pub fn fetch_turtle(&self) -> TurtleState {
match self.renderer.borrow_mut().send_query(Query::Request(Request::TurtleState)) {
Some(Response::TurtleState(state)) => state,
_ => panic!("bug: the renderer process did not sent back TurtleState"),
}
}
pub fn with_turtle_mut<F, T>(&mut self, update: F) -> T
where F: FnOnce(&mut TurtleState) -> T {
let mut turtle = self.fetch_turtle();
let result = update(&mut turtle);
self.renderer.borrow_mut().send_query(Query::Update(StateUpdate::TurtleState(turtle)));
result
}
pub fn fetch_drawing(&self) -> DrawingState {
match self.renderer.borrow_mut().send_query(Query::Request(Request::DrawingState)) {
Some(Response::DrawingState(state)) => state,
_ => panic!("bug: the renderer process did not sent back DrawingState"),
}
}
pub fn with_drawing_mut<F, T>(&mut self, update: F) -> T
where F: FnOnce(&mut DrawingState) -> T {
let mut drawing = self.fetch_drawing();
let result = update(&mut drawing);
self.renderer.borrow_mut().send_query(Query::Update(StateUpdate::DrawingState(drawing)));
result
}
fn set_temporary_path(&mut self, path: Option<Path>) {
self.renderer.borrow_mut().send_query(Query::Update(StateUpdate::TemporaryPath(path)));
}
pub fn poll_event(&mut self) -> Option<Event> {
match self.renderer.borrow_mut().send_query(Query::Request(Request::Event)) {
Some(Response::Event(event)) => event,
_ => panic!("bug: the renderer process did not sent back an Event"),
}
}
pub fn begin_fill(&mut self) {
let fill_color = self.fetch_turtle().fill_color;
self.send_drawing_command(BeginFill(fill_color));
}
pub fn end_fill(&mut self) {
self.send_drawing_command(EndFill);
}
pub fn clear(&mut self) {
self.send_drawing_command(Clear);
}
pub fn go_to(&mut self, end: Point) {
let TurtleState {position: start, speed, pen, ..} = self.fetch_turtle();
let distance = (end - start).len();
if !distance.is_normal() {
return;
}
let speed = speed.to_movement(); let total_millis = (distance / speed * 1000.).abs();
let animation = MoveAnimation {
path: Path {start, end, pen},
timer: Instant::now(),
total_millis,
};
self.play_animation(animation);
}
pub fn forward(&mut self, distance: Distance) {
if !distance.is_normal() {
return;
}
let TurtleState {position: start, speed, heading, pen, ..} = self.fetch_turtle();
let movement = Point {
x: distance * heading.cos(),
y: distance * heading.sin(),
};
let end = start + movement;
let speed = speed.to_movement(); let total_millis = (distance / speed * 1000.).abs();
let animation = MoveAnimation {
path: Path {start, end, pen},
timer: Instant::now(),
total_millis,
};
self.play_animation(animation);
}
pub fn rotate(&mut self, angle: Radians, clockwise: bool) {
if !angle.is_normal() {
return;
}
let TurtleState {heading, speed, ..} = self.fetch_turtle();
let speed = speed.to_rotation(); let total_millis = angle / speed * 1000.;
let total_millis = total_millis.to_radians().abs();
let animation = RotateAnimation {
start: heading,
delta_angle: angle,
clockwise,
timer: Instant::now(),
total_millis,
};
self.play_animation(animation);
}
fn play_animation<A: Animation>(&mut self, animation: A) {
loop {
let status = self.with_turtle_mut(|turtle| animation.advance(turtle));
match status {
AnimationStatus::Running(path) => self.set_temporary_path(path),
AnimationStatus::Complete(path) => {
if let Some(path) = path {
self.set_temporary_path(None);
self.send_drawing_command(StorePath(path));
}
break;
},
}
}
}
fn send_drawing_command(&self, command: DrawingCommand) {
self.renderer.borrow_mut().send_query(Query::Drawing(command));
}
}