
use crate::wgpuctx::WgpuCtx;

pub struct RenderPipelineBuilder<'a>
{
    bind_groups: Vec<Option<&'a wgpu::BindGroupLayout>>,
    shader: &'a wgpu::ShaderModule,

    vertex_entry_point: Option<&'static str>,
    fragment_entry_point: Option<&'static str>,

    vertex_comp_options: Option<wgpu::PipelineCompilationOptions<'a>>,
    fragment_comp_options: Option<wgpu::PipelineCompilationOptions<'a>>,

    vertex_buffer_layouts: Vec<wgpu::VertexBufferLayout<'a>>,

    cull_mode: Option<wgpu::Face>,
    primitive_topology: wgpu::PrimitiveTopology
}

impl<'a> RenderPipelineBuilder<'a>
{
    pub fn new(
        shader: &'a wgpu::ShaderModule)
    -> Self {
        Self {
            bind_groups: Vec::new(),
            shader: shader,

            vertex_entry_point: Some("vs_main"),
            fragment_entry_point: Some("fs_main"),

            vertex_comp_options: None,
            fragment_comp_options: None,

            vertex_buffer_layouts: Vec::new(),

            cull_mode: Some(wgpu::Face::Back),
            primitive_topology: wgpu::PrimitiveTopology::TriangleList
        }
    }

    pub fn add_bindgroup(
        mut self,
        bindgroup: &'a wgpu::BindGroupLayout)
    -> Self {
        self.bind_groups.push(Some(bindgroup));
        self
    }

    pub fn add_vertex_layout(
        mut self,
        layout: wgpu::VertexBufferLayout<'a>)
    -> Self {
        self.vertex_buffer_layouts.push(layout);
        self
    }

    pub fn cull_mode(
        mut self,
        mode: Option<wgpu::Face>
    ) -> Self {
        self.cull_mode = mode; self
    }

    pub fn primitive_topology(
        mut self,
        topology: wgpu::PrimitiveTopology
    ) -> Self {
        self.primitive_topology = topology; self
    }

    pub fn build(
        self,
        label: Option<&'static str>,
        wgpuctx: &WgpuCtx)
    -> wgpu::RenderPipeline
    {
        let layout_descr = wgpu::PipelineLayoutDescriptor {
            label: label,
            bind_group_layouts: self.bind_groups.as_slice(),
            immediate_size: 0,
        };

        let layout = wgpuctx.create_pipeline_layout(&layout_descr);

        wgpuctx.create_render_pipeline(
            &wgpu::RenderPipelineDescriptor {
                label: label,
                layout: Some(&layout),
                vertex: wgpu::VertexState {
                    module: self.shader,
                    entry_point: self.vertex_entry_point,
                    compilation_options: match self.vertex_comp_options {
                        Some(option) => option,
                        None => wgpu::PipelineCompilationOptions::default()
                    },
                    buffers: self.vertex_buffer_layouts.as_slice()
                },
                fragment: Some(wgpu::FragmentState {
                    module: self.shader,
                    entry_point: self.fragment_entry_point,
                    compilation_options: match self.fragment_comp_options {
                        Some(option) => option,
                        None => wgpu::PipelineCompilationOptions::default()
                    },
                    targets: &[Some(wgpu::ColorTargetState {
                        format: wgpuctx.surface_config().format,
                        blend: Some(wgpu::BlendState{
                            color: wgpu::BlendComponent { 
                                src_factor: wgpu::BlendFactor::SrcAlpha, 
                                dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, 
                                operation: wgpu::BlendOperation::Add 
                            },
                            alpha: wgpu::BlendComponent::OVER
                        }),
                        write_mask: wgpu::ColorWrites::ALL
                    })],
                }),
                primitive: wgpu::PrimitiveState {
                    topology: self.primitive_topology,
                    strip_index_format: if self.primitive_topology.is_strip() {
                        Some(wgpu::IndexFormat::Uint32) } else {
                        None },
                    front_face: wgpu::FrontFace::Ccw,
                    cull_mode: self.cull_mode,
                    polygon_mode: wgpu::PolygonMode::Fill,
                    unclipped_depth: false,
                    conservative: false
                },
                depth_stencil: None,
                multisample: wgpu::MultisampleState {
                    count: 1,
                    mask: !0,
                    alpha_to_coverage_enabled: false
                },
                cache: None,
                multiview_mask: None,

            }
        )
    }
}
