#![allow(clippy::drop_non_drop)]
pub mod kernel;
pub mod params;

use after_effects::{self as ae, Command, InData, OutData, Parameters};
use prgpu::adobe;
use prgpu::effect::{Effect, EffectDescriptor, FrameDataContext, NoLicenseGate, ParamApi};
use prgpu::graph::{MipPyramidDesc, RenderGraph, Slot};
use prgpu::params::SetupParams;

use crate::kernel as k;
use crate::kernel::{ {{ pass1_kernel_params_name }}, {{ pass2_kernel_params_name }} };
use crate::params::Params;

const FEEDBACK_URL: &str = "https://example.com/feedback"; // TODO: replace with your feedback URL

#[derive(Clone, Copy)]
pub struct FrameData {
	pub edge_params: {{ pass1_kernel_params_name }},
	pub composite_params: {{ pass2_kernel_params_name }},
}

#[derive(Default)]
pub struct {{ pass1_kernel_params_name }}Effect;

impl Effect for {{ pass1_kernel_params_name }}Effect {
	type Params = Params;
	type FrameData = FrameData;
	type License = NoLicenseGate;

	fn descriptor() -> EffectDescriptor {
		EffectDescriptor::new("{{ effect_name }}")
			.about("{{ display_name }}")
			.version(env!("CARGO_PKG_VERSION"))
			.options_button("Infos")
			.premiere_pixel_formats([ae::pr::PixelFormat::Bgra4444_32f, ae::pr::PixelFormat::Bgra4444_8u])
	}

	fn params(params: &mut Parameters<Self::Params>, in_data: InData, out_data: OutData) -> Result<(), ae::Error> {
		Params::setup(params, in_data, out_data)
	}

	fn ui(api: &mut ParamApi<Self::Params>) -> Result<(), ae::Error> {
		api.actions(|a| {
			a.on_click(Params::ReloadShaders, |ctx| {
				ctx.hot_reload_shaders();
				Ok(())
			});
			a.on_click(Params::Feedback, |_ctx| {
				let _ = webbrowser::open(FEEDBACK_URL);
				Ok(())
			});
		});
		Ok(())
	}

	fn frame_data(ctx: FrameDataContext<Self::Params>) -> Result<Self::FrameData, ae::Error> {
		let edge_params = {{ pass1_kernel_params_name }}::from_context(&ctx)?;
		let composite_params = {{ pass2_kernel_params_name }}::from_context(&ctx)?;
		Ok(FrameData {
			edge_params,
			composite_params,
		})
	}

	fn pipeline(g: &mut RenderGraph<Self::FrameData>) {
		let buf_a = g.declare_mip_pyramid("buf_a", |ctx| {
			MipPyramidDesc::new(ctx.output_width(), ctx.output_height()).levels(1).tag(0x4D50_0001)
		});

		g.add_pass("edge", k::{{ pass1_kernel_name }}::kernel(), Slot::MainSource, buf_a.mip(0), |ctx| {
			let mut p = ctx.frame_data().edge_params;
			p.edge_src_width = ctx.output_width();
			p.edge_src_height = ctx.output_height();
			p.edge_src_pitch = ctx.output_width();
			p
		});

		g.add_pass_with_input("composite", k::{{ pass2_kernel_name }}::kernel(), buf_a.mip(0), Slot::MainSource, Slot::Output, |ctx| {
			let mut p = ctx.frame_data().composite_params;
			p.edge_width = ctx.output_width();
			p.edge_height = ctx.output_height();
			p.edge_pitch = ctx.output_width();
			p
		});
	}
}

#[derive(Default)]
struct Plugin(adobe::ae::EffectAdapter<{{ pass1_kernel_params_name }}Effect>);

impl AdobePluginGlobal for Plugin {
	fn params_setup(&self, params: &mut Parameters<Params>, in_data: InData, out_data: OutData) -> Result<(), ae::Error> {
		self.0.params_setup(params, in_data, out_data)
	}

	fn handle_command(&mut self, command: Command, in_data: InData, out_data: OutData, params: &mut Parameters<Params>) -> Result<(), ae::Error> {
		self.0.handle_command(command, in_data, out_data, params)
	}
}

ae::define_effect!(Plugin, (), Params);

pub type PremiereGPU = adobe::premiere::GpuFilterAdapter<{{ pass1_kernel_params_name }}Effect>;
premiere::define_gpu_filter!(PremiereGPU);
