// Used in the GasaGen.cpp translation unit
#if GASA_INTELLISENSE_DIRECTIVES
#pragma once
#define GEN_EXPOSE_BACKEND
#include "gen.hpp"
#include "gen.builder.hpp"
#include "GasaGenCommon.cpp"
#endif

void gen_netslime_interface(CodeClass aclass)
{
	CodeBody net_slime_class_interface = def_body(ECode::Class_Body);
	{
	#pragma push_macro("FORCEINLINE")
	#undef FORCEINLINE
		CodeFn DrawNetCullingSphere = parse_function( code(
			FORCEINLINE void DrawNetCullingSphere(float Duration, float Thickness) const final { Gasa::DrawNetCullingSphere(this, Duration, Thickness); }
		));
		CodeFn GetNetworkMode   = parse_function( code( FORCEINLINE ENetworkMode GetNetworkMode()   const { return Gasa::GetNetworkMode(this); }  ));
		CodeFn IsClient         = parse_function( code( FORCEINLINE bool         IsClient()         const { return Gasa::IsClient(this); }        ));
		CodeFn IsListenServer   = parse_function( code( FORCEINLINE bool         IsListenServer()   const { return Gasa::IsListenServer(this); }  ));
		CodeFn IsNetOwner       = parse_function( code( FORCEINLINE bool         IsNetOwner()       const { return Gasa::IsNetOwner(this); }      ));
		CodeFn IsServer         = parse_function( code( FORCEINLINE bool         IsServer()         const { return Gasa::IsServer(this); }        ));
		CodeFn IsSimulatedProxy = parse_function( code( FORCEINLINE bool         IsSimulatedProxy() const { return Gasa::IsSimulatedProxy(this); } ));
		CodeFn NetLog = parse_function( code(
			FORCEINLINE void NetLog( FString Message, EGasaVerbosity Verbosity = EGasaVerbosity::Log
				, FLogCategoryBase& Category  = LogGasaNet
				, bool              DumpStack = false
				, int32             Line      = __builtin_LINE()
				, const ANSICHAR*   File      = __builtin_FILE()
				, const ANSICHAR*   Func      = __builtin_FUNCTION() )
			{
				Gasa::NetLog(this, Message, Verbosity, Category, DumpStack, Line, File, Func );
			}
		));
		CodeFn ServerAuthorized = parse_function( code( FORCEINLINE bool ServerAuthorized() const { return Gasa::ServerAuthorized(this); } ));
	#pragma pop_macro("FORCEINLINE")
		net_slime_class_interface.append(GetNetworkMode);
		net_slime_class_interface.append(IsClient);
		net_slime_class_interface.append(IsListenServer);
		net_slime_class_interface.append(IsNetOwner);
		net_slime_class_interface.append(IsServer);
		net_slime_class_interface.append(IsSimulatedProxy);
		net_slime_class_interface.append(NetLog);
	}

	CodeBody new_body = def_body(ECode::Class_Body);
	for(Code code = aclass->Body.begin(); code != aclass->Body.end(); ++ code )
	{
		switch (code->Type)
		{
			default:
				new_body.append(code);
			break;

			// TODO(Ed): Could this be turned into a singly? void find_and_swap_region_pragma(CodeClass, StrC region)
			// IT could return void if its assumed that the Code passed will have destructive edits to the body.
			case ECode::Preprocess_Pragma:
			{
				local_persist bool found = false;
				if (found || ! code->Content.starts_with( txt("region NetSlime")))
				{
					new_body.append(code);
					continue;
				}

				// Add pragma
				new_body.append(code);
				++ code;

				new_body.append( def_comment( txt("NetSlime interface is generated by GasaGen/GasaGen_NetSlime.cpp")));
				new_body.append(net_slime_class_interface);

				while (code->Type != ECode::Preprocess_Pragma
					|| ! code->Content.starts_with(txt("endregion NetSlime")))
					++ code;

				new_body.append(code);
			}
			break;
		}
	}
	aclass->Body = new_body;
}

void gen_netslime_interfaces()
{
	Array<StringCached> header_paths = Array<StringCached>::init_reserve(GlobalAllocator, 32);
	// header_paths.append(get_cached_string(txt( path_module_gasa     "GasaObject.h")));
	// header_paths.append(get_cached_string(txt( path_gasa_actors     "GasaActor.h")));
	// header_paths.append(get_cached_string(txt( path_gasa_characters "GasaCharacter.h")));
	// header_paths.append(get_cached_string(txt( path_gasa_game       "GasaGameMode.h")));
	// header_paths.append(get_cached_string(txt( path_gasa_game       "GasaGameState.h")));

	for (StringCached path : header_paths)
	{
		CodeBody original_header = parse_file(path);
		CodeBody header_body     = def_body(ECode::Global_Body);
		for (Code code : original_header)
		{
			switch (code->Type) {
			case ECode::Class:
			{
				CodeClass aclass = code.cast<CodeClass>();
				gen_netslime_interface(aclass);
				header_body.append(aclass);
			}
			break;
			default:
				header_body.append(code);
			}
		}
		Builder header = Builder::open(path);
		header.print(header_body);
		header.write();
		format_file(path);
	}
}