use crate::state::State;
use druid::{
	BoxConstraints, Env, Event, EventCtx, LayoutCtx, LifeCycle, LifeCycleCtx, PaintCtx, Point,
	Rect, Selector, Size, UpdateCtx, Widget, WidgetPod,
};

type ChildBuilder = Box<dyn Fn(&Env) -> Box<dyn Widget<State>>>;

pub const SHOW_AT: Selector<(Point, BoxConstraints, ChildBuilder)> =
	Selector::new("dropdown.show-at");
pub const SHOW_MIDDLE: Selector<(BoxConstraints, ChildBuilder)> =
	Selector::new("dropdown.show-middle");
pub const HIDE: Selector = Selector::new("dropdown.hide");

pub struct Child {
	origin: Option<Point>,
	bc: BoxConstraints,
	widget: WidgetPod<State, Box<dyn Widget<State>>>,
}

pub struct Overlay {
	child: Option<Child>,
}

impl Overlay {
	pub fn new() -> Overlay {
		Overlay { child: None }
	}
}

impl Widget<State> for Overlay {
	fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut State, env: &Env) {
		let mut remove_child = false;
		if let Some(child) = &mut self.child {
			child.widget.event(ctx, event, data, env);
			match event {
				Event::MouseDown(mouse) => {
					if !child.widget.layout_rect().contains(mouse.pos) {
						ctx.set_active(true);
					}
					ctx.set_handled();
				}
				Event::MouseUp(mouse) => {
					if ctx.is_active() && !child.widget.layout_rect().contains(mouse.pos) {
						remove_child = true;
						ctx.set_active(false);
					}
					ctx.set_handled();
				}
				Event::MouseMove(_) => {
					ctx.set_handled();
				}
				_ => {}
			}
		}
		match event {
			Event::Command(cmd) if cmd.is(SHOW_AT) => {
				let (pos, bc, child_builder) = cmd.get_unchecked(SHOW_AT);
				self.child = Some(Child {
					origin: Some(*pos),
					bc: *bc,
					widget: WidgetPod::new(child_builder(env)),
				});
				ctx.children_changed();
			}
			Event::Command(cmd) if cmd.is(SHOW_MIDDLE) => {
				let (bc, child_builder) = cmd.get_unchecked(SHOW_MIDDLE);
				self.child = Some(Child {
					origin: None,
					bc: *bc,
					widget: WidgetPod::new(child_builder(env)),
				});
				ctx.children_changed();
			}
			Event::Command(cmd) if cmd.is(HIDE) => {
				remove_child = true;
			}
			_ => {}
		}
		if remove_child {
			self.child = None;
			ctx.request_layout();
			ctx.request_paint();
		}
	}

	fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &State, env: &Env) {
		if let Some(child) = &mut self.child {
			child.widget.lifecycle(ctx, event, data, env);
		}
	}

	fn update(&mut self, ctx: &mut UpdateCtx, _old_data: &State, data: &State, env: &Env) {
		if let Some(child) = &mut self.child {
			child.widget.update(ctx, data, env);
		}
	}

	fn layout(
		&mut self,
		ctx: &mut LayoutCtx,
		bc: &BoxConstraints,
		data: &State,
		env: &Env,
	) -> Size {
		if let Some(child) = &mut self.child {
			let size = child.widget.layout(ctx, &child.bc, data, env);
			let origin = child
				.origin
				.unwrap_or_else(|| (bc.max().to_vec2() / 2.0 - size.to_vec2() / 2.0).to_point());
			child
				.widget
				.set_layout_rect(ctx, data, env, Rect::from_origin_size(origin, size));
		}
		bc.max()
	}

	fn paint(&mut self, ctx: &mut PaintCtx, data: &State, env: &Env) {
		if let Some(child) = &mut self.child {
			child.widget.paint(ctx, data, env);
		}
	}
}