// Emacs style mode select   -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id: d730b4fedeffdd5f127b7a5cf936c5c822d9ca0b $
//
// Copyright (C) 1998-2006 by Randy Heit (ZDoom).
// Copyright (C) 2006-2026 by The Odamex Team.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// DESCRIPTION:
//  "Horde" game mode defines and recipes.
//
//-----------------------------------------------------------------------------

#pragma once

#include <vector>

#include "d_player.h"
#include "info.h"

struct hordeRecipe_t
{
	mobjtype_t type;
	int count;
	int limit;
	int totalCount;
	bool isBoss;

	hordeRecipe_t() : type(MT_NULL), count(0), limit(0), totalCount(0), isBoss(false) { }

	hordeRecipe_t& operator=(const hordeRecipe_t& other)
	{
		if (this == &other)
			return *this;

		type = other.type;
		count = other.count;
		limit = other.limit;
		totalCount = other.totalCount;
		isBoss = other.isBoss;

		return *this;
	}

	void clear()
	{
		type = MT_NULL;
		count = 0;
		limit = 0;
		totalCount = 0;
		isBoss = false;
	}

	bool isValid() const { return type != MT_NULL && count > 0; }
};

struct hordeDefine_t
{
	struct powConfig_t
	{
		float chance;
		powConfig_t() : chance(1.0f) { }
	};

	struct powerup_t
	{
		mobjtype_t mobj;
		powConfig_t config;
	};

	enum waveMonsterType_e
	{
		RM_NULL,
		RM_NORMAL,
		RM_BOSS,
	};

	struct monConfig_t
	{
		int minGroupHealth;
		int maxGroupHealth;
		int limit;
		float chance;
		monConfig_t()
		    : minGroupHealth(-1), maxGroupHealth(-1), limit(-1), chance(1.0f) { }
	};

	struct monster_t
	{
		hordeDefine_t::waveMonsterType_e monster;
		mobjtype_t mobj;
		monConfig_t config;
	};

	typedef std::vector<weapontype_t> weapons_t;
	typedef std::vector<ammotype_t> ammos_t;
	typedef std::vector<powerup_t> powerups_t;
	typedef std::vector<monster_t> monsters_t;

	std::string name;    // Name of the wave.
	weapons_t weapons;   // Weapons we can spawn this wave.
	ammos_t ammos;       // Ammos we can replenish this wave.
	monsters_t monsters; // Monsters we can spawn this wave.
	powerups_t powerups; // Powerups we can spawn this wave.
	int minGroupHealth;  // Minimum health of a group of monsters to spawn.
	int maxGroupHealth;  // Maximum health of a group of monsters to spawn.
	int minBossHealth;   // Minimum health of a group of bosses to spawn.
	int maxBossHealth;   // Maximum health of a group of bosses to spawn.

	hordeDefine_t()
	    : minGroupHealth(-1), maxGroupHealth(-1), minBossHealth(-1),
	      maxBossHealth(-1)
	{
	}

	void addPowerup(const mobjtype_t mobj, const powConfig_t& config);
	const powerup_t& randomPowerup() const;
	void addMonster(const waveMonsterType_e monster, const mobjtype_t mobj,
	                const monConfig_t& config);
	int minTotalHealth() const;
	int maxTotalHealth() const;
	int goalHealth() const;
	const char* difficulty(const bool colored) const;
	StringTokens weaponStrings(player_t* player) const;
};

template <>
struct hashfunc<mobjtype_t>
{
	unsigned int operator()(mobjtype_t val) const
	{
		if (sizeof(mobjtype_t) == 8)
			return __hash_jenkins_64bit(val);
		else
			return __hash_jenkins_32bit(val);
	}
};

typedef OHashTable<int32_t, int> mobjCounts_t;

void G_ParseHordeDefs();
const hordeDefine_t& G_HordeDefine(size_t id);

int P_HordeMobjCount(const mobjCounts_t& counts, const mobjtype_t type);
size_t P_HordePickDefine(const int current, const int total);
bool P_HordeSpawnRecipe(hordeRecipe_t& out, const hordeDefine_t& define,
                        const bool wantBoss, const mobjCounts_t& monsterCounts);
bool P_HordeDefineNamed(size_t& out, const std::string& name);
