#pragma once

#define MAX_SCORE 64

#define REGISTER_SP(id) REGISTER(Scores, SP, id, m_id, new_pure(PlayerScoreField))
REGISTRY(Scores, MAX_SCORE)
REGISTER_REGISTRY(Scores)
// do not sort alphabetically, player sort priority is based on score registration order
//REGISTRY_SORT(Scores)
REGISTRY_CHECK(Scores)

REGISTRY_DEFINE_GET(Scores, NULL)
STATIC_INIT(Scores_renumber) { FOREACH(Scores, true, it.m_id = i); }

/*
 * Score indices
 */
#ifdef GAMEQC
// networked fields
// NOTE: score registration order is used as player sort priority (after primary and secondary)

// TODO: move gametype scores to gametype files
// TODO: allow gametypes to fully customize player sorting priority, even the common ones

REGISTER_SP(RACE_LAPS);
REGISTER_SP(RACE_TIME);
REGISTER_SP(RACE_FASTEST);

REGISTER_SP(ASSAULT_OBJECTIVES);

REGISTER_SP(CTF_CAPS);
REGISTER_SP(CTF_FCKILLS);
REGISTER_SP(CTF_RETURNS);
REGISTER_SP(CTF_DROPS);
REGISTER_SP(CTF_PICKUPS);
REGISTER_SP(CTF_CAPTIME);

REGISTER_SP(DOM_TAKES);
REGISTER_SP(DOM_TICKS);

REGISTER_SP(FREEZETAG_REVIVALS);

REGISTER_SP(KEEPAWAY_BCTIME);
REGISTER_SP(KEEPAWAY_CARRIERKILLS);
REGISTER_SP(KEEPAWAY_PICKUPS);

REGISTER_SP(KH_CAPS);
REGISTER_SP(KH_KCKILLS);
REGISTER_SP(KH_LOSSES);
REGISTER_SP(KH_DESTRUCTIONS);
REGISTER_SP(KH_PUSHES);
REGISTER_SP(KH_PICKUPS);

REGISTER_SP(LMS_RANK);
REGISTER_SP(LMS_LIVES);

REGISTER_SP(NEXBALL_GOALS);
REGISTER_SP(NEXBALL_FAULTS);

REGISTER_SP(ONS_CAPS);
REGISTER_SP(ONS_TAKES);

REGISTER_SP(TKA_PICKUPS);
REGISTER_SP(TKA_BCTIME);
REGISTER_SP(TKA_CARRIERKILLS);

REGISTER_SP(SURV_SURVIVALS);
REGISTER_SP(SURV_HUNTS);

REGISTER_SP(SCORE);
REGISTER_SP(KILLS);
REGISTER_SP(DEATHS);
REGISTER_SP(TEAMKILLS);
REGISTER_SP(SUICIDES);
REGISTER_SP(DMG);
REGISTER_SP(DMGTAKEN);

REGISTER_SP(ROUNDS_PL);

REGISTER_SP(SKILL); // not sortable
REGISTER_SP(FPS); // not sortable

// fields not networked via the score system
REGISTER_SP(END);

REGISTER_SP(PING);
REGISTER_SP(PL);
REGISTER_SP(NAME);
REGISTER_SP(SEPARATOR);

REGISTER_SP(KDRATIO); // kills / deaths
REGISTER_SP(SUM); // kills - deaths
REGISTER_SP(FRAGS); // kills - suicides
#endif


// the stuff you don't need to see

/**
 * Lower scores are better (e.g. suicides)
 */
const int SFL_LOWER_IS_BETTER = BIT(0);

/**
 * Don't show zero values as scores
 */
const int SFL_HIDE_ZERO = BIT(1);

/**
 * Allow a column to be hidden (do not automatically add it even if it is a sorting key)
 */
const int SFL_ALLOW_HIDE = BIT(4);

/**
 * Display as a rank (with st, nd, rd, th suffix)
 */
const int SFL_RANK = BIT(5);

/**
 * Display as mm:ss.s, value is stored as 10ths of a second (AND 0 is the worst possible value!)
 */
const int SFL_TIME = BIT(6);

const int SFL_NOT_SORTABLE = BIT(7); // don't sort by this field

// not an extra constant yet
#define SFL_ZERO_IS_WORST SFL_TIME

/**
 * Scoring priority (NOTE: PRIMARY is used for fraglimit)
 * NOTE: SFL_SORT_PRIO_SECONDARY value must be lower than SFL_SORT_PRIO_PRIMARY's
 */
const int SFL_SORT_PRIO_SECONDARY = BIT(2);
const int SFL_SORT_PRIO_PRIMARY = BIT(3);
const int SFL_SORT_PRIO_MASK = SFL_SORT_PRIO_PRIMARY | SFL_SORT_PRIO_SECONDARY;

#define IS_INCREASING(x) ( (x) & SFL_LOWER_IS_BETTER )
#define IS_DECREASING(x) ( !((x) & SFL_LOWER_IS_BETTER) )

USING(PlayerScoreField, entity);
.int _scores[MAX_SCORE];
.string m_name;
.int m_flags;

#define scores(this) _scores[(this).m_id]
#define scores_label(this) ((this).m_name)
#define scores_flags(this) ((this).m_flags)

#define MAX_TEAMSCORE 2
USING(ScoreTeam, string);
.int _teamscores[MAX_TEAMSCORE];
#define teamscores(i) _teamscores[i]
string _teamscores_label[MAX_TEAMSCORE];
#define teamscores_label(i) _teamscores_label[i]
int _teamscores_flags[MAX_TEAMSCORE];
#define teamscores_flags(i) _teamscores_flags[i]

const int ST_SCORE = 0;
