#pragma once

#include <common/items/_mod.qh>
#include <common/resources/resources.qh>
#include <common/stats.qh>

#ifdef SVQC
#include <common/effects/qc/_mod.qh>
#endif
#ifndef SVQC
#include <common/util.qh> // for icon_path_from_HUDskin
#endif

USING(WepSet, vector);

const int MAX_WEAPONSLOTS = 2;
.entity weaponentities[MAX_WEAPONSLOTS];

int weaponslot(.entity weaponentity)
{
	for (int i = 0; i < MAX_WEAPONSLOTS; ++i)
		if (weaponentities[i] == weaponentity)
			return i;
	return 0;
}

#ifdef GAMEQC
// weapon states (actor.(weaponentity).state)
/** no weapon selected */
const int WS_CLEAR  = 0;
/** raise frame */
const int WS_RAISE  = 1;
/** deselecting frame */
const int WS_DROP   = 2;
/** fire state */
const int WS_INUSE  = 3;
/** idle frame */
const int WS_READY  = 4;
#endif

/** fields which are explicitly/manually set are marked with "M", fields set automatically are marked with "A" */
CLASS(Weapon, Object)
	ATTRIB(Weapon, m_id, int, 0);
#ifdef SVQC
	/** the canonical spawnfunc name */
	ATTRIB(Weapon, m_canonical_spawnfunc, string);
	/** control what happens when this weapon is spawned */
	METHOD(Weapon, m_spawnfunc_hookreplace, Weapon(Weapon this, entity e))
	{
		return this;
	}
	/** M: rating    : bot weapon priority */
	ATTRIB(Weapon, bot_pickupbasevalue, float, 0);
#endif
	/** M: ammotype  : main ammo type */
	ATTRIB(Weapon, ammo_type, Resource, RES_NONE);
	/** M: impulse   : weapon impulse */
	ATTRIB(Weapon, impulse, int, -1);
	/** M: flags     : WEPSPAWNFLAG_... combined */
	ATTRIB(Weapon, spawnflags, int, 0);
	/** M: color     : waypointsprite color. previously called "wpcolor" */
	ATTRIB(Weapon, m_color, vector, '0 0 0');
#ifdef GAMEQC
	/** M: modelname : name of model (without g_ v_ or h_ prefixes) */
	ATTRIB(Weapon, mdl, string, "");
	/** M: model MDL_id_ITEM */
	ATTRIB(Weapon, m_model, entity);
	/** M: flash model MDL_id_MUZZLEFLASH */
	ATTRIB(Weapon, m_muzzlemodel, entity, MDL_Null);
	/** M: flash effect EFFECT_id_MUZZLEFLASH */
	ATTRIB(Weapon, m_muzzleeffect, entity);
#endif
#ifdef CSQC
	/** M: crosshair : per-weapon crosshair: "CrosshairImage Size" */
	ATTRIB(Weapon, w_crosshair, string, "gfx/crosshairmoustache");
	/** A: crosshair : per-weapon crosshair size (argument two of "crosshair" field) */
	ATTRIB(Weapon, w_crosshair_size, float, 1);
	/** A: reticle   : per-weapon zoom reticle */
	ATTRIB(Weapon, w_reticle, string, string_null);
	/** M: wepimg    : "weaponfoobar" side view image file of weapon. WEAPONTODO: Move out of skin files, move to common files */
#endif
	ATTRIB(Weapon, model2, string, "");
	/** M: refname   : reference name name */
	ATTRIB(Weapon, netname, string, "");
	/** M: wepname   : human readable name */
	ATTRIB(Weapon, m_name, string, "AOL CD Thrower");
	/** M: deprecated refname : old reference name for compatibility with weapons that were renamed */
	ATTRIB(Weapon, m_deprecated_netname, string, "");

#ifdef GAMEQC
	ATTRIB(Weapon, m_pickup, entity);
#endif

#ifdef SVQC
	/** (SERVER) setup weapon data */
	METHOD(Weapon, wr_setup, void(Weapon this, entity actor, .entity weaponentity)) { }
	/** (SERVER) logic to run every frame */
	METHOD(Weapon, wr_think, void(Weapon this, entity actor, .entity weaponentity, int fire)) { }
	/** (SERVER) checks ammo for weapon primary */
	METHOD(Weapon, wr_checkammo1, bool(Weapon this, entity actor, .entity weaponentity))
	{
		return false;
	}
	/** (SERVER) checks ammo for weapon second */
	METHOD(Weapon, wr_checkammo2, bool(Weapon this, entity actor, .entity weaponentity))
	{
		return false;
	}
	/** (SERVER) runs bot aiming code for this weapon */
	METHOD(Weapon, wr_aim, void(Weapon this, entity actor, .entity weaponentity)) { }
#endif
#ifdef GAMEQC
	/** (BOTH)   precaches models/sounds used by this weapon, also sets up weapon properties */
	METHOD(Weapon, wr_init, void(Weapon this)) { }
#endif
#ifdef SVQC
	/** (SERVER) notification number for suicide message (may inspect w_deathtype for details) */
	METHOD(Weapon, wr_suicidemessage, entity(Weapon this))
	{
		return NULL;
	}
	/** (SERVER) notification number for kill message (may inspect w_deathtype for details) */
	METHOD(Weapon, wr_killmessage, entity(Weapon this))
	{
		return NULL;
	}
	/** (SERVER) handles reloading for weapon */
	METHOD(Weapon, wr_reload, void(Weapon this, entity actor, .entity weaponentity)) { }
	/** (SERVER) clears fields that the weapon may use */
	METHOD(Weapon, wr_resetplayer, void(Weapon this, entity actor)) { }
	/** (CLIENT) impact effect for weapon explosion */
	METHOD(Weapon, wr_impacteffect, void(Weapon this, entity actor)) { }
	/** (SERVER) called whenever a player dies */
	METHOD(Weapon, wr_playerdeath, void(Weapon this, entity actor, .entity weaponentity)) { }
	/** (SERVER) logic to run when weapon is lost */
	METHOD(Weapon, wr_gonethink, void(Weapon this, entity actor, .entity weaponentity)) { }
	/** (SERVER) dump weapon cvars to config in data directory (see: sv_cmd dumpweapons) */
	METHOD(Weapon, wr_config, void(Weapon this)) { }
#endif
#ifdef GAMEQC
	/** (BOTH) weapon specific glow */
	METHOD(Weapon, wr_glow, vector(Weapon this, int actor_colors, entity wepent))
	{
		return '0 0 0';
	}
	/** (BOTH) weapon specific zoom reticle */
	METHOD(Weapon, wr_zoom, bool(Weapon this, entity actor))
	{
		// no weapon specific image for this weapon
		return false;
	}
#endif
#ifdef CSQC
	/** (CLIENT) check whether the weapon should zoom (special handling) */
	METHOD(Weapon, wr_zoomdir, bool(Weapon this))
	{
		return false;
	}
	/** (CLIENT) weapon specific view model */
	METHOD(Weapon, wr_viewmodel, string(Weapon this, entity wep))
	{
		return string_null;
	}
#endif
#ifdef SVQC
	/** (SERVER) the weapon is dropped */
	METHOD(Weapon, wr_drop, void(Weapon this, entity actor, .entity weaponentity)) { }
	/** (SERVER) a weapon is picked up */
	METHOD(Weapon, wr_pickup, void(Weapon this, entity actor, .entity weaponentity)) { }
	/** (SERVER) update cvar based properties */
	METHOD(Weapon, wr_update, void(Weapon this)) { }
#endif
#ifdef MENUQC
	METHOD(Weapon, describe, string(Weapon this))
	{
		TC(Weapon, this);
		return SUPER(Weapon).describe(this);
	}
#endif
#ifndef SVQC
	METHOD(Weapon, display, void(Weapon this, void(string name, string icon) returns))
	{
		returns(this.m_name, icon_path_from_HUDskin(this.model2));
	}
#endif
ENDCLASS(Weapon)

#ifdef SVQC

void weapon_defaultspawnfunc(entity this, Weapon e);
#define SPAWNFUNC_WEAPON(name, weapon) \
	spawnfunc(name) \
	{ \
		weapon_defaultspawnfunc(this, weapon); \
	}

#else

#define SPAWNFUNC_WEAPON(name, weapon)

#endif

#ifdef GAMEQC
CLASS(WeaponPickup, Pickup)
	ATTRIB(WeaponPickup, m_weapon, Weapon);
	ATTRIB(WeaponPickup, m_name, string);
	ATTRIB(WeaponPickup, m_sound, Sound, SND_WEAPONPICKUP);
#ifdef SVQC
	ATTRIB(WeaponPickup, m_itemflags, int, FL_WEAPON);
	float weapon_pickupevalfunc(entity player, entity item);
	ATTRIB(WeaponPickup, m_pickupevalfunc, float(entity player, entity item), weapon_pickupevalfunc);
#endif
	CONSTRUCTOR(WeaponPickup, Weapon w)
	{
		CONSTRUCT(WeaponPickup);
		this.m_weapon = w;
		this.m_name = w.m_name;
		this.m_model = w.m_model;
#ifdef SVQC
		this.m_botvalue = w.bot_pickupbasevalue;
#endif
	}
#ifdef SVQC
	METHOD(WeaponPickup, giveTo, bool(entity this, entity item, entity player))
	{
		bool b = Item_GiveTo(item, player);
		//if (b)
			//LOG_TRACEF("entity %i picked up %s", player, this.m_name);
		return b;
	}
#endif
ENDCLASS(WeaponPickup)

#endif

#ifdef SVQC
CLASS(OffhandWeapon, Object)
	METHOD(OffhandWeapon, offhand_think, void(OffhandWeapon this, entity player, bool key_pressed)) { }
ENDCLASS(OffhandWeapon)

.OffhandWeapon offhand;
#endif

#ifdef GAMEQC
int max_shot_distance = 32768; // determined by world mins/maxs when map loads
#endif

#ifdef CSQC
int autocvar_cl_tracers_teamcolor;
#endif

// weapon flags
const int WEP_TYPE_OTHER           = BIT(0);  // not for damaging people
const int WEP_TYPE_SPLASH          = BIT(1);  // splash damage
const int WEP_TYPE_HITSCAN         = BIT(2);  // hitscan
const int WEP_FLAG_CANCLIMB        = BIT(3);  // can be used for movement
const int WEP_FLAG_NORMAL          = BIT(4);  // in "most weapons" set
const int WEP_FLAG_HIDDEN          = BIT(5);  // hides from menu
const int WEP_FLAG_RELOADABLE      = BIT(6);  // can has reload
const int WEP_FLAG_SUPERWEAPON     = BIT(7);  // powerup timer
const int WEP_FLAG_MUTATORBLOCKED  = BIT(8);  // hides from impulse 99 etc. (mutators are allowed to clear this flag)
const int WEP_TYPE_MELEE_PRI       = BIT(9);  // primary attack is melee swing (for animation)
const int WEP_TYPE_MELEE_SEC       = BIT(10); // secondary attack is melee swing (for animation)
const int WEP_FLAG_DUALWIELD       = BIT(11); // weapon can be dual wielded
const int WEP_FLAG_NODUAL          = BIT(12); // weapon doesn't work well with dual wielding (fireball etc just explode on fire), doesn't currently prevent anything
const int WEP_FLAG_PENETRATEWALLS  = BIT(13); // weapon has high calibur bullets that can penetrate thick walls (WEAPONTODO)
const int WEP_FLAG_BLEED           = BIT(14); // weapon pierces and causes bleeding (used for damage effects)
const int WEP_FLAG_NOTRUEAIM       = BIT(15); // weapon doesn't aim directly at targets
const int WEP_FLAG_SPECIALATTACK   = BIT(16); // special attack (not a true weapon); NOTE: if this flag is set then WEP_FLAG_HIDDEN must be set as well

// variables:
string weaponorder_byid;
