#pragma once
#include <core/inc.hpp>

namespace curl_read
{
	size_t writefunc( void* ptr, size_t size, size_t nmemb, std::string* s )
	{
		s->append( static_cast< char* >( ptr ), size * nmemb );
		return size * nmemb;
	}

	size_t writefunc_2( void* contents, size_t size, size_t nmemb, std::vector<char>* memory )
	{
		const size_t total_size = size * nmemb;
		memory->insert( memory->end( ), static_cast< char* >( contents ), static_cast< char* >( contents ) + total_size );
		return total_size;
	}
}

auto sync::c_sync::load_gzip( const std::string& url ) -> std::string {

	curl_easy_setopt( this->m_curl, CURLOPT_URL, url.c_str( ) );

	std::vector<char> memoryBuffer;
	curl_easy_setopt( this->m_curl, CURLOPT_WRITEFUNCTION, curl_read::writefunc_2 );
	curl_easy_setopt( this->m_curl, CURLOPT_WRITEDATA, &memoryBuffer );

	const auto code = curl_easy_perform( this->m_curl );

	if ( code != CURLE_OK ) {
		return hash_str( "failure" );
	}

	return gzip::decompress( memoryBuffer.data( ), memoryBuffer.size( ) );
}

auto sync::c_sync::load_gamelist( CURL* curl ) -> void {

	static bool already_downloaded = false;

	if ( already_downloaded ) return;

	curl_easy_setopt( curl, CURLOPT_URL, "https://dumpspace.spuckwaffel.com/Games/GameList.json" );

	// sorry!
	curl_easy_setopt( curl, CURLOPT_SSL_VERIFYPEER, 0 );

	std::string returnData = "";
	curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, curl_read::writefunc );
	curl_easy_setopt( curl, CURLOPT_WRITEDATA, &returnData );

	const auto code = curl_easy_perform( curl );

	if ( code != CURLE_OK ) {
		return;
	}

	json_game_list = nlohmann::json::parse( returnData.c_str( ), nullptr, false );

	if ( json_game_list == nlohmann::detail::value_t::discarded ) {
		printf( hash_str( "failed to parse game list, sorry!\n" ) );
		return;
	}

	already_downloaded = true;
}

auto sync::c_sync::cache_content( ) -> bool {

	m_curl = curl_easy_init( );
	game_id = hash_str( "6b77eceb" );
	website = hash_str( "https://dumpspace.spuckwaffel.com/Games/" );
	game_list = hash_str( "https://dumpspace.spuckwaffel.com/Games/GameList.json" );

	load_gamelist( m_curl );

	int fileVersion = 0;

	for ( auto jsonItem : json_game_list[ hash_str( "games" ) ] )
	{
		if ( jsonItem.value( hash_str( "hash" ), hash_str( "" ) ) == this->game_id )
		{
			engine_path = jsonItem.value( hash_str( "engine" ), hash_str( "" ) );
			location_path = jsonItem.value( hash_str( "location" ), hash_str( "" ) );
		}
	}

	if ( engine_path.empty( ) || location_path.empty( ) ) {
		printf( hash_str( "failed to find engine or location of the game, sorry!\n" ) );
		return false;
	}

	auto parse = [ & ]( const std::string& data, nlohmann::json& json )
	{
		json = nlohmann::json::parse( data.c_str( ), nullptr, false );
		if ( json == nlohmann::detail::value_t::discarded ) {
			//printf( hash_str( "json could not be parsed, sorry!\n" ) );
			return false;
		}
	};

	auto parse_class_info = [ & ]( nlohmann::json& data )
	{
		for ( auto& json : data )
		{
			auto it = json.begin( );
			const std::string className = it.key( );
			for ( auto& innerJson : it.value( ) )
			{
				it = innerJson.begin( );
				if ( it.key( ) == hash_str( "__MDKClassSize" ) )
					continue;

				if ( it.key( ) == hash_str( "__InheritInfo" ) )
					continue;

				content_info info;
				info.pointer = static_cast< int >( it.value( )[ 1 ] );
				info.size = static_cast< int >( it.value( )[ 2 ] );

				if ( fileVersion == 10201 )
					info.is_bit = it.value( ).size( ) == 4;
				else if ( fileVersion == 10202 )
					info.is_bit = it.value( ).size( ) == 5;
				info.valid = true;
				if ( info.is_bit )
				{
					if ( fileVersion == 10201 )
					{
						info.bit_offset = static_cast< int >( it.value( )[ 3 ] );
						content_map.insert( std::pair( className + it.key( ).substr( 0, it.key( ).length( ) - 4 ), info ) );
					}

					else if ( fileVersion == 10202 )
					{
						info.bit_offset = static_cast< int >( it.value( )[ 4 ] );
						content_map.insert( std::pair( className + it.key( ), info ) );
					}
				}
				else
					content_map.insert( std::pair( className + it.key( ), info ) );
			}
		}
	};


	parse( load_gzip( website + engine_path + hash_str( "/" ) + location_path + hash_str( "/ClassesInfo.json.gz" ) ), json_class_list );

	fileVersion = json_class_list.value( hash_str( "version" ), 0 );

	parse_class_info( json_class_list[ hash_str( "data" ) ] );

	parse( load_gzip( website + engine_path + hash_str( "/" ) + location_path + hash_str( "/StructsInfo.json.gz" ) ), json_struct_list );

	fileVersion = json_struct_list.value( hash_str( "version" ), 0 );

	parse_class_info( json_struct_list[ hash_str( "data" ) ] );

	curl_easy_cleanup( m_curl );

	return true;
}


auto sync::c_sync::get_offset( 
	const std::string& class_name, 
	const std::string& member_name,
	const std::string& redacted_name, 
	const std::uint64_t addition ) -> std::uintptr_t {

	const auto it = content_map.find( class_name + member_name );
	if ( it == content_map.end( ) ) return std::uintptr_t( );

	auto offset_ptr = it->second.pointer + addition;
	//m_log->set_text_point( hash_str( "Successfully found %s::%s -> 0x%llx" ), class_name.c_str( ), addition ? redacted_name.c_str( ) : member_name.c_str( ), offset_ptr );
	return offset_ptr;
}