mod pipeline;

use std::sync::{Arc, Mutex};

use anyhow::Result;
use cgmath::{ortho, vec4, Matrix4};
use rfactor_sm_reader::{rF2VehicleTelemetry, VehicleScoringInfoV01};
use ringbuf::{HeapRb, Rb};
use ui::prelude::*;
use vulkan_rs::prelude::*;

use crate::overlay::{
    rfactor_data::{DataReceiver, GamePhase},
    UiOverlay,
};
use crate::write_log;

use self::pipeline::HistoryPipeline;

use super::PositionOnlyVertex;

pub struct Pedals {
    gui: Arc<GuiBuilder>,

    brake: Arc<ProgressBar>,
    throttle: Arc<ProgressBar>,
    _history: Arc<Icon>,

    enable: bool,

    throttle_samples: HeapRb<f32>,
    brake_samples: HeapRb<f32>,

    ortho: Matrix4<f32>,

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

    render_target: RenderTarget,
    pipeline: HistoryPipeline,

    brake_descriptor: Arc<DescriptorSet>,
    brake_vertex_buffer: Arc<Buffer<PositionOnlyVertex>>,
    throttle_descriptor: Arc<DescriptorSet>,
    throttle_vertex_buffer: Arc<Buffer<PositionOnlyVertex>>,
}

impl Pedals {
    pub fn new(
        gui_handler: &Arc<GuiHandler>,
        device: Arc<Device>,
        queue: Arc<Mutex<Queue>>,
    ) -> Result<Self> {
        const DESC: &str = include_str!("pedals.xml");

        let gui = GuiBuilder::from_str(gui_handler, DESC)?;

        let brake = gui.element("brake")?;
        let throttle = gui.element("throttle")?;
        let history: Arc<Icon> = gui.element("history")?;

        let (icon_width, icon_height) = history.extent();
        let history_image = Image::empty(
            icon_width as u32,
            icon_height as u32,
            VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
            VK_SAMPLE_COUNT_1_BIT,
        )
        .format(VK_FORMAT_R8G8B8A8_UNORM)
        .attach_sampler(Sampler::nearest_sampler().build(&device)?)
        .build(&device, &queue)?;

        history_image.convert_layout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)?;

        history.set_icon(&history_image)?;

        let render_target = RenderTarget::builder()
            .add_sub_pass(
                SubPass::builder(history_image.width(), history_image.height())
                    .set_prepared_targets(&[history_image.clone()], 0, [0.3, 0.3, 0.3, 1.0], true)
                    .build(&device, &queue)?,
            )
            .build(&device)?;

        let pipeline = HistoryPipeline::new(
            device.clone(),
            render_target.render_pass(),
            history_image.width(),
            history_image.height(),
        )?;

        let ortho = ortho(0.0, history_image.width() as f32, -0.01, 1.01, -1.0, 1.0);

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

        let brake_color_buffer: Arc<Buffer<f32>> = Buffer::builder()
            .set_usage(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT)
            .set_memory_usage(MemoryUsage::CpuOnly)
            .set_data(&[0.9, 0.0, 0.0, 1.0])
            .build(device.clone())?;

        write_log!("allocate brake descriptor");

        let brake_descriptor = descriptor_pool.prepare_set().allocate()?;
        brake_descriptor.update(&[DescriptorWrite::uniform_buffers(0, &[&brake_color_buffer])])?;

        let throttle_color_buffer: Arc<Buffer<f32>> = Buffer::builder()
            .set_usage(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT)
            .set_memory_usage(MemoryUsage::CpuOnly)
            .set_data(&[0.0, 0.9, 0.0, 1.0])
            .build(device.clone())?;

        write_log!("allocate throttle descriptor");

        let throttle_descriptor = descriptor_pool.prepare_set().allocate()?;
        throttle_descriptor.update(&[DescriptorWrite::uniform_buffers(
            0,
            &[&throttle_color_buffer],
        )])?;

        let mut throttle_samples = HeapRb::new(icon_width as usize);
        let mut brake_samples = HeapRb::new(icon_width as usize);

        for _ in 0..icon_width {
            throttle_samples.push_overwrite(0.0);
            brake_samples.push_overwrite(0.0);
        }

        let brake_vertex_buffer = Self::create_vertex_buffer(&device, icon_width as VkDeviceSize)?;
        let throttle_vertex_buffer =
            Self::create_vertex_buffer(&device, icon_width as VkDeviceSize)?;

        let me = Self {
            gui,

            brake,
            throttle,
            _history: history,

            enable: false,

            throttle_samples,
            brake_samples,

            ortho,

            device,
            queue,

            render_target,
            pipeline,

            brake_descriptor,
            brake_vertex_buffer,
            throttle_descriptor,
            throttle_vertex_buffer,
        };

        me.update_vertex_buffers()?;

        Ok(me)
    }

    fn create_vertex_buffer(
        device: &Arc<Device>,
        size: VkDeviceSize,
    ) -> Result<Arc<Buffer<PositionOnlyVertex>>> {
        Buffer::builder()
            .set_usage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)
            .set_memory_usage(MemoryUsage::CpuOnly)
            .set_size(size)
            .build(device.clone())
    }

    fn update_vertex_buffers(&self) -> Result<()> {
        self.update_vertex_buffer(
            &self.throttle_vertex_buffer,
            self.throttle_samples.as_slices(),
        )?;
        self.update_vertex_buffer(&self.brake_vertex_buffer, self.brake_samples.as_slices())?;

        Ok(())
    }

    fn update_vertex_buffer(
        &self,
        buffer: &Arc<Buffer<PositionOnlyVertex>>,
        (data1, data2): (&[f32], &[f32]),
    ) -> Result<()> {
        let points = data1
            .iter()
            .chain(data2.iter())
            .enumerate()
            .map(|(x, &date)| PositionOnlyVertex {
                position: self.ortho * vec4(x as f32, date, 0.0, 1.0),
            })
            .collect::<Vec<PositionOnlyVertex>>();

        buffer.fill(&points)
    }

    pub fn render(&self) -> Result<Arc<CommandBuffer>> {
        let command_buffer =
            CommandBuffer::new_primary().build(self.device.clone(), self.queue.clone())?;

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

            self.render_target
                .begin(&recorder, VK_SUBPASS_CONTENTS_INLINE, 0);

            recorder.bind_pipeline(self.pipeline.pipeline())?;

            recorder.bind_descriptor_sets_minimal(&[&self.throttle_descriptor]);
            recorder.bind_vertex_buffer(&self.throttle_vertex_buffer);
            recorder.draw_complete_single_instance(self.throttle_vertex_buffer.size() as u32);

            recorder.bind_descriptor_sets_minimal(&[&self.brake_descriptor]);
            recorder.bind_vertex_buffer(&self.brake_vertex_buffer);
            recorder.draw_complete_single_instance(self.brake_vertex_buffer.size() as u32);

            self.render_target.end(&recorder);
        }

        Ok(command_buffer)
    }
}

impl UiOverlay for Pedals {}

impl DataReceiver for Pedals {
    fn game_phase_change(&mut self, phase: GamePhase) -> Result<()> {
        match phase {
            GamePhase::None => {
                self.enable = false;
                self.gui.disable()?;
            }
            _ => {
                self.enable = true;
                self.gui.enable()?;
            }
        }

        Ok(())
    }

    fn update_for_phase(&self, phase: GamePhase) -> bool {
        match phase {
            GamePhase::Practice | GamePhase::Qualifying | GamePhase::Race | GamePhase::Warmup => {
                true
            }
            _ => false,
        }
    }

    fn scoring_update(
        &mut self,
        _phase: GamePhase,
        _vehicle_scoring: &[VehicleScoringInfoV01],
    ) -> Result<()> {
        Ok(())
    }

    fn telemetry_update(
        &mut self,
        player_id: Option<i32>,
        telemetries: &[rF2VehicleTelemetry],
    ) -> Result<()> {
        if let Some(id) = player_id {
            if let Some(telemetry) = telemetries.iter().find(|telemetry| telemetry.id == id) {
                let brake = 1.0 - telemetry.unfiltered_brake as f32;
                let throttle = 1.0 - telemetry.unfiltered_throttle as f32;

                self.throttle.set_progress(throttle)?;
                self.brake.set_progress(brake)?;

                self.throttle_samples.push_overwrite(throttle);
                self.brake_samples.push_overwrite(brake);

                self.update_vertex_buffers()?;
            }
        }

        Ok(())
    }
}

#[cfg(test)]
mod test {
    use ringbuf::{HeapRb, Rb};

    #[test]
    fn rb_test() {
        const CAP: usize = 10;

        let mut buf = HeapRb::new(CAP);

        for _ in 0..CAP {
            buf.push_overwrite(20);
        }

        println!("{:?}", buf.as_slices());

        buf.push_overwrite(40);
        buf.push_overwrite(40);
        buf.push_overwrite(40);
        buf.push_overwrite(40);

        println!("{:?}", buf.as_slices());
    }
}