use crate::write_log;

use self::{
    rendering::Rendering,
    rfactor_data::{DataReceiver, RFactorData},
};

mod elements;
mod rendering;
mod rfactor_data;

use anyhow::Result;
use assetpath::AssetPath;
use std::{
    cell::RefCell,
    rc::Rc,
    sync::{Arc, Mutex},
};
use ui::prelude::*;
use vulkan_rs::prelude::*;

use elements::*;

use serde::{Deserialize, Serialize};

pub trait UiOverlay: DataReceiver {
    fn enable_ui(&mut self) -> Result<()>;
}

#[derive(Deserialize, Serialize)]
pub struct OverlayConfig {
    pub radar_config: RadarConfig,
    pub font_path: String,
}

impl OverlayConfig {
    pub const fn new() -> Self {
        Self {
            radar_config: RadarConfig::new(),
            font_path: String::new(),
        }
    }
}

pub struct Overlay {
    config: OverlayConfig,

    instance: Option<Arc<Instance>>,
    device: Option<Arc<Device>>,
    queue: Option<Arc<Mutex<Queue>>>,
    rendering: Option<Rendering>,
    gui_handler: Option<Arc<GuiHandler>>,

    ui_elements: Vec<Rc<RefCell<dyn UiOverlay>>>,

    rfactor_data: Option<RFactorData>,
}

impl Overlay {
    pub const fn new() -> Self {
        Self {
            config: OverlayConfig::new(),

            instance: None,
            device: None,
            queue: None,
            rendering: None,
            gui_handler: None,
            ui_elements: Vec::new(),

            rfactor_data: None,
        }
    }

    pub fn set_config(&mut self, config: OverlayConfig) {
        self.config = config;
    }

    pub fn set_instance(&mut self, instance: Arc<Instance>) {
        self.instance = Some(instance);
    }

    pub fn instance(&self) -> Arc<Instance> {
        self.instance.as_ref().unwrap().clone()
    }

    pub fn set_device(&mut self, device: Arc<Device>) {
        self.device = Some(device);
    }

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

    pub fn set_queue(&mut self, queue: Arc<Mutex<Queue>>) {
        self.queue = Some(queue);
    }

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

    pub fn swapchain(&self, swapchain: VkSwapchainKHR) -> Option<&Arc<Swapchain>> {
        let sc = self.rendering.as_ref().unwrap().swapchain();

        if sc.vk_handle() == swapchain {
            Some(sc)
        } else {
            None
        }
    }

    pub fn create_rendering(&mut self, swapchain: Arc<Swapchain>) -> Result<()> {
        write_log!("-> create rendering: start");

        self.rendering = None;

        write_log!("-> create rendering: old cleared");

        let mut rendering = Rendering::new(self.queue(), swapchain.clone())?;

        write_log!("-> create rendering: new created");

        // only font is used
        let mut create_info = GuiHandlerCreateInfo::default();
        create_info.font_path = AssetPath::from(self.config.font_path.clone());
        create_info.font_path.assume_prefix_free();

        // provide trait required by GuiHandler
        let ctx = Arc::new(ContextImpl::new(
            self.device(),
            self.queue(),
            swapchain,
            rendering.images().clone(),
        ));

        // create GuiHandler
        let gui_handler = GuiHandler::new(create_info, &(ctx as Arc<dyn ContextInterface>))?;

        // create ui elements

        // create radar
        let radar = Rc::new(RefCell::new(Radar::new(
            self.config.radar_config,
            self.device(),
            self.queue(),
            &rendering,
        )?));

        // create pedals
        let pedals = Rc::new(RefCell::new(Pedals::new(&gui_handler)?));

        // add rendering callbacks
        rendering.add_render_callback({
            let radar = radar.clone();

            move |index| radar.borrow().render(index)
        });

        rendering.add_render_callback({
            let gui_handler = gui_handler.clone();
            let device = self.device();
            let queue = self.queue();

            move |index| {
                let command_buffer =
                    CommandBuffer::new_primary().build(device.clone(), queue.clone())?;

                {
                    let mut recorder = command_buffer.begin(VkCommandBufferBeginInfo::new(
                        VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
                            | VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT,
                    ))?;

                    gui_handler.process(&mut recorder, &TargetMode::Mono(index as usize))?;
                }

                Ok(command_buffer)
            }
        });

        // add ui elements to local list
        self.ui_elements.push(radar);
        self.ui_elements.push(pedals);

        write_log!("-> create rendering: end");

        self.rendering = Some(rendering);
        self.gui_handler = Some(gui_handler);

        Ok(())
    }

    pub fn render(&mut self) -> Result<()> {
        if self.rfactor_data.is_none() {
            self.rfactor_data = RFactorData::new().ok();

            if let Some(data) = &mut self.rfactor_data {
                write_log!("created RFactorData");

                for receiver in self.ui_elements.iter() {
                    receiver.borrow_mut().enable_ui()?;
                    data.add_receiver(receiver.clone());
                }
            }
        }

        // check twice for rfactor data, because of borrowing rules
        if let Some(rfactor) = &mut self.rfactor_data {
            rfactor.update()?;
        }

        self.rendering.as_ref().unwrap().render()
    }
}

struct ContextImpl {
    device: Arc<Device>,
    queue: Arc<Mutex<Queue>>,
    swapchain: Arc<Swapchain>,
    images: Vec<Arc<Image>>,
}

impl ContextImpl {
    fn new(
        device: Arc<Device>,
        queue: Arc<Mutex<Queue>>,
        swapchain: Arc<Swapchain>,
        images: Vec<Arc<Image>>,
    ) -> Self {
        Self {
            device,
            queue,
            swapchain,
            images,
        }
    }
}

impl ContextInterface for ContextImpl {
    fn device(&self) -> &Arc<Device> {
        &self.device
    }

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

    fn format(&self) -> VkFormat {
        self.swapchain.format()
    }

    fn image_layout(&self) -> VkImageLayout {
        VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
    }

    fn image_count(&self) -> usize {
        self.images.len()
    }

    fn images(&self) -> TargetMode<Vec<Arc<Image>>> {
        TargetMode::Mono(self.images.clone())
    }

    fn width(&self) -> u32 {
        self.swapchain.width()
    }

    fn height(&self) -> u32 {
        self.swapchain.height()
    }
}