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

#pragma once
#include "common.hpp"

namespace streamfx::obs {
	class source {
		obs_source_t* _ref;
		bool          _is_owner;

		public:
		FORCE_INLINE ~source()
		{
			release();
		};

		/** Empty/Invalid hard reference.
		 *
		 */
		FORCE_INLINE source() : _ref(nullptr), _is_owner(false){};

		/** Create a new hard reference from an existing pointer.
		 *
		 * @param source The source object to reference.
		 * @param add_reference Should we increment the reference counter (duplicate ownership) or leave as it is (transfer ownership)?
		 */
		FORCE_INLINE source(obs_source_t* source, bool duplicate_reference = false, bool take_ownership = true) : _is_owner(take_ownership)
		{
			if (duplicate_reference) {
				_ref = obs_source_get_ref(source);
			} else {
				_ref = source;
			}
		};

		/** Create a new hard reference for a given source by name.
		 *
		 * Attention: May fail if the name does not exactly match.
		 */
		FORCE_INLINE source(std::string_view name) : _is_owner(true)
		{
			_ref = obs_get_source_by_name(name.data());
		};

		/** Create a new hard reference for a new source.
		 *
		 * Attention: May fail.
		 */
		FORCE_INLINE source(std::string_view id, std::string_view name, obs_data_t* settings, obs_data_t* hotkeys) : _is_owner(true)
		{
			_ref = obs_source_create(id.data(), name.data(), settings, hotkeys);
			if (!_ref) {
				throw std::runtime_error("Failed to create source with given parameters.");
			}
		};

		/** Create a new hard reference for a new private source.
		 *
		 * Attention: May fail.
		 */
		FORCE_INLINE source(std::string_view id, std::string_view name, obs_data_t* settings) : _is_owner(true)
		{
			_ref = obs_source_create_private(id.data(), name.data(), settings);
			if (!_ref) {
				throw std::runtime_error("Failed to create source with given parameters.");
			}
		};

		FORCE_INLINE source(source&& move) noexcept
		{
			_ref      = move._ref;
			_is_owner = move._is_owner;
			move._ref = nullptr;
		};

		FORCE_INLINE ::streamfx::obs::source& operator=(source&& move) noexcept
		{
			release();
			_ref      = move._ref;
			_is_owner = move._is_owner;
			move._ref = nullptr;
			return *this;
		};

		FORCE_INLINE source(const source& copy)
		{
			if (copy._is_owner) {
				_ref = obs_source_get_ref(copy._ref);
			} else {
				_ref = copy._ref;
			}
			_is_owner = copy._is_owner;
		};

		FORCE_INLINE ::streamfx::obs::source& operator=(const source& copy)
		{
			release();
			if (copy._is_owner) {
				_ref = obs_source_get_ref(copy._ref);
			} else {
				_ref = copy._ref;
			}
			_is_owner = copy._is_owner;
			return *this;
		};

		public:
		/** Retrieve the underlying pointer for manual manipulation.
		 *
		 * Attention: Ownership remains with the class instance.
		 */
		FORCE_INLINE obs_source_t* get() const
		{
			return _ref;
		};

		/** Release the underlying pointer.
		 *
		 * Useful if you need to respond to the "source_remove" or "remove" signals.
		 *
		 * EXPORT void obs_source_release(obs_source_t *source);
		 */
		FORCE_INLINE void release()
		{
			if (_ref && _is_owner) {
				obs_source_release(_ref);
				_ref      = nullptr;
				_is_owner = false;
			}
		};

		/** Duplicate the source if possible.
		 *
		 * Will create a duplicate the source entirely unless forbidden. If forbidden, will instead just return a reference.
		 *
		 * EXPORT obs_source_t *obs_source_duplicate(obs_source_t *source, const char *desired_name, bool create_private);
		 */
		FORCE_INLINE ::streamfx::obs::source duplicate(std::string_view name, bool is_private)
		{
			return obs_source_duplicate(_ref, name.data(), is_private);
		};

		public:
		/** Get the source info identifier for this reference.
		 *
		 * May have a version appended to the end.
		 *
		 * EXPORT const char *obs_source_get_id(const obs_source_t *source);
		 */
		FORCE_INLINE std::string_view id() const
		{
			return obs_source_get_id(_ref);
		};

		/** Get the source info identifier for this reference.
		 *
		 * EXPORT const char *obs_source_get_unversioned_id(const obs_source_t *source);
		 */
		FORCE_INLINE std::string_view unversioned_id() const
		{
			return obs_source_get_unversioned_id(_ref);
		};

		/** What type is this source?
		 *
		 */
		FORCE_INLINE obs_source_type type() const
		{
			return obs_source_get_type(_ref);
		};

		/** Get the output flags.
		 * 
		 * EXPORT uint32_t obs_source_get_output_flags(const obs_source_t *source);
		 */
		FORCE_INLINE uint32_t output_flags() const
		{
			return obs_source_get_output_flags(_ref);
		};

		/** Get the flags
		 * 
		 * EXPORT uint32_t obs_source_get_flags(const obs_source_t *source);
		 */
		FORCE_INLINE uint32_t flags() const
		{
			return obs_source_get_flags(_ref);
		};

		/** Set the flags
		 * 
		 * EXPORT void obs_source_set_default_flags(obs_source_t* source, uint32_t flags);
		 */
		FORCE_INLINE void default_flags(uint32_t flags)
		{
			obs_source_set_default_flags(_ref, flags);
		};

		/** Set the flags
		 * 
		 * EXPORT void obs_source_set_flags(obs_source_t *source, uint32_t flags);
		 */
		FORCE_INLINE void flags(uint32_t flags)
		{
			obs_source_set_flags(_ref, flags);
		};

		/** What is the source type called?
		 *
		 * EXPORT const char *obs_source_get_display_name(const char *id);
		 */
		FORCE_INLINE std::string_view display_name() const
		{
			return obs_source_get_display_name(id().data());
		};

		/** What is this source called?
		 *
		 * EXPORT const char *obs_source_get_name(const obs_source_t *source);
		 */
		FORCE_INLINE std::string_view name() const
		{
			return obs_source_get_name(_ref);
		};

		/** Change the name of the source.
		 *
		 * Triggers 'rename' on the source itself, as well as 'source_rename' globally if not private.
		 *
		 * EXPORT void obs_source_set_name(obs_source_t *source, const char *name);
		 */
		FORCE_INLINE void name(std::string_view new_name)
		{
			obs_source_set_name(_ref, new_name.data());
		};

		/** 
		 *
		 * EXPORT bool obs_source_enabled(const obs_source_t *source);
		 */
		FORCE_INLINE bool enabled() const
		{
			return obs_source_enabled(_ref);
		};

		/** 
		 *
		 * EXPORT void obs_source_set_enabled(obs_source_t *source, bool enabled);
		 */
		FORCE_INLINE void enabled(bool enabled)
		{
			obs_source_set_enabled(_ref, enabled);
		};

		/** 
		 *
		 * EXPORT bool obs_source_is_hidden(obs_source_t *source);
		 */
		FORCE_INLINE bool hidden() const
		{
			return obs_source_is_hidden(_ref);
		};

		/** 
		 *
		 * EXPORT void obs_source_set_hidden(obs_source_t *source, bool hidden);
		 */
		FORCE_INLINE void hidden(bool v)
		{
			obs_source_set_hidden(_ref, v);
		};

		public /* Size */:
		/** Get the base width of the source, if supported.
		 *
		 * This will be the size without any other scaling factors applied.
		 * 
		 * EXPORT uint32_t obs_source_get_base_width(obs_source_t *source);
		 */
		FORCE_INLINE uint32_t base_width() const
		{
			return obs_source_get_base_width(_ref);
		};

		/** Get the base height of the source, if supported.
		 *
		 * This will be the size without any other scaling factors applied.
		 * 
		 * EXPORT uint32_t obs_source_get_base_height(obs_source_t *source);
		 */
		FORCE_INLINE uint32_t base_height() const
		{
			return obs_source_get_base_height(_ref);
		};

		/** Get the reported width of the source, if supported.
		 *
		 * EXPORT uint32_t obs_source_get_width(obs_source_t *source);
		 */
		FORCE_INLINE uint32_t width() const
		{
			return obs_source_get_width(_ref);
		};

		/** Get the reported height of the source, if supported.
		 *
		 * EXPORT uint32_t obs_source_get_height(obs_source_t *source);
		 */
		FORCE_INLINE uint32_t height() const
		{
			return obs_source_get_height(_ref);
		};

		/** Get the reported size of the source, if supported.
		 *
		 * EXPORT uint32_t obs_source_get_width(obs_source_t *source);
		 * EXPORT uint32_t obs_source_get_height(obs_source_t *source);
		 */
		FORCE_INLINE std::pair<uint32_t, uint32_t> size() const
		{
			return {width(), height()};
		};

		public /* Configuration */:
		/** Is the source configurable?
		 *
		 * EXPORT bool obs_source_configurable(const obs_source_t *source);
		 */
		FORCE_INLINE bool configurable()
		{
			return obs_source_configurable(_ref);
		};

		/** Retrieve the properties for the source.
		 *
		 * EXPORT obs_properties_t *obs_get_source_properties(const char *id);
		 */
		FORCE_INLINE obs_properties_t* properties()
		{
			return obs_source_properties(_ref);
		};

		/** Signal for properties to be updated.
		 *
		 * EXPORT void obs_source_update_properties(obs_source_t *source);
		 */
		FORCE_INLINE void update_properties()
		{
			obs_source_update_properties(_ref);
		};

		/** Retrieve the default values for the settings.
		 *
		 * EXPORT obs_data_t *obs_get_source_defaults(const char *id);
		 */
		FORCE_INLINE obs_data_t* defaults()
		{
			return obs_get_source_defaults(id().data());
		};

		/** Retrieve the private settings.
		 *
		 * EXPORT obs_data_t *obs_source_get_private_settings(obs_source_t *item);
		 */
		FORCE_INLINE obs_data_t* private_settings()
		{
			return obs_source_get_private_settings(_ref);
		};

		/** Retrieve the current settings.
		 *
		 * EXPORT obs_data_t *obs_source_get_settings(const obs_source_t *source);
		 */
		FORCE_INLINE obs_data_t* settings()
		{
			return obs_source_get_settings(_ref);
		};

		/** Update the settings with new ones.
		 *
		 * Does not remove previously existing entries.
		 *
		 * EXPORT void obs_source_update(obs_source_t *source, obs_data_t *settings);
		 */
		FORCE_INLINE void update(obs_data_t* settings)
		{
			obs_source_update(_ref, settings);
		};

		/** Reset the settings, then update with new settings.
		 *
		 * EXPORT void obs_source_reset_settings(obs_source_t *source, obs_data_t *settings);
		 */
		FORCE_INLINE void reset_settings(obs_data_t* settings = nullptr)
		{
			obs_source_reset_settings(_ref, settings);
		};

		/** Signal the source to load.
		 *
		 * EXPORT void obs_source_load(obs_source_t *source);
		 */
		FORCE_INLINE void load()
		{
			obs_source_load(_ref);
		};

		/** Signal the source and all its filters to load.
		 *
		 * EXPORT void obs_source_load2(obs_source_t *source);
		 */
		FORCE_INLINE void load2()
		{
			obs_source_load2(_ref);
		};

		/** Signal the source to save.
		 *
		 * EXPORT void obs_source_save(obs_source_t *source);
		 */
		FORCE_INLINE void save()
		{
			obs_source_save(_ref);
		};

		public /* Interaction */:
		/** 
		 *
		 * EXPORT void obs_source_send_mouse_click(obs_source_t *source, const struct obs_mouse_event *event, int32_t type, bool mouse_up, uint32_t click_count);
		 */
		FORCE_INLINE void send_mouse_press(const obs_mouse_event* event, int32_t type, bool released, uint32_t count)
		{
			return obs_source_send_mouse_click(_ref, event, type, released, count);
		};

		/** 
		 *
		 * EXPORT void obs_source_send_mouse_move(obs_source_t *source, const struct obs_mouse_event *event, bool mouse_leave);
		 */
		FORCE_INLINE void send_mouse_move(const obs_mouse_event* event, bool leave)
		{
			return obs_source_send_mouse_move(_ref, event, leave);
		};

		/** 
		 *
		 * EXPORT void obs_source_send_mouse_wheel(obs_source_t *source, const struct obs_mouse_event *event, int x_delta, int y_delta);
		 */
		FORCE_INLINE void send_mouse_wheel(const obs_mouse_event* event, int32_t x_delta, int32_t y_delta)
		{
			return obs_source_send_mouse_wheel(_ref, event, x_delta, y_delta);
		};

		/** 
		 *
		 * EXPORT void obs_source_send_key_click(obs_source_t *source, const struct obs_key_event *event, bool key_up);
		 */
		FORCE_INLINE void send_key_press(const obs_key_event* event, bool released)
		{
			return obs_source_send_key_click(_ref, event, released);
		};

		/** 
		 *
		 * EXPORT void obs_source_send_focus(obs_source_t *source, bool focus);
		 */
		FORCE_INLINE void send_focus(bool in_focus)
		{
			return obs_source_send_focus(_ref, in_focus);
		};

		public /* Filters */:
		/** 
		 *
		 * EXPORT void obs_source_filter_add(obs_source_t *source, obs_source_t *filter);
		 */
		FORCE_INLINE void add_filter(::streamfx::obs::source& filter)
		{
			return obs_source_filter_add(_ref, filter.get());
		};

		/** 
		 *
		 * EXPORT void obs_source_filter_remove(obs_source_t *source, obs_source_t *filter);
		 */
		FORCE_INLINE void remove_filter(::streamfx::obs::source& filter)
		{
			return obs_source_filter_remove(_ref, filter.get());
		};

		/** 
		 *
		 * EXPORT obs_source_t *obs_filter_get_parent(const obs_source_t *filter);
		 */
		FORCE_INLINE ::streamfx::obs::source get_filter_parent()
		{
			return obs_filter_get_parent(_ref);
		};

		/** 
		 *
		 * EXPORT obs_source_t *obs_filter_get_target(const obs_source_t *filter);
		 */
		FORCE_INLINE ::streamfx::obs::source get_filter_target()
		{
			return obs_filter_get_target(_ref);
		};

		/** 
		 *
		 * EXPORT void obs_source_skip_video_filter(obs_source_t *filter);
		 */
		FORCE_INLINE void skip_video_filter()
		{
			return obs_source_skip_video_filter(_ref);
		};

		/** 
		 *
		 * EXPORT bool obs_source_process_filter_begin(obs_source_t *filter, enum gs_color_format format, enum obs_allow_direct_render allow_direct);
		 */
		FORCE_INLINE bool process_filter_begin(gs_color_format format, obs_allow_direct_render allow_direct)
		{
			return obs_source_process_filter_begin(_ref, format, allow_direct);
		};

		/** 
		 *
		 * EXPORT void obs_source_process_filter_end(obs_source_t *filter, gs_effect_t *effect, uint32_t width, uint32_t height);
		 */
		FORCE_INLINE void process_filter_end(gs_effect_t* effect, uint32_t width, uint32_t height)
		{
			obs_source_process_filter_end(_ref, effect, width, height);
		};

		/** 
		 *
		 * EXPORT void obs_source_process_filter_tech_end(obs_source_t *filter, gs_effect_t *effect, uint32_t width, uint32_t height, const char *tech_name);
		 */
		FORCE_INLINE void process_filter_tech_end(gs_effect_t* effect, uint32_t width, uint32_t height, std::string_view tech_name)
		{
			obs_source_process_filter_tech_end(_ref, effect, width, height, tech_name.data());
		};

		public /* Active/Showing References */:
		/** Is the source visible in main view?
		 *
		 * EXPORT bool obs_source_active(const obs_source_t *source);
		 */
		FORCE_INLINE bool active() const
		{
			return obs_source_active(_ref);
		}

		/** Add a active reference (visible in main view).
		 *
		 * EXPORT void obs_source_inc_active(obs_source_t *source);
		 */
		FORCE_INLINE void increment_active()
		{
			obs_source_inc_active(_ref);
		}

		/** Remove a active reference (visible in main view).
		 *
		 * EXPORT void obs_source_dec_active(obs_source_t *source);
		 */
		FORCE_INLINE void decrement_active()
		{
			obs_source_dec_active(_ref);
		}

		/** Is the source visible in auxiliary views?
		 *
		 * EXPORT bool obs_source_showing(const obs_source_t *source);
		 */
		FORCE_INLINE bool showing() const
		{
			return obs_source_showing(_ref);
		}

		/** Add a showing reference (visible in auxiliary view).
		 *
		 * EXPORT void obs_source_inc_showing(obs_source_t *source);
		 * EXPORT void obs_source_dec_showing(obs_source_t *source);
		 */
		FORCE_INLINE void increment_showing()
		{
			obs_source_inc_showing(_ref);
		}

		/** Add a showing reference (visible in auxiliary view).
		 *
		 * EXPORT void obs_source_inc_showing(obs_source_t *source);
		 * EXPORT void obs_source_dec_showing(obs_source_t *source);
		 */
		FORCE_INLINE void decrement_showing()
		{
			obs_source_dec_showing(_ref);
		}

		public /* ToDo */:
		/** 
		 *
		 * EXPORT obs_missing_files_t* obs_source_get_missing_files(const obs_source_t *source);
		 */
		obs_missing_files_t* get_missing_files();

		/** 
		 *
		 * EXPORT void obs_source_replace_missing_file(obs_missing_file_cb cb, obs_source_t *source, const char *new_path, void *data);
		 */
		void replace_missing_file(obs_missing_file_cb cb, std::string_view path, void* data);

		public:
		FORCE_INLINE operator obs_source_t*() const
		{
			return _ref;
		}

		FORCE_INLINE obs_source_t* operator*() const
		{
			return _ref;
		}

		FORCE_INLINE operator bool() const
		{
			return _ref != nullptr;
		};

		FORCE_INLINE bool operator==(source const& rhs) const
		{
			return _ref == rhs._ref;
		};

		FORCE_INLINE bool operator<(source const& rhs) const
		{
			return _ref < rhs._ref;
		};

		FORCE_INLINE bool operator==(obs_source_t* const& rhs) const
		{
			return _ref == rhs;
		};
	};
} // namespace streamfx::obs
