#pragma once

#include "RE/B/BSTArray.h"
#include "RE/B/BSTHashMap.h"
#include "RE/B/BSTLocklessQueue.h"
#include "RE/N/NiCullingProcess.h"
#include "RE/N/NiSmartPointer.h"

namespace RE
{
	class BSCompoundFrustum;
	class BSCullingProcess;
	class BSMultiBound;
	class BSOcclusionPlane;
	class BSPortalGraphEntry;
	class BSShaderAccumulator;
	class NiAVObject;
	class NiBound;
	class NiFrustumPlanes;

	struct CullJobDescriptor
	{
		NiPointer<BSShaderAccumulator>   shaderAccumulator;           // 00
		NiPointer<BSShaderAccumulator>   shaderAccumulatorSecondary;  // 08
		NiPointer<NiCamera>              camera;                      // 10
		BSCompoundFrustum*               compoundFrustum;             // 18
		NiFrustum*                       frustum;                     // 20
		BSPortalGraphEntry*              portalGraphEntry;            // 28
		BSCullingProcess*                cullingProcess;              // 30
		NiFrustumPlanes*                 customCullPlanes;            // 38
		NiAVObject*                      scene;                       // 40
		BSTArray<NiPointer<NiAVObject>>* cullingObjects;              // 48
		uint32_t                         cullMode;                    // 50
		float                            lightRadius;                 // 54
		uint32_t                         unk58;                       // 58
		bool                             isParabolic;                 // 5C
		bool                             isDirectionalLight;          // 5D
		bool                             ignorePreprocess;            // 5E
		bool                             doCustomCullPlanes;          // 5F
		bool                             cameraRelatedUpdates;        // 60
		bool                             unk61;                       // 61
		bool                             updateAccumulateFlag;        // 62
	};
	static_assert(sizeof(CullJobDescriptor) == 0x68);

	class BSCullingProcess : public NiCullingProcess
	{
	public:
		inline static constexpr auto RTTI = RTTI_BSCullingProcess;
		inline static constexpr auto Ni_RTTI = NiRTTI_BSCullingProcess;
		inline static constexpr auto VTABLE = VTABLE_BSCullingProcess;

		enum class BSCPCullingType
		{
			kNormal = 0,
			kAllPass = 1,
			kAllFail = 2,
			kIgnoreMultiBounds = 3,
			kForceMultiBoundsNoUpdate = 4
		};

		struct Data
		{
		public:
			// members
			BSGeometry*  geometry;              // 00
			std::int32_t alphaGroupStartIndex;  // 08
			std::int32_t alphaGroupStopIndex;   // 0C
		};
		static_assert(sizeof(Data) == 0x10);

		struct AlphaGroup
		{
		public:
			std::int8_t   unk00[0x30];  // 00
			std::uint32_t count;        // 30
			std::uint32_t pad34;        // 34
		};
		static_assert(sizeof(AlphaGroup) == 0x38);

		// override (NiCullingProcess)
		const NiRTTI* GetRTTI() const override;  // 00

		~BSCullingProcess() override;  // 15

		void Process1(NiAVObject* a_object, std::int32_t a_alphaGroupIndex) override;                         // 16
		void Process2(const NiCamera* a_camera, NiAVObject* a_scene, NiVisibleArray* a_visibleSet) override;  // 17
		void AppendVirtual(BSGeometry& a_visible, std::int32_t a_alphaGroupIndex) override;                   // 18

		// add
		virtual void               AppendNonAccum(NiAVObject& a_object, std::int32_t a_alphaGroupIndex);  // 19
		virtual bool               TestBaseVisibility1(BSMultiBound& a_bound);                            // 1A
		virtual bool               TestBaseVisibility2(BSOcclusionPlane& a_bound);                        // 1B
		[[nodiscard]] virtual bool TestBaseVisibility3(const NiBound& a_bound);                           // 1C

		bool AddShared(NiAVObject* a_object);

		BSTArray<NiPointer<NiAVObject>>                   objectArray;         // 00128
		BSTLocklessQueue::ObjMultiProdCons<Data, 4096, 0> cullQueue;           // 00140
		BSTHashMap<NiAVObject*, bool>                     roomSharedMap;       // 30160
		BSPortalGraphEntry*                               portalGraphEntry;    // 30190
		REX::Enum<BSCPCullingType>                        cullMode;            // 30198
		BSCompoundFrustum*                                compoundFrustum;     // 301A0
		REX::Enum<BSCPCullingType>                        cullModeStack[10];   // 301A8
		std::uint32_t                                     cullModeStackIndex;  // 301D0
		bool                                              recurseToGeometry;   // 301D4
		bool                                              isGroupingAlphas;    // 301D5
		std::uint16_t                                     unk301D6;            // 301D6
		BSTArray<AlphaGroup*>                             alphaGroups;         // 301D8
		std::int32_t                                      alphaGroupIndex;     // 301F0
		std::uint32_t                                     unk301F4;            // 301F4
	};
	static_assert(sizeof(BSCullingProcess) == 0x301F8);
}
