use vulkano::pipeline::GraphicsPipelineAbstract;
use std::sync::Arc;
use std::collections::{HashSet, HashMap};
use vulkano::device::Device;
use vulkano::framebuffer::{RenderPassAbstract, Subpass};
use vulkano::pipeline::GraphicsPipeline;
use vulkano::pipeline::shader::{GraphicsEntryPoint, ShaderModule, GraphicsShaderType, GeometryShaderExecutionMode};
use shade_runner::{Input, Output, Layout, Entry};
use std::ffi::CStr;
use std::marker::PhantomData;
use vulkano::pipeline::depth_stencil::{DepthStencil, Compare, DepthBounds, Stencil, StencilOp};
use vulkano::pipeline::vertex::{SingleBufferDefinition, OneVertexOneInstanceDefinition, Vertex};
use shade_runner as sr;
use crate::canvas::managed::shader::shader_common::{ShaderType, CompiledShaderResources, CompiledShader};
use crate::canvas::managed::handles::CompiledShaderHandle;
use crate::canvas::managed::shader::generic_shader::GenericShader;
use crate::canvas::managed::shader::dynamic_vertex::RuntimeVertexDef;
use crate::canvas::managed::ShaderSpecializationConstants;
use crate::util::vertex::ColorVertex3D;
use vulkano::pipeline::blend::{LogicOp, AttachmentBlend, BlendOp, BlendFactor};


/// CanvasShader holds the pipeline and render pass for the input shader source
#[derive(Clone)]
pub struct TextShader {
    graphics_pipeline: Option<Arc<dyn GraphicsPipelineAbstract + Sync + Send>>,

    handle: Arc<CompiledShaderHandle>,
    name: String,

    device: Arc<Device>,
    renderpass: Arc<dyn RenderPassAbstract + Send + Sync>,
}

impl TextShader {}

/// Gives CanvasShader the resource functions
impl CompiledShaderResources for TextShader {}

/// Convenience interface so we don't have to juggle shader types
impl CompiledShader for TextShader {

    /// This will explode when the shader does not want to compile
    fn new<V: Vertex>(filename: String,
           device: Arc<Device>,
           handle: Arc<CompiledShaderHandle>,
           render_pass: Arc<dyn RenderPassAbstract + Send + Sync>) -> TextShader {

        let compiled_vertex = GenericShader::compile(
            GenericShader::get_path(filename.clone(), ShaderType::VERTEX).into(),
            device.clone(), ShaderType::VERTEX
        );

        let vertex_entry_point = unsafe {
            Some(compiled_vertex.1.graphics_entry_point(
                &CStr::from_bytes_with_nul_unchecked(b"main\0"),
                compiled_vertex.0.input.unwrap(),
                compiled_vertex.0.output.unwrap(),
                compiled_vertex.0.layout,
                GenericShader::convert_vk(ShaderType::VERTEX),
            )).unwrap()
        };

        let compiled_fragment = GenericShader::compile(
            GenericShader::get_path(filename.clone(), ShaderType::FRAGMENT).into(),
            device.clone(), ShaderType::FRAGMENT
        );

        let fragment_entry_point = unsafe {
            Some(compiled_fragment.1.graphics_entry_point(
                &CStr::from_bytes_with_nul_unchecked(b"main\0"),
                compiled_fragment.0.input.unwrap(),
                compiled_fragment.0.output.unwrap(),
                compiled_fragment.0.layout,
                GenericShader::convert_vk(ShaderType::FRAGMENT),
            )).unwrap()
        };

        let stencil = DepthStencil {
            depth_compare: Compare::Less,
            depth_write: true,
            depth_bounds_test: DepthBounds::Disabled,
            stencil_front: Stencil {
                compare: Compare::NotEqual,
                pass_op: StencilOp::Keep,
                fail_op: StencilOp::IncrementAndWrap,
                depth_fail_op: StencilOp::Keep,
                compare_mask: None,
                write_mask: None,
                reference: None,
            },
            stencil_back: Stencil {
                compare: Compare::NotEqual,
                pass_op: StencilOp::Keep,
                fail_op: StencilOp::DecrementAndWrap,
                depth_fail_op: StencilOp::Keep,
                compare_mask: None,
                write_mask: None,
                reference: None,
            },
        };

        let vertex_definition = RuntimeVertexDef::from_primitive(0);


        let blend = AttachmentBlend {
            enabled: true,
            color_op: BlendOp::Add,
            // color_source: BlendFactor::SrcAlpha,
            // color_destination: BlendFactor::OneMinusSrcAlpha,
            color_source: BlendFactor::One,
            color_destination: BlendFactor::One,
            alpha_op: BlendOp::Add,
            alpha_source: BlendFactor::One,
            alpha_destination: BlendFactor::One,
            mask_red: true,
            mask_green: true,
            mask_blue: true,
            mask_alpha: true,
        };

        TextShader {
            graphics_pipeline:
            Some(Arc::new(GraphicsPipeline::start()

                .vertex_input(SingleBufferDefinition::<V>::new())
                //.vertex_input(vertex_definition)

                .vertex_shader(vertex_entry_point.clone(), ShaderSpecializationConstants {
                    first_constant: 0,
                    second_constant: 0,
                    third_constant: 0.0,
                })

                .triangle_list()
                // Use a resizable viewport set to draw over the entire window
                .viewports_dynamic_scissors_irrelevant(1)

                .fragment_shader(fragment_entry_point.clone(), ShaderSpecializationConstants {
                    first_constant: 0,
                    second_constant: 0,
                    third_constant: 0.0,
                })

                .depth_stencil(stencil)
//                .blend_collective(blend)

                //.blend_logic_op(LogicOp::Noop)
                // We have to indicate which subpass of which render pass this pipeline is going to be used
                // in. The pipeline will only be usable from this particular subpass.
                .render_pass(Subpass::from(render_pass.clone(), 0).unwrap())

                .build(device.clone())
                .unwrap())),

            device: device,
            handle: handle.clone(),
            name: filename.clone(),
            renderpass: render_pass.clone(),
        }
    }

    fn get_name(&self) -> String {
        self.name.clone()
    }

    fn get_handle(&self) -> Arc<CompiledShaderHandle> {
        self.handle.clone()
    }

    fn get_pipeline(&self) -> Arc<dyn GraphicsPipelineAbstract + Sync + Send> {
        self.graphics_pipeline.clone().unwrap()
    }
    fn get_renderpass(&self) -> Arc<dyn RenderPassAbstract + Send + Sync> {
        self.renderpass.clone()
    }
    fn recompile<V: Vertex>(self, render_pass: Arc<dyn RenderPassAbstract + Send + Sync>) -> TextShader {
        TextShader::new::<V>(self.name,
                        self.device,
                        self.handle,
                        self.renderpass.clone())
    }
}












