#pragma once

// must be called at least once to initialize, or when modelindex is changed
void animdecide_load_if_needed(entity e);

// client side frame inferring
void animdecide_setimplicitstate(entity e, float onground);
void animdecide_setframes(entity e, bool support_blending, .int fld_frame, .int fld_frame1time, .int fld_frame2, .int fld_frame2time);

CLASS(ModelAnimation, Object)
	ATTRIB(ModelAnimation, m_framenames, string);
	STATIC_METHOD(ModelAnimation, getframe, int(ModelAnimation this, int mdlidx))
	{
		FOREACH_WORD(this.m_framenames, true, {
			int f = frameforname(mdlidx, it);
			if (f != -1) return f;
		});
#ifdef CSQC
		LOG_DEBUGF("Missing animation for %s: %s", modelnameforindex(mdlidx), this.registered_id);
#endif
		return -1;
	}
ENDCLASS(ModelAnimation)

REGISTRY(Animations, BITS(8))
REGISTER_REGISTRY(Animations)

REGISTRY_DEFINE_GET(Animations, NULL)
#define WriteAnimation(to, it) WriteRegistered(Animations, to, it)
#define ReadAnimation() ReadRegistered(Animations)
#define REGISTER_ANIMATION(id, framenames) \
	.vector anim_##id; \
	REGISTER(Animations, ANIM_##id, m_id, NEW(ModelAnimation)) { \
		this.m_framenames = framenames; \
	}

vector anim_vec(ModelAnimation anim, int mdlidx, int numframes, float framerate)
{
	vector v;
	v.x = ModelAnimation_getframe(anim, mdlidx);
	v.y = numframes;
	v.z = framerate;
	return v;
}

// player animation data for this model
// each vector is as follows:
// _x = startframe
// _y = numframes
// _z = framerate
/** player dies */
REGISTER_ANIMATION(die1, "dieone groupified_0_anim");
/** player dies differently */
REGISTER_ANIMATION(die2, "dietwo groupified_1_anim");
/** player pulls out a weapon */
REGISTER_ANIMATION(draw, "draw groupified_2_anim");
REGISTER_ANIMATION(duck, "duck groupified_3_anim");
/** player walking while crouching */
REGISTER_ANIMATION(duckwalk, "duckwalk groupified_4_anim");
/** player jumping from a crouch */
REGISTER_ANIMATION(duckjump, "duckjump groupified_5_anim");
/** player idling while crouching */
REGISTER_ANIMATION(duckidle, "duckidle groupified_6_anim");
/** player standing */
REGISTER_ANIMATION(idle, "idle groupified_7_anim");
/** player jump */
REGISTER_ANIMATION(jump, "jump groupified_8_anim");
/** player flinches from pain */
REGISTER_ANIMATION(pain1, "painone groupified_9_anim");
/** player flinches from pain, differently */
REGISTER_ANIMATION(pain2, "paintwo groupified_10_anim");
/** player shoots */
REGISTER_ANIMATION(shoot, "shoot groupified_11_anim");
/** player taunts others (FIXME: no code references this) */
REGISTER_ANIMATION(taunt, "taunt groupified_12_anim");
/** player running forward */
REGISTER_ANIMATION(run, "run groupified_13_anim");
/** player running backward */
REGISTER_ANIMATION(runbackwards, "runbackwards groupified_14_anim");
/** player shuffling left quickly */
REGISTER_ANIMATION(strafeleft, "strafeleft groupified_15_anim");
/** player shuffling right quickly */
REGISTER_ANIMATION(straferight, "straferight groupified_16_anim");
/**  */
REGISTER_ANIMATION(dead1, "deadone groupified_17_anim");
/**  */
REGISTER_ANIMATION(dead2, "deadtwo groupified_18_anim");
/** player running forward and right */
REGISTER_ANIMATION(forwardright, "forwardright groupified_19_anim");
/** player running forward and left */
REGISTER_ANIMATION(forwardleft, "forwardleft groupified_20_anim");
/** player running backward and right */
REGISTER_ANIMATION(backright, "backright groupified_21_anim");
/** player running back and left */
REGISTER_ANIMATION(backleft, "backleft groupified_22_anim");
/** player doing the melee action */
REGISTER_ANIMATION(melee, "melee groupified_23_anim");
REGISTER_ANIMATION(duckwalkbackwards, "duckwalkbackwards groupified_24_anim");
REGISTER_ANIMATION(duckwalkstrafeleft, "duckwalkstrafeleft duckstrafeleft groupified_25_anim");
REGISTER_ANIMATION(duckwalkstraferight, "duckwalkstraferight duckstraferight groupified_26_anim");
REGISTER_ANIMATION(duckwalkforwardright, "duckwalkforwardright duckforwardright groupified_27_anim");
REGISTER_ANIMATION(duckwalkforwardleft, "duckwalkforwardleft groupified_28_anim");
REGISTER_ANIMATION(duckwalkbackright, "duckwalkbackright duckbackwardright groupified_29_anim");
REGISTER_ANIMATION(duckwalkbackleft, "duckwalkbackleft duckbackwardleft groupified_30_anim");
.float animdecide_modelindex;

// please network this one
.int anim_state;
.float anim_time;
.float anim_lower_action;
.float anim_lower_time;
.float anim_upper_action;
.float anim_upper_time;

// when copying entities, copy these too
.int anim_implicit_state;
.float anim_implicit_time;
.float anim_lower_implicit_action;
.float anim_lower_implicit_time;
.float anim_upper_implicit_action;
.float anim_upper_implicit_time;

// explicit anim states (networked)
void animdecide_setstate(entity e, int newstate, float restart);
const int ANIMSTATE_DEAD1 = BIT(0); // base frames: die1
const int ANIMSTATE_DEAD2 = BIT(1); // base frames: die2
const int ANIMSTATE_DUCK = BIT(2); // turns walk into duckwalk, jump into duckjump, etc.
const int ANIMSTATE_FROZEN = BIT(3); // force idle
const int ANIMSTATE_FOLLOW = BIT(4); // also force idle

// implicit anim states (inferred from velocity, etc.)
const int ANIMIMPLICITSTATE_INAIR = BIT(0);
const int ANIMIMPLICITSTATE_FORWARD = BIT(1);
const int ANIMIMPLICITSTATE_BACKWARDS = BIT(2);
const int ANIMIMPLICITSTATE_LEFT = BIT(3);
const int ANIMIMPLICITSTATE_RIGHT = BIT(4);
const int ANIMIMPLICITSTATE_JUMPRELEASED = BIT(5);

// explicit actions (networked); negative values are for lower body
void animdecide_setaction(entity e, float action, float restart);
const int ANIMACTION_JUMP = -1; // jump
const int ANIMACTION_DRAW = 1; // draw
const int ANIMACTION_PAIN1 = 2; // pain
const int ANIMACTION_PAIN2 = 3; // pain
const int ANIMACTION_SHOOT = 4; // shoot
const int ANIMACTION_TAUNT = 5; // taunt
const int ANIMACTION_MELEE = 6; // melee
