#pragma once

// negative = SVQC autochannels
// positive = one per entity

const int CH_INFO = 0;
const int CH_WEAPON_A = -1;
const int CH_WEAPON_B = -1;
const int CH_WEAPON_SINGLE = 1;
const int CH_VOICE = -2;
// const int CH_VOICE_SINGLE = 2;
const int CH_TRIGGER = -3;
const int CH_TRIGGER_SINGLE = 3;
const int CH_SHOTS = -4;
const int CH_SHOTS_SINGLE = 4;
// const int CH_TUBA = -5;
const int CH_TUBA_SINGLE = 5;
const int CH_PAIN = -6;
const int CH_PAIN_SINGLE = 6;
const int CH_PLAYER = -7;
const int CH_PLAYER_SINGLE = 7;
// const int CH_BGM_SINGLE = -8;
const int CH_BGM_SINGLE = 8;
const int CH_AMBIENT = -9;
const int CH_AMBIENT_SINGLE = 9;

const float ATTEN_NONE = 0;
const float ATTEN_MIN = 0.015625;
const float ATTEN_LOW = 0.2;
const float ATTEN_NORM = 0.5;
const float ATTEN_LARGE = 1;
const float ATTEN_IDLE = 2;
const float ATTEN_STATIC = 3;
const float ATTEN_MAX = 3.984375;

const float VOL_BASE = 0.7;
const float VOL_BASEVOICE = 1.0;
const float VOL_MUFFLED = 0.35;

// Play all sounds via sound7, for access to the extra channels.
// Otherwise, channels 8 to 15 would be blocked for a weird QW feature.
#ifdef SVQC
	#define _sound(e, c, s, v, a) \
		MACRO_BEGIN \
			entity __e = e; \
			if (sound_allowed(MSG_BROADCAST, __e)) \
				sound7(__e, c, s, v, a, 0, 0); \
		MACRO_END
#else
	#define _sound(e, c, s, v, a) sound7(e, c, s, v, a, 0, 0)
#endif
#define sound(e, c, s, v, a) _sound(e, c, Sound_fixpath(s), v, a)

/**
 * because sound7 didn't have origin
 *
 * @param e sound owner
 * @param o sound origin
 * @param chan sound channel
 * @param samp sound filename
 * @param vol sound volume
 * @param atten sound attenuation
 * @param speed
 * @param sf
 */
#define sound8(e, o, chan, samp, vol, atten, speed, sf) \
	MACRO_BEGIN \
		entity __e; \
		int __chan = chan; \
		string __samp = samp; \
		bool auto = false; \
		if (__chan > 0) __e = e; \
		else \
		{ \
			auto = true; \
			__chan = fabs(__chan); \
			entity tmp = __e = new(csqc_autochannel); \
			setthink(tmp, SUB_Remove); \
			tmp.nextthink = time + soundlength(__samp); \
		} \
		vector old_origin = __e.origin; \
		vector old_mins = __e.mins; \
		vector old_maxs = __e.maxs; \
		setorigin(__e, o); \
		setsize(__e, '0 0 0', '0 0 0'); \
		sound7(__e, __chan, __samp, vol, atten, speed, sf); \
		if (!auto) \
		{ \
			setorigin(__e, old_origin); \
			setsize(__e, old_mins, old_maxs); \
		} \
	MACRO_END

string _Sound_fixpath(string base)
{
	if (base == "") return string_null;
#ifdef SVQC
	return strcat(base, ".wav");  // let the client engine decide
#else
#define extensions(x) \
	x(wav) \
	x(ogg) \
	x(flac) \
	/**/
#define tryext(ext) { \
		string s = strcat(base, "." #ext); \
		if (fexists(strcat("sound/", s))) { \
			return s; \
		} \
	}
	extensions(tryext);
	LOG_WARNF("Missing sound: \"%s\"", strcat("sound/", base));
#undef tryext
#undef extensions
	return string_null;
#endif
}

CLASS(Sound, Object)
	ATTRIB(Sound, m_id, int, 0);
	ATTRIB(Sound, sound_str, string());
	ATTRIB(Sound, sound_str_, string);
	CONSTRUCTOR(Sound, string() path)
	{
		CONSTRUCT(Sound);
		this.sound_str = path;
	}
	METHOD(Sound, sound_precache, void(Sound this))
	{
		TC(Sound, this);
		string s = _Sound_fixpath(this.sound_str());
		if (!s) return;
		//profile(sprintf("precache_sound(\"%s\")", s));
		precache_sound(s);
		strcpy(this.sound_str_, s);
	}
ENDCLASS(Sound)

entity _Sound_fixpath_this;
string _Sound_fixpath_cached;
#define Sound_fixpath(this) ( \
	_Sound_fixpath_this = (this), \
	_Sound_fixpath_cached = _Sound_fixpath_this.sound_str_, \
	_Sound_fixpath_cached ? _Sound_fixpath_cached : _Sound_fixpath(_Sound_fixpath_this.sound_str()) \
)
