use std::ffi::CStr;
use vulkano::buffer::{CpuAccessibleBuffer, BufferUsage};
use std::sync::Arc;
use crate::canvas::canvas_state::{Drawable, CanvasState, CanvasImageHandle, CanvasTextureHandle};
use vulkano::framebuffer::RenderPassAbstract;
use vulkano::pipeline::{GraphicsPipelineAbstract, ComputePipeline};
use vulkano::device::Device;
use image::ImageBuffer;
use image::GenericImageView;
use vulkano::image::{ImageUsage, AttachmentImage};
use vulkano::descriptor::descriptor_set::{PersistentDescriptorSetBuf, PersistentDescriptorSet};
use vulkano::format::Format;
use vulkano::descriptor::pipeline_layout::PipelineLayout;
use std::borrow::Borrow;
use image::Rgba;
use vulkano::command_buffer::AutoCommandBufferBuilder;
use std::path::PathBuf;
use shade_runner::{CompiledShaders, Entry, CompileError};
use vulkano::pipeline::shader::ShaderModule;
use shaderc::CompileOptions;
use crate::compute::compu_kernel::{CompuKernel, CompuKernelHandle};
use crate::compute::compu_buffer::{CompuBuffers, CompuBufferHandle};
use crate::compute::compu_frame::CompuFrame;


// Canvas analog
pub struct CompuState {
    compute_buffers: Vec<CompuBuffers>,
    kernels: Vec<CompuKernel>,
}

impl CompuState {
    pub fn new() -> CompuState {
        CompuState {
            compute_buffers: vec![],
            kernels: vec![],
        }
    }

    pub fn new_compute_buffer(&mut self,
                              data: Vec<u8>,
                              dimensions: (u32, u32),
                              stride: u32,
                              device: Arc<Device>) -> Arc<CompuBufferHandle> {
        let handle = Arc::new(CompuBufferHandle {
            handle: self.compute_buffers.len() as u32
        });

        self.compute_buffers.push(
            (CompuBuffers::new(device.clone(), data, dimensions, stride, handle.clone())));

        handle
    }

    pub fn read_compute_buffer(&mut self, handle: Arc<CompuBufferHandle>) -> Vec<u8> {
        // This is way more difficult than it should be
        //let compute_buffer : CompuBuffers = self.compute_buffers.get(handle.into()).unwrap();
        //compute_buffer.read_output_buffer().to_vec()
        Vec::new()
    }
    pub fn write_compute_buffer(&self, handle: Arc<CompuBufferHandle>, data: Vec<u8>) {}

    pub fn new_kernel(&mut self,
                      filename: String,
                      device: Arc<Device>) -> Arc<CompuKernelHandle> {
        let handle = Arc::new(CompuKernelHandle {
            handle: self.kernels.len() as u32
        });

        self.kernels.push((CompuKernel::new(filename, device.clone(), handle.clone())));

        handle
    }

    pub fn get_kernel_handle(&self, kernel_name: String) -> Option<Arc<CompuKernelHandle>> {
        for i in self.kernels.clone() {
            if i.get_name() == kernel_name {
                return Some(i.get_handle());
            }
        }
        None
    }

    pub fn compute_commands(&mut self,
                            compute_frame: CompuFrame,
                            mut command_buffer: AutoCommandBufferBuilder,
                            canvas: &CanvasState)
                            -> AutoCommandBufferBuilder {

        // i = (Buffer, Kernel)
        for i in compute_frame.pure_compute {
            let buffer_id = (*i.0).clone().handle as usize;
            let kernel_id = (*i.1).clone().handle as usize;

            let buffer = self.compute_buffers.get(buffer_id).unwrap();
            let kernel = self.kernels.get(kernel_id).unwrap();

            let p = kernel.clone().get_pipeline();
            let d = buffer.get_descriptor_set(kernel.clone().get_pipeline());

            let size = buffer.get_size();

            command_buffer = command_buffer
                .dispatch([size.0 / 8, size.1 / 8, 1], p, d, ()).unwrap()
        }

        // i = (Buffer, Image, Kernel)
        for i in compute_frame.swapped_to_image {
            let buffer_id = (*i.0).clone().handle as usize;
            let image_id = i.1.clone();
            let kernel_id = (*i.2).clone().handle as usize;

            let buffer = self.compute_buffers.get(buffer_id).unwrap();
            let image = canvas.get_image(image_id);
            let kernel = self.kernels.get(kernel_id).unwrap();

            let p = kernel.clone().get_pipeline();
            let d = buffer.get_descriptor_set(kernel.clone().get_pipeline());

            let dimensions = image.dimensions();
            let dimensions = (dimensions[0], dimensions[1]);
            if dimensions != buffer.get_size() {
                panic!("Buffer sizes not the same");
            }

            let size = buffer.get_size();

            command_buffer = command_buffer
                .dispatch([size.0 / 8, size.1 / 8, 1], p, d, ()).unwrap()
                .copy_buffer_to_image(buffer.get_input_buffer(), image).unwrap();
        }


        // i = (Input Buffer, Output Buffer, Kernel)
        // Input buffer -> Kernel -> Output buffer
        for i in compute_frame.swapped_to_buffer {
            let input_buffer_id = (*i.0).clone().handle as usize;
            let output_buffer_id = (*i.1).clone().handle as usize;
            let kernel_id = (*i.2).clone().handle as usize;

            let input_buffer = self.compute_buffers.get(input_buffer_id).unwrap();
            let output_buffer = self.compute_buffers.get(output_buffer_id).unwrap();
            let kernel = self.kernels.get(kernel_id).unwrap();

            let pipeline = kernel.clone().get_pipeline();
            let descriptor_set = input_buffer.get_descriptor_set(kernel.clone().get_pipeline());

            if input_buffer.get_size() != output_buffer.get_size() {
                panic!("Buffer sizes not the same");
            }
            let size = input_buffer.get_size();

            command_buffer = command_buffer
                //  .dispatch([size.0/8, size.1/8,1], pipeline, descriptor_set, ()).unwrap()
                .copy_buffer(
                    input_buffer.get_output_buffer(),
                    output_buffer.get_input_buffer()).unwrap();
        }

        command_buffer
    }
}

