#include "../client.h"

void command_lootsim(Client *c, const Seperator *sep)
{
	int arguments = sep->argnum;
	if (arguments < 3 || !sep->IsNumber(1) || !sep->IsNumber(2) || !sep->IsNumber(3)) {
		c->Message(Chat::White, "Usage: #lootsim [npc_type_id] [loottable_id] [iterations] [loot_log_enabled=0]");
		return;
	}

	auto npc_id       = std::stoul(sep->arg[1]);
	auto loottable_id = std::stoul(sep->arg[2]);
	auto iterations   = std::stoul(sep->arg[3]) > 1000 ? 1000 : std::stoul(sep->arg[3]);
	auto log_enabled  = arguments > 3 ? std::stoul(sep->arg[4]) : false;

	// temporarily disable loot logging unless set explicitly
	LogSys.log_settings[Logs::Loot].log_to_console = log_enabled ? LogSys.log_settings[Logs::Loot].log_to_console : 0;
	LogSys.log_settings[Logs::Loot].log_to_file    = log_enabled ? LogSys.log_settings[Logs::Loot].log_to_file : 0;
	LogSys.log_settings[Logs::Loot].log_to_gmsay   = log_enabled ? LogSys.log_settings[Logs::Loot].log_to_gmsay : 0;

	auto npc_type = content_db.LoadNPCTypesData(npc_id);
	if (npc_type) {
		auto npc = new NPC(npc_type, nullptr, c->GetPosition(), GravityBehavior::Water);
		if (npc) {

			BenchTimer benchmark;

			npc->SetRecordLootStats(true);
			for (int i = 0; i < iterations; i++) {
				npc->AddLootTable(loottable_id);

				for (auto &id: zone->GetGlobalLootTables(npc)) {
					npc->AddLootTable(id);
				}
			}

			entity_list.AddNPC(npc);

			c->SendChatLineBreak();
			c->Message(
				Chat::White,
				fmt::format(
					"# [Loot Simulator] NPC [{}] ({}) Loot Table ID [{}] Dropped Items [{}] iterations [{}]",
					npc->GetCleanName(),
					npc_id,
					loottable_id,
					npc->GetRolledItems().size(),
					iterations
				).c_str()
			);
			c->SendChatLineBreak();

			// npc level loot table
			auto loot_table = database.GetLootTable(loottable_id);
			if (!loot_table) {
				c->Message(Chat::Red, "Loot table not found");
				return;
			}

			for (uint32 i = 0; i < loot_table->NumEntries; i++) {
				auto le = loot_table->Entries[i];

				c->Message(
					Chat::White,
					fmt::format(
						"# Lootdrop ID [{}] drop_limit [{}] min_drop [{}] mult [{}] probability [{}]",
						le.lootdrop_id,
						le.droplimit,
						le.mindrop,
						le.multiplier,
						le.probability
					).c_str()
				);

				auto loot_drop = database.GetLootDrop(le.lootdrop_id);
				if (!loot_drop) {
					continue;
				}

				for (uint32 ei = 0; ei < loot_drop->NumEntries; ei++) {
					auto               e            = loot_drop->Entries[ei];
					int                rolled_count = npc->GetRolledItemCount(e.item_id);
					const EQ::ItemData *item        = database.GetItem(e.item_id);

					EQ::SayLinkEngine linker;
					linker.SetLinkType(EQ::saylink::SayLinkItemData);
					linker.SetItemData(item);

					auto rolled_percentage = (float) ((float) ((float) rolled_count / (float) iterations) * 100);

					c->Message(
						Chat::White,
						fmt::format(
							"-- [{}] item_id [{}] chance [{}] rolled_count [{}] ({:.2f}%) name [{}]",
							ei,
							e.item_id,
							e.chance,
							rolled_count,
							rolled_percentage,
							linker.GenerateLink()
						).c_str()
					);
				}
			}


			// global loot
			auto tables = zone->GetGlobalLootTables(npc);
			if (!tables.empty()) {
				c->SendChatLineBreak();
				c->Message(Chat::White, "# [Loot Simulator] Global Loot");
			}

			for (auto &id: tables) {
				c->SendChatLineBreak();
				c->Message(Chat::White, fmt::format("# Global Loot Table ID [{}]", id).c_str());
				c->SendChatLineBreak();

				loot_table = database.GetLootTable(id);
				if (!loot_table) {
					c->Message(Chat::Red, fmt::format("Global Loot table not found [{}]", id).c_str());
					continue;
				}

				for (uint32 i = 0; i < loot_table->NumEntries; i++) {
					auto le = loot_table->Entries[i];

					c->Message(
						Chat::White,
						fmt::format(
							"# Lootdrop ID [{}] drop_limit [{}] min_drop [{}] mult [{}] probability [{}]",
							le.lootdrop_id,
							le.droplimit,
							le.mindrop,
							le.multiplier,
							le.probability
						).c_str()
					);

					auto loot_drop = database.GetLootDrop(le.lootdrop_id);
					if (!loot_drop) {
						continue;
					}

					for (uint32 ei = 0; ei < loot_drop->NumEntries; ei++) {
						auto               e            = loot_drop->Entries[ei];
						int                rolled_count = npc->GetRolledItemCount(e.item_id);
						const EQ::ItemData *item        = database.GetItem(e.item_id);

						EQ::SayLinkEngine linker;
						linker.SetLinkType(EQ::saylink::SayLinkItemData);
						linker.SetItemData(item);

						auto rolled_percentage = (float) ((float) ((float) rolled_count / (float) iterations) * 100);

						c->Message(
							Chat::White,
							fmt::format(
								"-- [{}] item_id [{}] chance [{}] rolled_count [{}] ({:.2f}%) name [{}]",
								ei,
								e.item_id,
								e.chance,
								rolled_count,
								rolled_percentage,
								linker.GenerateLink()
							).c_str()
						);
					}
				}
			}


			c->SendChatLineBreak();
			c->Message(
				Chat::White,
				fmt::format(
					"# Global Loot Benchmark End [{}] iterations took [{}](s)",
					iterations,
					benchmark.elapsed()
				).c_str()
			);
			c->SendChatLineBreak();

			LogSys.LoadLogDatabaseSettings();
		}
	}
	else {
		c->Message(
			Chat::White,
			fmt::format(
				"Failed to spawn NPC ID {}.",
				npc_id
			).c_str()
		);
	}
}


