use crate::framebuffer::FrameBuffer;
use crate::handlers::LineHandler;
use rusttype::LayoutIter;
use rusttype::{point, FontCollection, Scale};
use serenity::async_trait;
use serenity::http::AttachmentType;
use serenity::model::channel::Message;
use serenity::prelude::*;
use std::borrow::Cow;

const COLOUR: (u8, u8, u8) = (255, 0, 0);
const SCALE: Scale = Scale { x: 48.0, y: 48.0 };
const PADDING_X: u32 = 0;
const PADDING_Y: u32 = 0;
const MAX_WIDTH: i32 = 800;
const Y_GAP: i32 = 5;

// Wrap text when width exceeds MAX_WIDTH
struct WrappingLayoutIter<'a> {
	iter: LayoutIter<'a, 'a>,
	offset_x: i32,
	offset_y: i32,
	cur_x: i32,
	cur_y: i32,
}

impl<'a> WrappingLayoutIter<'a> {
	fn new(iter: LayoutIter<'a, 'a>, offset_x: i32, offset_y: i32) -> Self {
		Self {
			iter,
			offset_x,
			offset_y,
			cur_x: 0,
			cur_y: 0,
		}
	}
}

impl<'a> Iterator for WrappingLayoutIter<'a> {
	type Item = Vec<(i32, i32, f32)>;

	fn next(&mut self) -> Option<Self::Item> {
		let (glyph, bounding_box) = loop {
			let glyph = self.iter.next()?;
			if let Some(bb) = glyph.pixel_bounding_box() {
				break (glyph, bb);
			}
		};

		if bounding_box.max.x + self.cur_x > MAX_WIDTH {
			self.cur_x = -bounding_box.min.x;
			self.cur_y += bounding_box.max.y - bounding_box.min.y + Y_GAP;
		}

		let mut buf = Vec::new();

		glyph.draw(|x, y, a| {
			let x = x as i32 + bounding_box.min.x + self.offset_x + self.cur_x;
			let y = y as i32 + bounding_box.min.y + self.offset_y + self.cur_y;

			buf.push((x, y, a));
		});

		Some(buf)
	}
}

#[derive(Default)]
pub struct SusHandler;

impl SusHandler {
	async fn render_font(&self, ctx: &Context, msg: &Message, s: &str) {
		let font_data = include_bytes!("../amongus.ttf");
		let collection = FontCollection::from_bytes(font_data as &[u8]);
		let font = collection.into_font().unwrap();

		let start = point(0.0, 0.0);

		// Find image dimensions
		let mut min_x = 0;
		let mut max_x = 0;
		let mut max_y = 0;
		let mut min_y = 0;
		for arr in WrappingLayoutIter::new(font.layout(s, SCALE, start), 0, 0) {
			for (x, y, _) in arr {
				if x < min_x {
					min_x = x;
				}

				if x > max_x {
					max_x = x;
				}

				if y < min_y {
					min_y = y;
				}

				if y > max_y {
					max_y = y;
				}
			}
		}

		let offset_x = -min_x;
		let offset_y = -min_y;
		max_x += offset_x;
		max_y += offset_y;

		let mut framebuffer =
			FrameBuffer::new(max_x as u32 + PADDING_X * 2, max_y as u32 + PADDING_Y * 2);

		for arr in WrappingLayoutIter::new(
			font.layout(s, SCALE, start),
			offset_x + PADDING_X as i32,
			offset_y + PADDING_Y as i32,
		) {
			for (x, y, a) in arr {
				framebuffer.set_pixel(
					x as u32,
					y as u32,
					COLOUR.0,
					COLOUR.1,
					COLOUR.2,
					(a * 255.0) as u8,
				);
			}
		}

		let buf = framebuffer.as_png_vec();

		msg.channel_id
			.send_message(&ctx, |e| {
				e.add_file(AttachmentType::Bytes {
					data: Cow::Borrowed(&buf),
					filename: "output.png".to_string(),
				});

				e
			})
			.await
			.unwrap();
	}
}

#[async_trait]
impl LineHandler for SusHandler {
	async fn line(&self, ctx: &Context, msg: &Message, line: &str) {
		if line.starts_with("!SUS") {
			let s = line.split(' ').collect::<Vec<_>>()[1..]
				.join(" ")
				.to_uppercase();

			if s.is_empty() {
				return;
			}

			self.render_font(ctx, msg, &s).await;
		}
	}
}