// AUTOGENERATED COPYRIGHT HEADER START
// Copyright (C) 2021-2023 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
// AUTOGENERATED COPYRIGHT HEADER END

#pragma once
#include "obs/gs/gs-effect.hpp"
#include "obs/gs/gs-rendertarget.hpp"
#include "obs/gs/gs-texture.hpp"
#include "obs/obs-source-factory.hpp"
#include "plugin.hpp"
#include "util/util-threadpool.hpp"

#include "warning-disable.hpp"
#include <atomic>
#include <memory>
#include <mutex>
#include "warning-enable.hpp"

#ifdef ENABLE_FILTER_UPSCALING_NVIDIA
#include "nvidia/vfx/nvidia-vfx-superresolution.hpp"
#endif

namespace streamfx::filter::upscaling {
	enum class upscaling_provider {
		INVALID                = -1,
		AUTOMATIC              = 0,
		NVIDIA_SUPERRESOLUTION = 1,
	};

	const char* cstring(upscaling_provider provider);

	std::string string(upscaling_provider provider);

	class upscaling_instance : public ::streamfx::obs::source_instance {
		std::pair<uint32_t, uint32_t> _in_size;
		std::pair<uint32_t, uint32_t> _out_size;

		std::atomic<upscaling_provider>         _provider;
		upscaling_provider                      _provider_ui;
		std::atomic<bool>                       _provider_ready;
		std::mutex                              _provider_lock;
		std::shared_ptr<util::threadpool::task> _provider_task;

		std::shared_ptr<::streamfx::obs::gs::effect>  _standard_effect;
		std::shared_ptr<::streamfx::obs::gs::sampler> _channel0_sampler;
		std::shared_ptr<::streamfx::obs::gs::sampler> _channel1_sampler;

		std::shared_ptr<::streamfx::obs::gs::rendertarget> _input;
		std::shared_ptr<::streamfx::obs::gs::texture>      _output;
		bool                                               _dirty;

#ifdef ENABLE_FILTER_UPSCALING_NVIDIA
		std::shared_ptr<::streamfx::nvidia::vfx::superresolution> _nvidia_fx;
#endif

		public:
		upscaling_instance(obs_data_t* data, obs_source_t* self);
		~upscaling_instance() override;

		void load(obs_data_t* data) override;
		void migrate(obs_data_t* data, uint64_t version) override;
		void update(obs_data_t* data) override;
		void properties(obs_properties_t* properties);

		uint32_t get_width() override;
		uint32_t get_height() override;

		void video_tick(float time) override;
		void video_render(gs_effect_t* effect) override;

		private:
		void switch_provider(upscaling_provider provider);
		void task_switch_provider(util::threadpool::task_data_t data);

#ifdef ENABLE_FILTER_UPSCALING_NVIDIA
		void nvvfxsr_load();
		void nvvfxsr_unload();
		void nvvfxsr_size();
		void nvvfxsr_process();
		void nvvfxsr_properties(obs_properties_t* props);
		void nvvfxsr_update(obs_data_t* data);
#endif
	};

	class upscaling_factory : public ::streamfx::obs::source_factory<::streamfx::filter::upscaling::upscaling_factory, ::streamfx::filter::upscaling::upscaling_instance> {
#ifdef ENABLE_FILTER_UPSCALING_NVIDIA
		bool                                           _nvidia_available;
		std::shared_ptr<::streamfx::nvidia::cuda::obs> _nvcuda;
		std::shared_ptr<::streamfx::nvidia::cv::cv>    _nvcvi;
		std::shared_ptr<::streamfx::nvidia::vfx::vfx>  _nvvfx;
#endif

		public:
		virtual ~upscaling_factory();
		upscaling_factory();

		virtual const char* get_name() override;

		virtual void              get_defaults2(obs_data_t* data) override;
		virtual obs_properties_t* get_properties2(upscaling_instance* data) override;

#ifdef ENABLE_FRONTEND
		static bool on_manual_open(obs_properties_t* props, obs_property_t* property, void* data);
#endif

		bool               is_provider_available(upscaling_provider);
		upscaling_provider find_ideal_provider();

		public: // Singleton
		static void                                                              initialize();
		static void                                                              finalize();
		static std::shared_ptr<::streamfx::filter::upscaling::upscaling_factory> instance();
	};

} // namespace streamfx::filter::upscaling
