use crate::prelude::*;
use anyhow::Result;
use assetpath::AssetPath;
use serde::{Deserialize, Serialize};
use utilities::{impl_reprc, prelude::*};
use vulkan_rs::{prelude::*, render_target::sub_pass::InputAttachmentInfo};

use super::{
    elements::Elements,
    gui::{
        colorable::ColorableVertex, iconizable::IconBuilderType, texturedvertex::TexturedVertex,
    },
};

use cgmath::{ortho, vec2, vec4};

use std::sync::Weak;
use std::sync::{
    atomic::{AtomicBool, AtomicU32, Ordering::SeqCst},
    Arc, Mutex, RwLock,
};
use std::{collections::HashMap, ptr};

use paste::paste;

#[derive(Deserialize, Serialize, Clone, Debug)]
pub enum Font<'a> {
    Path(AssetPath),
    Bytes(&'a [u8]),
}

impl Default for Font<'_> {
    fn default() -> Self {
        Self::Path(AssetPath::default())
    }
}

#[derive(Deserialize, Serialize, Clone, Debug, Default)]
pub struct GuiHandlerCreateInfo<'a> {
    // default button textures
    pub menu_button: AssetPath,
    pub menu_button_selected: AssetPath,

    // path to the alphabet image
    #[serde(borrow)]
    pub font: Font<'a>,

    // sound info
    #[cfg(feature = "audio")]
    pub click_sound: AssetPath,
    #[cfg(feature = "audio")]
    pub hover_sound: AssetPath,

    // resource base directory
    pub resource_directory: AssetPath,
}

impl<'a> GuiHandlerCreateInfo<'a> {
    pub const fn new() -> Self {
        GuiHandlerCreateInfo {
            // default button textures
            menu_button: AssetPath::new(),
            menu_button_selected: AssetPath::new(),

            // path to the alphabet image
            font: Font::Path(AssetPath::new()),

            // sound info
            #[cfg(feature = "audio")]
            click_sound: AssetPath::new(),
            #[cfg(feature = "audio")]
            hover_sound: AssetPath::new(),

            // resource base directory
            resource_directory: AssetPath::new(),
        }
    }
}

struct GuiSeparator {
    _descriptor_layout: Arc<DescriptorSetLayout>,

    pipeline: Arc<Pipeline>,
}

struct DisplayableTexture {
    _descriptor_pool: Arc<DescriptorPool>,
    _descriptor_set: Arc<DescriptorSet>,

    _texture: Arc<Image>,
}

impl_reprc!(
    struct ColorBuffer {
        #[assume_reprc]
        color: f32,
    }
);

struct TextableColor {
    _descriptor_pool: Arc<DescriptorPool>,
    _descriptor_set: Arc<DescriptorSet>,

    _buffer: Arc<Buffer<ColorBuffer>>,
}

struct CommandBufferState {
    text_buffers: RwLock<Vec<Arc<Buffer<TexturedVertex>>>>,
}

struct TextToScreen {
    pipeline: TargetMode<GuiSeparator>,
    descriptor: TargetMode<Arc<DescriptorSet>>,
    buffer: Arc<Buffer<TexturedVertex>>,
}

impl TextToScreen {
    fn new(
        device: &Arc<Device>,
        pipeline: TargetMode<GuiSeparator>,
        render_target: &TargetMode<RwLock<RenderTarget>>,
    ) -> Result<Self> {
        let descriptor = pipeline
            .chain(render_target)
            .execute(|(separator, render_target)| {
                let descriptor = DescriptorPool::builder()
                    .set_layout(separator._descriptor_layout.clone())
                    .build(device.clone())?
                    .prepare_set()
                    .allocate()?;

                Self::update_descriptor(&descriptor, &*render_target.read().unwrap())?;

                Ok(descriptor)
            })?;

        let edges = [
            TexturedVertex {
                position: vec4(-1.0, -1.0, 0.0, 1.0),
                texture_coordinates: vec2(0.0, 0.0),
            },
            TexturedVertex {
                position: vec4(-1.0, 1.0, 0.0, 1.0),
                texture_coordinates: vec2(0.0, 1.0),
            },
            TexturedVertex {
                position: vec4(1.0, 1.0, 0.0, 1.0),
                texture_coordinates: vec2(1.0, 1.0),
            },
            TexturedVertex {
                position: vec4(1.0, 1.0, 0.0, 1.0),
                texture_coordinates: vec2(1.0, 1.0),
            },
            TexturedVertex {
                position: vec4(1.0, -1.0, 0.0, 1.0),
                texture_coordinates: vec2(1.0, 0.0),
            },
            TexturedVertex {
                position: vec4(-1.0, -1.0, 0.0, 1.0),
                texture_coordinates: vec2(0.0, 0.0),
            },
        ];

        let buffer = Buffer::builder()
            .set_usage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)
            .set_memory_usage(MemoryUsage::CpuOnly)
            .set_data(&edges)
            .build(device.clone())?;

        Ok(Self {
            pipeline,
            descriptor,
            buffer,
        })
    }

    fn update_descriptor(
        descriptor: &Arc<DescriptorSet>,
        render_target: &RenderTarget,
    ) -> Result<()> {
        // take resolve target of second sub pass as image sampler
        descriptor.update(&[DescriptorWrite::input_attachments(
            0,
            &[&render_target.sub_pass(1).attachments()[1].image(0)],
        )
        .change_image_layout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)])
    }

    fn update_on_resize(&self, render_target: &TargetMode<RwLock<RenderTarget>>) -> Result<()> {
        self.descriptor
            .chain(render_target)
            .execute(|(descriptor, render_target)| {
                Self::update_descriptor(descriptor, &*render_target.read().unwrap())
            })?;

        Ok(())
    }
}

pub struct GuiHandler {
    context: Arc<dyn ContextInterface>,

    device: Arc<Device>,
    queue: Arc<Mutex<Queue>>,

    width: AtomicU32,
    height: AtomicU32,

    resource_base_path: AssetPath,

    top_ui: RwLock<Option<Arc<dyn TopGui>>>,
    tooltip_ui: RwLock<Option<Arc<dyn TopGui>>>,

    render_targets: TargetMode<RwLock<RenderTarget>>,
    command_buffers: TargetMode<Vec<CommandBufferState>>,

    text_objects: TargetMode<GuiSeparator>,
    rectangle_objects: TargetMode<GuiSeparator>,
    single_color_objects: TargetMode<GuiSeparator>,

    text_to_screen: TextToScreen,

    _bitmap_font: Arc<Image>,
    _bitmap_desc_pool: Arc<DescriptorPool>,
    bitmap_desc_set: Arc<DescriptorSet>,

    text_color_layout: Arc<DescriptorSetLayout>,

    internal_icons: RwLock<HashMap<AssetPath, Weak<Image>>>,
    internal_textures: RwLock<HashMap<AssetPath, DisplayableTexture>>,
    internal_colors: RwLock<HashMap<Color, TextableColor>>,

    ortho: RwLock<cgmath::Matrix4<f32>>,

    icon_descriptor_layout: Arc<DescriptorSetLayout>,

    needs_update: AtomicBool,
    text_change_queue: RwLock<Vec<Box<dyn Fn() -> Result<()> + Send + Sync>>>,

    on_selected: RwLock<Option<Box<dyn Fn() -> Result<()> + Send + Sync>>>,

    menu_button: AssetPath,
    menu_button_selected: AssetPath,

    #[cfg(feature = "audio")]
    click_sound: AssetPath,

    #[cfg(feature = "audio")]
    hover_sound: AssetPath,

    // ----- gui handling -----
    layers: Mutex<Vec<(i32, Elements)>>,

    mouse_x: AtomicU32,
    mouse_y: AtomicU32,

    last_direction: Mutex<GuiDirection>,

    current_writeable: RwLock<Option<Arc<Writeable>>>,
    current_hoverable: RwLock<Option<Arc<Hoverable>>>,
    current_clickable: RwLock<Option<Arc<Clickable>>>,
    current_selectable: RwLock<Option<Arc<Selectable>>>,

    text_sample_count: VkSampleCountFlags,
}

impl GuiHandler {
    pub fn new(
        gui_handler_create_info: GuiHandlerCreateInfo<'_>,
        context: &Arc<dyn ContextInterface>,
    ) -> Result<Arc<GuiHandler>> {
        let device = context.device();
        let queue = context.queue();

        let command_buffers = context
            .images()
            .execute(|_| Self::create_command_buffers(context.image_count()))?;

        let text_sample_count = device.max_supported_sample_count(VK_SAMPLE_COUNT_4_BIT);

        let render_targets =
            Self::create_render_targets(device, &context.images(), queue, text_sample_count)?;

        let (text_objs, color_layout) =
            Self::init_text_objects(device, &render_targets, text_sample_count)?;
        let rect_objs = Self::init_rectangle_objects(device, &render_targets)?;
        let single_color_objects = Self::init_single_color_objects(device, &render_targets)?;

        let text_to_screen = TextToScreen::new(
            device,
            Self::init_text_screen_objects(device, &render_targets)?,
            &render_targets,
        )?;

        let (bitmap_texture, bitmap_desc_pool, bitmap_desc_set) = Self::init_bitmap_font(
            device,
            queue,
            match &text_objs {
                TargetMode::Mono(l) => l,
                TargetMode::Stereo(l, _) => l,
            }
            ._descriptor_layout
            .clone(),
            gui_handler_create_info.font.clone(),
        )?;

        let icon_descriptor_layout = DescriptorSetLayout::builder()
            .add_layout_binding(
                0,
                VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
                VK_SHADER_STAGE_FRAGMENT_BIT,
                0,
            )
            .build(device.clone())?;

        Ok(Arc::new(GuiHandler {
            context: context.clone(),

            device: device.clone(),
            queue: queue.clone(),

            width: AtomicU32::new(context.width()),
            height: AtomicU32::new(context.height()),

            menu_button: {
                let mut menu_button = gui_handler_create_info.menu_button;
                menu_button.set_prefix(&gui_handler_create_info.resource_directory.full_path());

                menu_button
            },

            menu_button_selected: {
                let mut menu_button_selected = gui_handler_create_info.menu_button_selected;
                menu_button_selected
                    .set_prefix(&gui_handler_create_info.resource_directory.full_path());

                menu_button_selected
            },

            #[cfg(feature = "audio")]
            click_sound: {
                let mut click_sound = gui_handler_create_info.click_sound;
                click_sound.set_prefix(&gui_handler_create_info.resource_directory.full_path());

                click_sound
            },

            #[cfg(feature = "audio")]
            hover_sound: {
                let mut hover_sound = gui_handler_create_info.hover_sound;
                hover_sound.set_prefix(&gui_handler_create_info.resource_directory.full_path());

                hover_sound
            },

            resource_base_path: gui_handler_create_info.resource_directory,

            top_ui: RwLock::new(None),
            tooltip_ui: RwLock::new(None),

            render_targets,
            command_buffers,

            text_objects: text_objs,
            rectangle_objects: rect_objs,
            single_color_objects,

            text_to_screen,

            _bitmap_font: bitmap_texture,
            _bitmap_desc_pool: bitmap_desc_pool,
            bitmap_desc_set,

            text_color_layout: color_layout,

            internal_icons: RwLock::new(HashMap::new()),
            internal_textures: RwLock::new(HashMap::new()),
            internal_colors: RwLock::new(HashMap::new()),

            icon_descriptor_layout,

            needs_update: AtomicBool::new(true),
            text_change_queue: RwLock::new(Vec::new()),

            on_selected: RwLock::new(None),

            layers: Mutex::default(),

            ortho: RwLock::new(ortho(
                0.0,
                context.width() as f32,
                0.0,
                context.height() as f32,
                -1.0,
                1.0,
            )),

            mouse_x: AtomicU32::new(0),
            mouse_y: AtomicU32::new(0),

            last_direction: Mutex::new(GuiDirection::None),

            current_clickable: RwLock::new(None),
            current_hoverable: RwLock::new(None),
            current_selectable: RwLock::new(None),
            current_writeable: RwLock::new(None),

            text_sample_count,
        }))
    }

    pub fn device(&self) -> &Arc<Device> {
        &self.device
    }

    pub fn queue(&self) -> &Arc<Mutex<Queue>> {
        &self.queue
    }

    #[cfg(feature = "audio")]
    pub(crate) fn context(&self) -> &Arc<dyn ContextInterface> {
        &self.context
    }

    pub fn width(&self) -> u32 {
        self.width.load(SeqCst)
    }

    pub fn height(&self) -> u32 {
        self.height.load(SeqCst)
    }

    pub(crate) fn icon_descriptor_layout(&self) -> &Arc<DescriptorSetLayout> {
        &self.icon_descriptor_layout
    }

    pub(crate) fn request_icon(
        &self,
        icon_builder: impl Into<IconBuilderType>,
    ) -> Result<Arc<Image>> {
        let icon_builder = icon_builder.into();

        match icon_builder {
            IconBuilderType::Image(image) => {
                if let Some(path) = image.file_name() {
                    self.internal_icons
                        .write()
                        .unwrap()
                        .insert(path.clone(), Arc::downgrade(&image));
                }

                Ok(image)
            }
            IconBuilderType::Path(mut path) => {
                let mut internal_icons = self.internal_icons.write().unwrap();

                match internal_icons.get_mut(&path) {
                    Some(weak_image) => match weak_image.upgrade() {
                        Some(image) => Ok(image),
                        None => {
                            if !path.has_prefix() {
                                path.set_prefix(&self.resource_base_path().full_path());
                            }

                            let image = Image::from_file(path)?
                                .max_mip_map_levels()
                                .attach_sampler(Sampler::pretty_sampler().build(self.device())?)
                                .build(self.device(), self.queue())?;

                            *weak_image = Arc::downgrade(&image);

                            Ok(image)
                        }
                    },
                    None => {
                        if !path.has_prefix() {
                            path.set_prefix(&self.resource_base_path().full_path());
                        }

                        let image = Image::from_file(path.clone())?
                            .max_mip_map_levels()
                            .attach_sampler(Sampler::pretty_sampler().build(self.device())?)
                            .build(self.device(), self.queue())?;

                        internal_icons.insert(path, Arc::downgrade(&image));

                        Ok(image)
                    }
                }
            }
        }
    }

    fn text_desc_layout(&self) -> &Arc<DescriptorSetLayout> {
        &match &self.text_objects {
            TargetMode::Mono(l) => l,
            TargetMode::Stereo(l, _) => l,
        }
        ._descriptor_layout
    }

    pub(crate) fn image_descriptor(&self, mut path: AssetPath) -> Result<Arc<DescriptorSet>> {
        if !path.has_prefix() {
            path.set_prefix(&self.resource_base_path.full_path());
        }

        if self.internal_textures.read().unwrap().contains_key(&path) {
            Ok(self.internal_textures.read().unwrap()[&path]
                ._descriptor_set
                .clone())
        } else {
            let texture = Image::from_file(path.clone())?
                .format(VK_FORMAT_R8G8B8A8_UNORM)
                .attach_sampler(Sampler::nearest_sampler().build(&self.device)?)
                .build(&self.device, &self.queue)?;

            let desc_pool = DescriptorPool::builder()
                .set_layout(self.text_desc_layout().clone())
                .build(self.device.clone())?;

            let descriptor_set = DescriptorPool::prepare_set(&desc_pool).allocate()?;

            descriptor_set.update(&[DescriptorWrite::combined_samplers(0, &[&texture])])?;

            let displayable_texture = DisplayableTexture {
                _descriptor_pool: desc_pool,
                _descriptor_set: descriptor_set,

                _texture: texture,
            };

            self.internal_textures
                .write()
                .unwrap()
                .insert(path.clone(), displayable_texture);

            Ok(self.internal_textures.read().unwrap()[&path]
                ._descriptor_set
                .clone())
        }
    }

    pub(crate) fn color_descriptor(&self, color: Color) -> Result<Arc<DescriptorSet>> {
        if self.internal_colors.read().unwrap().contains_key(&color) {
            Ok(self.internal_colors.read().unwrap()[&color]
                ._descriptor_set
                .clone())
        } else {
            let desc_pool = DescriptorPool::builder()
                .set_layout(self.text_color_layout.clone())
                .build(self.device.clone())?;

            let desc_set = DescriptorPool::prepare_set(&desc_pool).allocate()?;

            let color_array: [f32; 3] = color.into();
            let color_buffer_array: [ColorBuffer; 3] = color_array
                .into_iter()
                .map(|f| ColorBuffer { color: f })
                .collect::<Vec<ColorBuffer>>()
                .try_into()
                .unwrap_or_else(|_: Vec<ColorBuffer>| {
                    unreachable!("create array from vec from an array")
                });

            let buffer = Buffer::builder()
                .set_usage(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT)
                .set_memory_usage(MemoryUsage::CpuOnly)
                .set_data(&color_buffer_array)
                .build(self.device.clone())?;

            desc_set.update(&[DescriptorWrite::uniform_buffers(0, &[&buffer])])?;

            let textable_color = TextableColor {
                _descriptor_pool: desc_pool,
                _descriptor_set: desc_set,

                _buffer: buffer,
            };

            self.internal_colors
                .write()
                .unwrap()
                .insert(color, textable_color);

            Ok(self.internal_colors.read().unwrap()[&color]
                ._descriptor_set
                .clone())
        }
    }

    pub(crate) fn ortho(&self) -> cgmath::Matrix4<f32> {
        *self.ortho.read().unwrap()
    }

    pub(crate) fn resource_base_path(&self) -> &AssetPath {
        &self.resource_base_path
    }

    pub fn menu_button(&self) -> &AssetPath {
        &self.menu_button
    }

    pub fn menu_button_selected(&self) -> &AssetPath {
        &self.menu_button_selected
    }

    #[cfg(feature = "audio")]
    pub fn click_sound(&self) -> &AssetPath {
        &self.click_sound
    }

    #[cfg(feature = "audio")]
    pub fn hover_sound(&self) -> &AssetPath {
        &self.hover_sound
    }

    pub fn set_on_selected_event<F>(&self, f: F) -> Result<()>
    where
        F: Fn() -> Result<()> + Send + Sync + 'static,
    {
        *self.on_selected.write().unwrap() = Some(Box::new(f));

        Ok(())
    }

    // ---------------------------------------------------------------------
    // -------------------------  event handling  --------------------------
    // ---------------------------------------------------------------------

    pub fn set_mouse_pos(&self, x: u32, y: u32) -> Result<()> {
        self.mouse_x.store(x, SeqCst);
        self.mouse_y.store(y, SeqCst);

        let mut hovered = None;

        {
            let layers = self.layers.lock().unwrap();

            for (_, elements) in layers.iter() {
                for hoverable in elements.iter_hoverables() {
                    if hoverable.is_hovered(x as i32, y as i32) {
                        hovered = Some(hoverable.clone());
                        break;
                    }
                }
            }
        }

        let mut current_hoverable = self.current_hoverable.write().unwrap();

        match hovered {
            Some(hovered) => {
                if let Some(current) = &*current_hoverable {
                    if Arc::ptr_eq(current, &hovered) {
                        return Ok(());
                    }

                    current.set_hovered(false)?;
                }

                hovered.set_hovered(true)?;

                *current_hoverable = Some(hovered);
            }
            None => {
                if current_hoverable.is_some() {
                    // unwrap is safe, just tested for `is_some`
                    if !current_hoverable
                        .as_ref()
                        .unwrap()
                        .is_hovered(x as i32, y as i32)
                    {
                        current_hoverable.as_ref().unwrap().set_hovered(false)?;
                        *current_hoverable = None;
                    }
                }
            }
        }

        Ok(())
    }

    pub fn mouse_position(&self) -> (u32, u32) {
        (self.mouse_x.load(SeqCst), self.mouse_y.load(SeqCst))
    }

    fn find_clickable(&self) -> Result<Option<Arc<Clickable>>> {
        let layers = self.layers.lock().unwrap();

        for (_, elements) in layers.iter() {
            for clickable in elements.iter_clickables() {
                if clickable.is_pressed(
                    self.mouse_x.load(SeqCst) as i32,
                    self.mouse_y.load(SeqCst) as i32,
                ) {
                    *self.current_clickable.write().unwrap() = Some(clickable.clone());
                    return Ok(Some(clickable.clone()));
                }
            }
        }

        Ok(None)
    }

    pub fn mouse_down(&self, mouse_button: MouseButton) -> Result<bool> {
        if mouse_button == MouseButton::Left {
            if let Some(tmp_clickable) = self.find_clickable()? {
                tmp_clickable.set_clicked(true)?;
                return Ok(true);
            }
        }

        Ok(false)
    }

    pub fn mouse_up(&self, mouse_button: MouseButton) -> Result<bool> {
        if mouse_button == MouseButton::Left {
            let mut clickable = self.current_clickable.write().unwrap();
            if clickable.is_some() {
                clickable.as_ref().unwrap().set_clicked(false)?;

                if clickable.as_ref().unwrap().is_pressed(
                    self.mouse_x.load(SeqCst) as i32,
                    self.mouse_y.load(SeqCst) as i32,
                ) {
                    if let Some(hoverable) = self.current_hoverable.read().unwrap().as_ref() {
                        hoverable.set_hovered(true)?;
                    }
                }

                *clickable = None;

                return Ok(true);
            }
        }

        Ok(false)
    }

    fn current_selectable(&self) -> Result<Option<Arc<Selectable>>> {
        match self.current_selectable.read().unwrap().as_ref() {
            Some(selectable) => Ok(Some(selectable.clone())),
            None => Ok(None),
        }
    }

    pub fn accept_selection(&self) -> Result<bool> {
        if let Some(current_selectable) = self.current_selectable()? {
            current_selectable.click_event()?;
            return Ok(true);
        }

        Ok(false)
    }

    pub fn accept_custom_selection(&self, button: ControllerButton) -> Result<bool> {
        if let Some(current_selectable) = self.current_selectable()? {
            if current_selectable.custom_click_event(button)? {
                return Ok(true);
            }
        }

        Ok(false)
    }

    pub fn decline_topgui(&self) -> Result<bool> {
        // workaround for unwanted borrowing behaviour inside decline function
        let opt_topgui = { self.top_ui.read().unwrap().as_ref().cloned() };

        if let Some(topgui) = opt_topgui {
            topgui.decline()?;
            return Ok(true);
        }

        Ok(false)
    }

    pub fn next_tab_topgui(&self, second_level: bool) -> Result<bool> {
        // workaround for unwanted borrowing behaviour inside decline function
        let opt_topgui = { self.top_ui.read().unwrap().as_ref().cloned() };

        if let Some(topgui) = opt_topgui {
            topgui.next_tab(second_level)?;
            return Ok(true);
        }

        Ok(false)
    }

    pub fn previous_tab_topgui(&self, second_level: bool) -> Result<bool> {
        // workaround for unwanted borrowing behaviour inside decline function
        let opt_topgui = { self.top_ui.read().unwrap().as_ref().cloned() };

        if let Some(topgui) = opt_topgui {
            topgui.previous_tab(second_level)?;
            return Ok(true);
        }

        Ok(false)
    }

    pub fn writeable(&self) -> Result<Option<Arc<Writeable>>> {
        Ok(self.current_writeable.read().unwrap().as_ref().cloned())
    }

    pub fn remove_char(&self) -> Result<bool> {
        match self.current_writeable.read().unwrap().as_ref() {
            Some(current_writable) => {
                current_writable.remove_last()?;
                Ok(true)
            }
            None => Ok(false),
        }
    }

    pub fn check_navigatable(&self) -> Result<bool> {
        Ok(self.current_selectable.read().unwrap().is_some())
    }

    pub fn update_selection(&self, direction: GuiDirection) -> Result<bool> {
        let mut last_direction = self.last_direction.lock().unwrap();

        if direction != *last_direction {
            *last_direction = direction;

            match self.current_selectable.write().unwrap().as_mut() {
                Some(current_selectable) => match direction {
                    GuiDirection::Left => {
                        if let Some(neighbour) = current_selectable.west_neighbour() {
                            current_selectable.set_selected(false)?;
                            *current_selectable = neighbour;
                            current_selectable.set_selected(true)?;
                        };

                        Ok(true)
                    }
                    GuiDirection::Right => {
                        if let Some(neighbour) = current_selectable.east_neighbour() {
                            current_selectable.set_selected(false)?;
                            *current_selectable = neighbour;
                            current_selectable.set_selected(true)?;
                        };

                        Ok(true)
                    }
                    GuiDirection::Up => {
                        if let Some(neighbour) = current_selectable.north_neighbour() {
                            current_selectable.set_selected(false)?;
                            *current_selectable = neighbour;
                            current_selectable.set_selected(true)?;
                        };

                        Ok(true)
                    }
                    GuiDirection::Down => {
                        if let Some(neighbour) = current_selectable.south_neighbour() {
                            current_selectable.set_selected(false)?;
                            *current_selectable = neighbour;
                            current_selectable.set_selected(true)?;
                        };

                        Ok(true)
                    }
                    GuiDirection::None => Ok(false),
                },
                None => Ok(false),
            }
        } else {
            Ok(false)
        }
    }

    pub(crate) fn enqueue_text_update(&self, function: Box<dyn Fn() -> Result<()> + Send + Sync>) {
        self.text_change_queue.write().unwrap().push(function);
        self.needs_update.store(true, SeqCst);
    }

    pub fn set_top_gui(&self, top_gui: Option<Arc<dyn TopGui>>) {
        *self.top_ui.write().unwrap() = top_gui;
    }

    pub fn set_tooltip(&self, tooltip: Option<Arc<dyn TopGui>>) {
        *self.tooltip_ui.write().unwrap() = tooltip;
    }

    fn render(
        &self,
        buffer_recorder: &mut CommandBufferRecorder<'_>,
        command_buffer_state: &CommandBufferState,
        render_target: &RenderTarget,
        single_color_objects: &GuiSeparator,
        rectangle_objects: &GuiSeparator,
        text_objects: &GuiSeparator,
        text_to_screen: &GuiSeparator,
        text_to_screen_desc: &Arc<DescriptorSet>,
        index: usize,
    ) -> Result<()> {
        let viewport = [VkViewport {
            x: 0.0,
            y: 0.0,
            width: self.width.load(SeqCst) as f32,
            height: self.height.load(SeqCst) as f32,
            minDepth: 0.0,
            maxDepth: 1.0,
        }];

        let scissor = [VkRect2D {
            offset: VkOffset2D { x: 0, y: 0 },
            extent: VkExtent2D {
                width: self.width.load(SeqCst),
                height: self.height.load(SeqCst),
            },
        }];

        let layers = self.layers.lock().unwrap();

        for (_, elements) in layers.iter() {
            render_target.begin(buffer_recorder, VK_SUBPASS_CONTENTS_INLINE, index);

            if !elements.is_colorables_empty() {
                buffer_recorder.bind_pipeline(&single_color_objects.pipeline)?;

                buffer_recorder.set_scissor(&scissor);
                buffer_recorder.set_viewport(&viewport);

                // ---------- render colorables ----------
                for colorable in elements.iter_colorables() {
                    buffer_recorder.bind_vertex_buffer(colorable.buffer());

                    buffer_recorder.bind_descriptor_sets_minimal(&[&colorable.descriptor_set()]);

                    buffer_recorder.draw_complete_single_instance(6);
                }
            }

            if !elements.is_displayables_empty() || !elements.is_iconizables_empty() {
                buffer_recorder.bind_pipeline(&rectangle_objects.pipeline)?;

                buffer_recorder.set_scissor(&scissor);
                buffer_recorder.set_viewport(&viewport);

                // ---------- render displayables ----------
                for displayable in elements.iter_displayables() {
                    buffer_recorder.bind_vertex_buffer(displayable.buffer());

                    buffer_recorder.bind_descriptor_sets_minimal(&[&displayable.descriptor_set()]);

                    buffer_recorder.draw_complete_single_instance(6);
                }

                // ---------- render iconizables ----------
                for iconizable in elements.iter_iconizables() {
                    buffer_recorder.bind_vertex_buffer(iconizable.buffer());

                    buffer_recorder.bind_descriptor_sets_minimal(&[iconizable.descriptor_set()]);

                    buffer_recorder.draw_complete_single_instance(6);
                }
            }

            // render text, render to offscreen (multisampled) and merge resolved target with actual image
            if !elements.is_textables_empty() {
                render_target.next_subpass(buffer_recorder, VK_SUBPASS_CONTENTS_INLINE);

                buffer_recorder.bind_pipeline(&text_objects.pipeline)?;

                buffer_recorder.set_scissor(&scissor);
                buffer_recorder.set_viewport(&viewport);

                let mut text_buffers = command_buffer_state.text_buffers.write().unwrap();
                text_buffers.clear();

                // ---------- render textables ----------
                for textable in elements.iter_textables() {
                    if let Some(text_buffer) = textable.buffer() {
                        buffer_recorder.bind_vertex_buffer(&text_buffer);

                        text_buffers.push(text_buffer);

                        buffer_recorder.bind_descriptor_sets_minimal(&[
                            &self.bitmap_desc_set,
                            &textable.descriptor_set(),
                        ]);

                        buffer_recorder.draw_complete_single_instance(textable.vertex_count());
                    }
                }

                // after text is written und resolved, we read image as input and draw it over the current image
                render_target.next_subpass(buffer_recorder, VK_SUBPASS_CONTENTS_INLINE);

                buffer_recorder.bind_pipeline(&text_to_screen.pipeline)?;

                buffer_recorder.set_scissor(&scissor);
                buffer_recorder.set_viewport(&viewport);

                buffer_recorder.bind_descriptor_sets_minimal(&[text_to_screen_desc]);
                buffer_recorder.bind_vertex_buffer(&self.text_to_screen.buffer);
                buffer_recorder
                    .draw_complete_single_instance(self.text_to_screen.buffer.size() as u32);
            } else {
                // dummy advance sub passes
                // since ending render pass and not being in the last sub pass seems to be an error
                render_target.next_subpass(buffer_recorder, VK_SUBPASS_CONTENTS_INLINE);
                render_target.next_subpass(buffer_recorder, VK_SUBPASS_CONTENTS_INLINE);
            }

            render_target.end(buffer_recorder);
        }

        Ok(())
    }
}

macro_rules! add_element {
    ($layers: expr, $layer_id: ident, $element: ident) => {
        let mut layers = $layers.lock().unwrap();

        paste! {
            match layers.iter_mut().find(|(id, _)| *id == $layer_id) {
                Some((_, layer)) => {
                    layer.[<add_ $element>]($element);
                }
                None => {
                    let mut elements = Elements::default();
                    elements.[<add_ $element>]($element);

                    layers.push(($layer_id, elements));
                    layers.sort_by(|(left_id, _), (right_id, _)| left_id.cmp(right_id));
                }
            }
        }
    };
}

macro_rules! remove_element {
    ($layers: expr, $layer_id: ident, $element: ident) => {
        let mut layers = $layers.lock().unwrap();

        paste! {
            match layers.iter_mut().find(|(id, _)| *id == $layer_id) {
                Some((_, layer)) => {
                    layer.[<delete_ $element>]($element);
                }
                None => {
                    panic!("layer not present from which {} is to remove", stringify!($element));
                }
            }
        }
    };
    ($layers: expr, $layer_id: ident, $element: ident, $update: expr) => {
        let mut layers = $layers.lock().unwrap();

        paste! {
            match layers.iter_mut().find(|(id, _)| *id == $layer_id) {
                Some((_, layer)) => {
                    if layer.[<delete_ $element>]($element) {
                        $update.store(true, SeqCst);
                    }
                }
                None => {
                    panic!("layer not present from which {} is to remove", stringify!($element));
                }
            }
        }
    };
}

// object handling
impl GuiHandler {
    // framable
    pub(crate) fn add_framable(&self, layer: i32, framable: Arc<Framable>) -> Result<()> {
        add_element!(self.layers, layer, framable);

        Ok(())
    }

    pub(crate) fn delete_framable(&self, layer: i32, framable: &Arc<Framable>) -> Result<()> {
        remove_element!(self.layers, layer, framable);

        Ok(())
    }

    // hoverable
    pub(crate) fn add_hoverable(&self, layer: i32, hoverable: Arc<Hoverable>) -> Result<()> {
        add_element!(self.layers, layer, hoverable);

        Ok(())
    }

    pub(crate) fn delete_hoverable(&self, layer: i32, hoverable: &Arc<Hoverable>) -> Result<()> {
        let mut current_hoverable = self.current_hoverable.write().unwrap();

        if current_hoverable.is_some() {
            // unwrap is safe, just tested for `is_some`
            if Arc::ptr_eq(hoverable, current_hoverable.as_ref().unwrap()) {
                *current_hoverable = None;
            }
        }

        remove_element!(self.layers, layer, hoverable);

        Ok(())
    }

    // selectable
    pub(crate) fn add_selectable(&self, layer: i32, selectable: Arc<Selectable>) -> Result<()> {
        add_element!(self.layers, layer, selectable);

        Ok(())
    }

    pub(crate) fn delete_selectable(&self, layer: i32, selectable: &Arc<Selectable>) -> Result<()> {
        let mut current_selectable = self.current_selectable.write().unwrap();
        if current_selectable.is_some() {
            // unwrap is safe, just tested for `is_some`
            if Arc::ptr_eq(selectable, current_selectable.as_ref().unwrap()) {
                *current_selectable = None;
            }
        }

        remove_element!(self.layers, layer, selectable);

        Ok(())
    }

    // displayable
    pub(crate) fn add_displayable(&self, layer: i32, displayable: Arc<Displayable>) -> Result<()> {
        add_element!(self.layers, layer, displayable);

        self.needs_update.store(true, SeqCst);

        Ok(())
    }

    pub(crate) fn delete_displayable(
        &self,
        layer: i32,
        displayable: &Arc<Displayable>,
    ) -> Result<()> {
        remove_element!(self.layers, layer, displayable, self.needs_update);

        Ok(())
    }

    // clickable
    pub(crate) fn add_clickable(&self, layer: i32, clickable: Arc<Clickable>) -> Result<()> {
        add_element!(self.layers, layer, clickable);

        Ok(())
    }

    pub(crate) fn delete_clickable(&self, layer: i32, clickable: &Arc<Clickable>) -> Result<()> {
        let mut current_clickable = self.current_clickable.write().unwrap();
        if current_clickable.is_some() {
            // unwrap is safe, just tested for `is_some`
            if ptr::eq(&clickable, &current_clickable.as_ref().unwrap()) {
                *current_clickable = None;
            }
        }

        remove_element!(self.layers, layer, clickable);

        Ok(())
    }

    // textable
    pub(crate) fn add_textable(&self, layer: i32, textable: Arc<Textable>) -> Result<()> {
        add_element!(self.layers, layer, textable);

        self.needs_update.store(true, SeqCst);

        Ok(())
    }

    pub(crate) fn delete_textable(&self, layer: i32, textable: &Arc<Textable>) -> Result<()> {
        remove_element!(self.layers, layer, textable, self.needs_update);

        Ok(())
    }

    // writable
    pub(crate) fn add_writeable(&self, layer: i32, writeable: Arc<Writeable>) -> Result<()> {
        add_element!(self.layers, layer, writeable);

        Ok(())
    }

    pub(crate) fn set_active_writeable(&self, writeable: Arc<Writeable>) -> Result<bool> {
        let mut current_writeable = self.current_writeable.write().unwrap();

        if let Some(current) = current_writeable.as_ref() {
            // change nothing if both are the same
            if Arc::ptr_eq(current, &writeable) {
                return Ok(false);
            }

            current.inactivation_event()?;
        }

        *current_writeable = Some(writeable);

        Ok(true)
    }

    pub(crate) fn delete_writeable(&self, layer: i32, writeable: &Arc<Writeable>) -> Result<()> {
        {
            let mut current_writeable = self.current_writeable.write().unwrap();

            if let Some(w) = &*current_writeable {
                if Arc::ptr_eq(w, writeable) {
                    w.inactivation_event()?;
                    *current_writeable = None;
                }
            }
        }

        remove_element!(self.layers, layer, writeable);

        Ok(())
    }

    // iconizable
    pub(crate) fn add_iconizable(&self, layer: i32, iconizable: Arc<Iconizable>) -> Result<()> {
        add_element!(self.layers, layer, iconizable);

        self.needs_update.store(true, SeqCst);

        Ok(())
    }

    pub(crate) fn delete_iconizable(&self, layer: i32, iconizable: &Arc<Iconizable>) -> Result<()> {
        remove_element!(self.layers, layer, iconizable, self.needs_update);

        Ok(())
    }

    pub(crate) fn set_selectable(&self, selectable: Option<Arc<Selectable>>) -> Result<()> {
        let mut current_selectable = self.current_selectable.write().unwrap();

        if let Some(selectable) = current_selectable.as_ref() {
            selectable.set_selected(false)?;
        }

        if let Some(selectable) = selectable.as_ref() {
            selectable.set_selected(true)?;

            if let Some(on_selected) = self.on_selected.read().unwrap().as_ref() {
                on_selected()?;
            }
        }

        *current_selectable = selectable;

        Ok(())
    }

    pub(crate) fn add_colorable(&self, layer: i32, colorable: Arc<Colorable>) -> Result<()> {
        add_element!(self.layers, layer, colorable);

        self.needs_update.store(true, SeqCst);

        Ok(())
    }

    pub(crate) fn delete_colorable(&self, layer: i32, colorable: &Arc<Colorable>) -> Result<()> {
        remove_element!(self.layers, layer, colorable, self.needs_update);

        Ok(())
    }
}

// private
impl GuiHandler {
    fn create_render_targets(
        device: &Arc<Device>,
        target_images: &TargetMode<Vec<Arc<Image>>>,
        queue: &Arc<Mutex<Queue>>,
        sample_count: VkSampleCountFlags,
    ) -> Result<TargetMode<RwLock<RenderTarget>>> {
        Ok(match target_images {
            TargetMode::Mono(images) => TargetMode::Mono(RwLock::new(Self::create_render_target(
                device,
                images,
                None,
                queue,
                sample_count,
            )?)),
            TargetMode::Stereo(left_images, right_images) => TargetMode::Stereo(
                RwLock::new(Self::create_render_target(
                    device,
                    left_images,
                    None,
                    queue,
                    sample_count,
                )?),
                RwLock::new(Self::create_render_target(
                    device,
                    right_images,
                    None,
                    queue,
                    sample_count,
                )?),
            ),
        })
    }

    fn create_render_target(
        device: &Arc<Device>,
        target_images: &Vec<Arc<Image>>,
        old_render_target: Option<&RenderTarget>,
        queue: &Arc<Mutex<Queue>>,
        sample_count: VkSampleCountFlags,
    ) -> Result<RenderTarget> {
        let mut builder = RenderTarget::builder()
            .add_sub_pass(
                SubPass::builder(target_images[0].width(), target_images[0].height())
                    .set_prepared_targets(target_images, 0, [0.0, 0.0, 0.0, 0.0], false)
                    .build(device)?,
            )
            .add_sub_pass(
                SubPass::builder(target_images[0].width(), target_images[0].height())
                    .add_target_info(CustomTarget {
                        usage: VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT.into(),
                        format: VK_FORMAT_R8G8B8A8_UNORM,
                        clear_on_load: true,
                        store_on_save: true,
                        attach_sampler: false,
                        use_as_input: false,
                        clear_value: ClearValue::Color([0.0, 0.0, 0.0, 0.0]),
                    })
                    .set_sample_count(sample_count)
                    .add_resolve_targets(CustomTarget::resolve(
                        VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
                        VK_FORMAT_R8G8B8A8_UNORM,
                        None,
                        false,
                        true,
                    ))
                    .use_queue(queue.clone())
                    .build(device)?,
            )
            .add_sub_pass(
                SubPass::builder(target_images[0].width(), target_images[0].height())
                    .set_input_attachment_info(InputAttachmentInfo {
                        sub_pass_index: 1,
                        input_indices: vec![1],
                    })
                    .set_prepared_targets(target_images, 0, [0.0, 0.0, 0.0, 0.0], false)
                    .build(device)?,
            );

        if let Some(render_target) = old_render_target {
            builder = builder.preserve_old_render_pass(render_target);
        }

        builder.build(device)
    }

    fn create_command_buffers(image_count: usize) -> Result<Vec<CommandBufferState>> {
        let mut command_buffers = Vec::with_capacity(image_count);

        for _ in 0..image_count {
            command_buffers.push(CommandBufferState {
                text_buffers: RwLock::new(Vec::new()),
            });
        }

        Ok(command_buffers)
    }

    fn init_bitmap_font(
        device: &Arc<Device>,
        queue: &Arc<Mutex<Queue>>,
        descriptor_layout: Arc<DescriptorSetLayout>,
        font: Font<'_>,
    ) -> Result<(Arc<Image>, Arc<DescriptorPool>, Arc<DescriptorSet>)> {
        let texture = match font {
            Font::Path(path) => Image::from_file(path)?,
            Font::Bytes(bytes) => Image::from_slice(bytes)?,
        }
        .format(VK_FORMAT_R8G8B8A8_UNORM)
        .max_mip_map_levels()
        .attach_sampler(Sampler::pretty_sampler().build(device)?)
        .build(device, queue)?;

        let descriptor_pool = DescriptorPool::builder()
            .set_layout(descriptor_layout)
            .build(device.clone())?;

        let descriptor_set = DescriptorPool::prepare_set(&descriptor_pool).allocate()?;

        descriptor_set.update(&[DescriptorWrite::combined_samplers(0, &[&texture])])?;

        Ok((texture, descriptor_pool, descriptor_set))
    }

    fn init_text_objects(
        device: &Arc<Device>,
        render_targets: &TargetMode<RwLock<RenderTarget>>,
        sample_count: VkSampleCountFlags,
    ) -> Result<(TargetMode<GuiSeparator>, Arc<DescriptorSetLayout>)> {
        // --- layout creation ---
        let descriptor_layout = DescriptorSetLayout::builder()
            .add_layout_binding(
                0,
                VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
                VK_SHADER_STAGE_FRAGMENT_BIT,
                0,
            )
            .build(device.clone())?;

        let color_layout = DescriptorSetLayout::builder()
            .add_layout_binding(
                0,
                VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
                VK_SHADER_STAGE_FRAGMENT_BIT,
                0,
            )
            .build(device.clone())?;

        let pipeline_layout = PipelineLayout::builder()
            .add_descriptor_set_layout(&descriptor_layout)
            .add_descriptor_set_layout(&color_layout)
            .build(device.clone())?;

        // --- pipeline creation ---
        let vertex_shader_text = include_bytes!("guishader/text.vert.spv");
        let fragment_shader_text = include_bytes!("guishader/text.frag.spv");

        let vertex_shader = ShaderModule::from_slice(device.clone(), vertex_shader_text)?;
        let fragment_shader = ShaderModule::from_slice(device.clone(), fragment_shader_text)?;

        Ok((
            render_targets.execute(|render_target| {
                Ok(GuiSeparator {
                    _descriptor_layout: descriptor_layout.clone(),

                    pipeline: GuiHandler::init_gui_pipeline::<TexturedVertex>(
                        device,
                        render_target.read().unwrap().render_pass(),
                        &pipeline_layout,
                        vertex_shader.clone(),
                        fragment_shader.clone(),
                        sample_count,
                        1,
                    )?,
                })
            })?,
            color_layout,
        ))
    }

    fn init_rectangle_objects(
        device: &Arc<Device>,
        render_targets: &TargetMode<RwLock<RenderTarget>>,
    ) -> Result<TargetMode<GuiSeparator>> {
        let descriptor_layout = DescriptorSetLayout::builder()
            .add_layout_binding(
                0,
                VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
                VK_SHADER_STAGE_FRAGMENT_BIT,
                0,
            )
            .build(device.clone())?;

        let pipeline_layout = PipelineLayout::builder()
            .add_descriptor_set_layout(&descriptor_layout)
            .build(device.clone())?;

        // pipeline creation
        let vertex_shader =
            ShaderModule::from_slice(device.clone(), include_bytes!("guishader/rect.vert.spv"))?;
        let fragment_shader =
            ShaderModule::from_slice(device.clone(), include_bytes!("guishader/rect.frag.spv"))?;

        render_targets.execute(|render_target| {
            Ok(GuiSeparator {
                _descriptor_layout: descriptor_layout.clone(),

                pipeline: GuiHandler::init_gui_pipeline::<TexturedVertex>(
                    device,
                    render_target.read().unwrap().render_pass(),
                    &pipeline_layout,
                    vertex_shader.clone(),
                    fragment_shader.clone(),
                    VK_SAMPLE_COUNT_1_BIT,
                    0,
                )?,
            })
        })
    }

    fn init_text_screen_objects(
        device: &Arc<Device>,
        render_targets: &TargetMode<RwLock<RenderTarget>>,
    ) -> Result<TargetMode<GuiSeparator>> {
        let descriptor_layout = DescriptorSetLayout::builder()
            .add_layout_binding(
                0,
                VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
                VK_SHADER_STAGE_FRAGMENT_BIT,
                0,
            )
            .build(device.clone())?;

        let pipeline_layout = PipelineLayout::builder()
            .add_descriptor_set_layout(&descriptor_layout)
            .build(device.clone())?;

        // pipeline creation
        let vertex_shader =
            ShaderModule::from_slice(device.clone(), include_bytes!("guishader/rect.vert.spv"))?;
        let fragment_shader = ShaderModule::from_slice(
            device.clone(),
            include_bytes!("guishader/input_rect.frag.spv"),
        )?;

        render_targets.execute(|render_target| {
            Ok(GuiSeparator {
                _descriptor_layout: descriptor_layout.clone(),

                pipeline: GuiHandler::init_gui_pipeline::<TexturedVertex>(
                    device,
                    render_target.read().unwrap().render_pass(),
                    &pipeline_layout,
                    vertex_shader.clone(),
                    fragment_shader.clone(),
                    VK_SAMPLE_COUNT_1_BIT,
                    2,
                )?,
            })
        })
    }

    fn init_single_color_objects(
        device: &Arc<Device>,
        render_targets: &TargetMode<RwLock<RenderTarget>>,
    ) -> Result<TargetMode<GuiSeparator>> {
        let color_layout = DescriptorSetLayout::builder()
            .add_layout_binding(
                0,
                VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
                VK_SHADER_STAGE_FRAGMENT_BIT,
                0,
            )
            .build(device.clone())?;

        let pipeline_layout = PipelineLayout::builder()
            .add_descriptor_set_layout(&color_layout)
            .build(device.clone())?;

        // pipeline creation
        let vertex_shader = ShaderModule::from_slice(
            device.clone(),
            include_bytes!("guishader/single_color.vert.spv"),
        )?;
        let fragment_shader = ShaderModule::from_slice(
            device.clone(),
            include_bytes!("guishader/single_color.frag.spv"),
        )?;

        render_targets.execute(|render_target| {
            Ok(GuiSeparator {
                _descriptor_layout: color_layout.clone(),

                pipeline: GuiHandler::init_gui_pipeline::<ColorableVertex>(
                    device,
                    render_target.read().unwrap().render_pass(),
                    &pipeline_layout,
                    vertex_shader.clone(),
                    fragment_shader.clone(),
                    VK_SAMPLE_COUNT_1_BIT,
                    0,
                )?,
            })
        })
    }

    fn init_gui_pipeline<T: VertexInputDescription>(
        device: &Arc<Device>,
        render_pass: &Arc<RenderPass>,
        pipeline_layout: &Arc<PipelineLayout>,
        vertex_shader: Arc<ShaderModule<shader_type::Vertex>>,
        fragment_shader: Arc<ShaderModule<shader_type::Fragment>>,
        sample_count: VkSampleCountFlags,
        sub_pass: u32,
    ) -> Result<Arc<Pipeline>> {
        Pipeline::new_graphics()
            .set_vertex_shader::<T>(vertex_shader)
            .set_fragment_shader(fragment_shader)
            .input_assembly(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, false)
            .default_multisample(sample_count)
            .default_color_blend(vec![VkPipelineColorBlendAttachmentState::default()])
            .default_rasterization(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE)
            .build(device.clone(), pipeline_layout, render_pass, sub_pass)
    }
}

impl GuiHandler {
    pub fn process(
        &self,
        buffer_recorder: &mut CommandBufferRecorder<'_>,
        indices: &TargetMode<usize>,
    ) -> Result<()> {
        if self.needs_update.load(SeqCst) {
            let mut text_changes = self.text_change_queue.write().unwrap();

            if !text_changes.is_empty() {
                for text_change in text_changes.iter() {
                    (text_change)()?;
                }

                text_changes.clear();
            }

            self.needs_update.store(false, SeqCst);
        }

        self.command_buffers
            .chain(&self.render_targets)
            .chain(&self.text_objects)
            .unfold()
            .chain(&self.rectangle_objects)
            .unfold()
            .chain(&self.single_color_objects)
            .unfold()
            .chain(indices)
            .unfold()
            .chain(&self.text_to_screen.pipeline)
            .unfold()
            .chain(&self.text_to_screen.descriptor)
            .unfold()
            .execute(
                |(
                    command_buffers,
                    render_target,
                    text_objects,
                    rectangle_objects,
                    single_color_object,
                    &&&index,
                    text_pipeline,
                    text_descriptor,
                )| {
                    self.render(
                        buffer_recorder,
                        &command_buffers[index],
                        &*render_target.read().unwrap(),
                        single_color_object,
                        rectangle_objects,
                        text_objects,
                        text_pipeline,
                        text_descriptor,
                        index,
                    )
                },
            )?;

        Ok(())
    }

    pub fn resize(&self, width: u32, height: u32) -> Result<()> {
        self.context
            .images()
            .chain(&self.render_targets)
            .execute(|(images, render_target)| {
                let mut rt_lock = render_target.write().unwrap();

                *rt_lock = Self::create_render_target(
                    &self.device,
                    &images,
                    Some(&*rt_lock),
                    &self.queue,
                    self.text_sample_count,
                )?;

                Ok(())
            })?;

        self.text_to_screen.update_on_resize(&self.render_targets)?;

        self.needs_update.store(true, SeqCst);
        *self.ortho.write().unwrap() = ortho(0.0, width as f32, 0.0, height as f32, -1.0, 1.0);

        self.width.store(width, SeqCst);
        self.height.store(height, SeqCst);

        let layers = self.layers.lock().unwrap();

        for (_, elements) in layers.iter() {
            for framable in elements.iter_framables() {
                framable.resize()?;
            }
        }

        Ok(())
    }
}