/*
 * Radio-related code for Action (formerly Axshun)
 * 
 * -Fireblade
 */

#include "g_local.h"

void Cmd_Say_f (edict_t *ent, qboolean team, qboolean arg0, qboolean partner_msg);

// Each of the possible radio messages and their length

radio_msg_t male_radio_msgs[] = {
        { "1", 6 },
        { "2", 6 },
        { "3", 8 },
        { "4", 7 },
        { "5", 8 },
        { "6", 9 },
        { "7", 8 },
        { "8", 7 },
        { "9", 7 },
        { "10", 6 },
        { "back", 6 },
        { "cover", 7 },
        { "down", 13 },
        { "enemyd", 10 },
        { "enemys", 9 },
        { "forward", 6 },
        { "go", 6 },
        { "im_hit", 7 },
        { "left", 7 },
        { "reportin", 9 }, 
        { "right", 6 },
        { "taking_f", 22 },
        { "teamdown", 13 },
        { "treport", 12 },
        { "up", 4 },
        { "END", 0 }, // end of list delimiter
};

radio_msg_t female_radio_msgs[] = {
        { "1", 5 },
        { "2", 5 },
        { "3", 5 },
        { "4", 5 },
        { "5", 5 },
        { "6", 8 },
        { "7", 7 },
        { "8", 5 },
        { "9", 5 },
        { "10", 5 },
        { "back", 6 },
        { "cover", 5 },
        { "down", 6 },
        { "enemyd", 9 },
        { "enemys", 9 },
        { "forward", 8 },
        { "go", 6 },
        { "im_hit", 7 },
        { "left", 8 },
        { "reportin", 9 }, 
        { "right", 5 },
        { "taking_f", 22 },
        { "teamdown", 10 },
        { "treport", 12 },
        { "up", 6 },
        { "END", 0 }, // end of list delimiter
};

void PrecacheRadioMsgSet(radio_msg_t *msgs, char *base_path)
{
        char msg_fullpath[2048];

        while (strcmp(msgs->msg, "END"))
        {
                sprintf(msg_fullpath, "%s%s.wav", base_path, msgs->msg);
                gi.soundindex(msg_fullpath);
                msgs++;
        }
}

void PrecacheRadioSounds()
{
        gi.soundindex(RADIO_CLICK);
        gi.soundindex(RADIO_DEATH_MALE);
        gi.soundindex(RADIO_DEATH_FEMALE);

        PrecacheRadioMsgSet(male_radio_msgs, "radio/male/");
        PrecacheRadioMsgSet(female_radio_msgs, "radio/female/");
}

void DeleteFirstRadioQueueEntry(edict_t *ent)
{
        int i;

        if (ent->client->resp.radio_queue_size <= 0)
        {
                gi.dprintf("DeleteFirstRadioQueueEntry: attempt to delete without any entries\n");
                return;
        }

        for (i = 1; i < ent->client->resp.radio_queue_size; i++)
        {
                memcpy(&(ent->client->resp.radio_queue[i - 1]),
                        &(ent->client->resp.radio_queue[i]),
                        sizeof(radio_queue_entry_t));
        }
        
        ent->client->resp.radio_queue_size--;
}

void DeleteRadioQueueEntry(edict_t *ent, int entry_num)
{
        int i;

        if (ent->client->resp.radio_queue_size <= entry_num)
        {
                gi.dprintf("DeleteRadioQueueEntry: attempt to delete out of range queue entry\n");
                return;
        }

        for (i = entry_num + 1; i < ent->client->resp.radio_queue_size; i++)
        {
                memcpy(&(ent->client->resp.radio_queue[i - 1]),
                        &(ent->client->resp.radio_queue[i]),
                        sizeof(radio_queue_entry_t));
        }
        
        ent->client->resp.radio_queue_size--;
}

// RadioThink should be called once on each player per server frame.
void RadioThink(edict_t *ent)
{
        // Try to clean things up, a bit....
        if (ent->client->resp.radio_partner)
        {       
                if (!ent->client->resp.radio_partner->inuse ||
                        ent->client->resp.radio_partner->client->resp.radio_partner != ent)
                {
                        ent->client->resp.radio_partner = NULL;
                }
        }
        if (ent->client->resp.partner_last_offered_to)
        {
                if (!ent->client->resp.partner_last_offered_to->inuse ||
                        ent->client->resp.partner_last_offered_to->solid == SOLID_NOT)
                {
                        ent->client->resp.partner_last_offered_to = NULL;
                }
        }
        if (ent->client->resp.partner_last_denied_from)
        {
                if (!ent->client->resp.partner_last_denied_from->inuse ||
                        ent->client->resp.partner_last_denied_from->solid == SOLID_NOT)
                {
                        ent->client->resp.partner_last_denied_from = NULL;
                }
        }
        // ................................

        if (ent->client->resp.radio_power_off)
        {
                ent->client->resp.radio_queue_size = 0;
                return;
        }

        if (ent->client->resp.radio_delay > 1)
        {
                ent->client->resp.radio_delay--;
                return;
        }
                else if (ent->client->resp.radio_delay == 1)
        {
                DeleteFirstRadioQueueEntry(ent);
                ent->client->resp.radio_delay = 0;
        }

        if (ent->client->resp.radio_queue_size)
        {
                char snd_play_cmd[512];
                edict_t *from;
                int check;

                from = ent->client->resp.radio_queue[0].from_player;

                if (!ent->client->resp.radio_queue[0].click &&
                        (!from->inuse || from->solid == SOLID_NOT || from->deadflag == DEAD_DEAD))
                {
                        if (ent->client->resp.radio_queue[0].from_gender)
                        {
                                strcpy(ent->client->resp.radio_queue[0].soundfile, RADIO_DEATH_FEMALE);
                                ent->client->resp.radio_queue[0].length = RADIO_DEATH_FEMALE_LEN;
                        }
                                else
                        {
                                strcpy(ent->client->resp.radio_queue[0].soundfile, RADIO_DEATH_MALE);
                                ent->client->resp.radio_queue[0].length = RADIO_DEATH_MALE_LEN;
                        }

                        for (check = 1; check < ent->client->resp.radio_queue_size; check++)
                        {
                                if (ent->client->resp.radio_queue[check].from_player == from)
                                {
                                        DeleteRadioQueueEntry(ent, check);
                                        check--;
                                }
                        }
                }                       

                sprintf(snd_play_cmd, "play %s", ent->client->resp.radio_queue[0].soundfile);
                stuffcmd(ent, snd_play_cmd);
                ent->client->resp.radio_queue[0].now_playing = 1;
                ent->client->resp.radio_delay = ent->client->resp.radio_queue[0].length;
        }
}

int TotalNonClickMessagesInQueue(edict_t *ent)
{
        int i, count = 0;

        for (i = 0; i < ent->client->resp.radio_queue_size; i++)
        {
                if (!ent->client->resp.radio_queue[i].click)
                        count++;
        }
        
        return count;
}

void AppendRadioMsgToQueue(edict_t *ent, char *msg, int len, int click, edict_t *from_player)
{
        radio_queue_entry_t *newentry;

        if (ent->client->resp.radio_queue_size >= MAX_RADIO_QUEUE_SIZE)
        {
                gi.dprintf("AppendRadioMsgToQueue: Maximum radio queue size exceeded\n");
                return;
        }

        newentry = &(ent->client->resp.radio_queue[ent->client->resp.radio_queue_size]);

        if (strlen(msg) + 1 > MAX_SOUNDFILE_PATH_LEN)
        {
                gi.dprintf("Radio sound file path (%s) exceeded maximum length\n", msg);
                *(msg + MAX_SOUNDFILE_PATH_LEN - 1) = 0;
        }
        strcpy(newentry->soundfile, msg);
        newentry->from_player = from_player;
        newentry->from_gender = from_player->client->resp.radio_gender;
        newentry->now_playing = 0;
        newentry->length = len;
        newentry->click = click;

        ent->client->resp.radio_queue_size++;
}

void InsertRadioMsgInQueueBeforeClick(edict_t *ent, char *msg, int len, edict_t *from_player)
{
        radio_queue_entry_t *newentry;

        if (ent->client->resp.radio_queue_size >= MAX_RADIO_QUEUE_SIZE)
        {
                gi.dprintf("InsertRadioMsgInQueueBeforeClick: Maximum radio queue size exceeded\n");
                return;
        }

        memcpy(&(ent->client->resp.radio_queue[ent->client->resp.radio_queue_size]),
                &(ent->client->resp.radio_queue[ent->client->resp.radio_queue_size - 1]),
                sizeof(radio_queue_entry_t));

        newentry = &(ent->client->resp.radio_queue[ent->client->resp.radio_queue_size - 1]);

        if (strlen(msg) + 1 > MAX_SOUNDFILE_PATH_LEN)
        {
                gi.dprintf("Radio sound file path (%s) exceeded maximum length\n", msg);
                *(msg + MAX_SOUNDFILE_PATH_LEN - 1) = 0;
        }
        strcpy(newentry->soundfile, msg);
        newentry->from_player = from_player;
        newentry->from_gender = from_player->client->resp.radio_gender;
        newentry->now_playing = 0;
        newentry->length = len;
        newentry->click = 0;

        ent->client->resp.radio_queue_size++;
}

void AddRadioMsg(edict_t *ent, char *msg, int len, edict_t *from_player)
{
        if (ent->client->resp.radio_queue_size == 0 ||
            (ent->client->resp.radio_queue[0].click &&
             ent->client->resp.radio_queue_size == 1))
        {
                AppendRadioMsgToQueue(ent, RADIO_CLICK, RADIO_CLICK_LEN, 1, from_player);
                AppendRadioMsgToQueue(ent, msg, len, 0, from_player);
                AppendRadioMsgToQueue(ent, RADIO_CLICK, RADIO_CLICK_LEN, 1, from_player);
        }
                else // we have some msgs in it already...
        {
                if (TotalNonClickMessagesInQueue(ent) < MAX_RADIO_MSG_QUEUE_SIZE)
                        InsertRadioMsgInQueueBeforeClick(ent, msg, len, from_player);
                        // else ignore the message...
        }
}

void RadioBroadcast(edict_t *ent, int partner, char *msg)
{
        int j, i, msg_len, found;
        edict_t *other;
        radio_msg_t *radio_msgs;
        char msg_fullpath[2048], *base_path;

        if (ent->deadflag == DEAD_DEAD || ent->solid == SOLID_NOT)
                return; 

        if (!teamplay->value)
        {
                if (!((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS)))
                        return;  // don't allow in a non-team setup...
        }

        if (ent->client->resp.radio_power_off)
        {
                gi.centerprintf(ent, "Your radio is off!\n");
                return;
        }

        if (partner && ent->client->resp.radio_partner == NULL)
        {
                gi.cprintf(ent, PRINT_HIGH, "You don't have a partner.\n");
                return;
        }

        if (ent->client->resp.radio_gender)
        {
                radio_msgs = female_radio_msgs;
                base_path = "radio/female/";
        }
                else
        {
                radio_msgs = male_radio_msgs;
                base_path = "radio/male/";
        }

        i = found = 0;
        while (strcmp(radio_msgs[i].msg, "END"))
        {
                if (!Q_stricmp(radio_msgs[i].msg, msg))
                {
                        found = 1;
                        sprintf(msg_fullpath, "%s%s.wav", base_path, radio_msgs[i].msg);
                        msg_len = radio_msgs[i].length;
                        break;
                }
                i++;
        }
        
        if (!found)
        {       
                gi.centerprintf(ent, "'%s' is not a valid radio message\n", msg);
                return;
        }

        if (radiolog->value)
        {
                gi.cprintf(NULL, PRINT_CHAT, "[%s RADIO] %s: %s\n",
                        partner ? "PARTNER" : "TEAM",
                        ent->client->pers.netname,
                        msg);
        }

        for (j = 1; j <= game.maxclients; j++)
        {
                other = &g_edicts[j];
                if (!other->inuse)
                        continue;
                if (!other->client)
                        continue;
                if (!OnSameTeam(ent, other))
                        continue;
                if (partner && other != ent->client->resp.radio_partner)
                        continue;
                AddRadioMsg(other, msg_fullpath, msg_len, ent);
        }
}

void Cmd_Radio_f(edict_t *ent)
{
        RadioBroadcast(ent, ent->client->resp.radio_partner_mode, gi.args());
}

void Cmd_Radiopartner_f(edict_t *ent)
{
        RadioBroadcast(ent, 1, gi.args());
}

void Cmd_Radioteam_f(edict_t *ent)
{
        RadioBroadcast(ent, 0, gi.args());
}

void Cmd_Radiogender_f(edict_t *ent)
{
        char *arg;

        if (!teamplay->value)
        {
                if (!((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS)))
                        return;  // don't allow in a non-team setup...
        }

        arg = gi.args();
        if (arg == NULL || !strlen(arg))
        {
                if (ent->client->resp.radio_gender)
                        gi.cprintf(ent, PRINT_HIGH, "Radio gender currently set to female\n");
                        else
                        gi.cprintf(ent, PRINT_HIGH, "Radio gender currently set to male\n");
                return;
        }
                
        if (!Q_stricmp(arg, "male"))
        {
                gi.cprintf(ent, PRINT_HIGH, "Radio gender set to male\n");
                ent->client->resp.radio_gender = 0;
        }
                else if (!Q_stricmp(arg, "female"))
        {
                gi.cprintf(ent, PRINT_HIGH, "Radio gender set to female\n");
                ent->client->resp.radio_gender = 1;
        }
                else
        {
                gi.cprintf(ent, PRINT_HIGH, "Invalid gender selection, try 'male' or 'female'\n");
        }
}

void Cmd_Radio_power_f(edict_t *ent)
{
        if (!teamplay->value)
        {
                if (!((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS)))
                        return;  // don't allow in a non-team setup...
        }

        ent->client->resp.radio_power_off = !ent->client->resp.radio_power_off;

        if (ent->client->resp.radio_power_off)
        {
                gi.centerprintf(ent, "Radio switched off\n");
                stuffcmd(ent, "play " RADIO_CLICK);
        }
                else
        {
                gi.centerprintf(ent, "Radio switched on\n");
                stuffcmd(ent, "play " RADIO_CLICK);
        }
}

void Cmd_Channel_f(edict_t *ent)
{
        if (!teamplay->value)
        {
                if (!((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS)))
                        return;  // don't allow in a non-team setup...
        }

        ent->client->resp.radio_partner_mode = !ent->client->resp.radio_partner_mode;
        if (ent->client->resp.radio_partner_mode)
        {
                gi.centerprintf(ent, "Channel set to 1, partner channel\n");
        }
                else
        {
                gi.centerprintf(ent, "Channel set to 0, team channel\n");
        }
}

// DetermineViewedTeammate: determine the current player you're viewing (only looks for live teammates)
// Modified from SetIDView (which was used from Zoid's CTF)
edict_t *DetermineViewedTeammate(edict_t *ent)
{
        vec3_t  forward, dir;
        trace_t tr;
        edict_t *who, *best;
//FIREBLADE, suggested by hal[9k]  3/11/1999
        float bd = 0.9;
//FIREBLADE
        float  d;
        int i;

        AngleVectors(ent->client->v_angle, forward, NULL, NULL);
        VectorScale(forward, 8192, forward);
        VectorAdd(ent->s.origin, forward, forward);
        PRETRACE();
        tr = gi.trace(ent->s.origin, NULL, NULL, forward, ent, MASK_SOLID);
        POSTTRACE();
        if (tr.fraction < 1 && tr.ent && tr.ent->client) {
                return NULL;
        }

        AngleVectors(ent->client->v_angle, forward, NULL, NULL);
        best = NULL;
        for (i = 1; i <= maxclients->value; i++) {
                who = g_edicts + i;
                if (!who->inuse)
                        continue;
                VectorSubtract(who->s.origin, ent->s.origin, dir);
                VectorNormalize(dir);
                d = DotProduct(forward, dir);
                if (d > bd && loc_CanSee(ent, who) && 
                        who->solid != SOLID_NOT && 
                        who->deadflag != DEAD_DEAD &&
                        OnSameTeam(who, ent)) {
                        bd = d;
                        best = who;
                }
        }

        if (bd > 0.90)
        {
                return best;
        }

        return NULL;
}

void Cmd_Partner_f(edict_t *ent)
{
        edict_t *target;
        char *genderstr;

        if (!teamplay->value)
        {
                if (!((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS)))
                        return;  // don't allow in a non-team setup...
        }

        if (ent->deadflag == DEAD_DEAD || ent->solid == SOLID_NOT)
                return; 

        if (ent->client->resp.radio_partner &&
                !ent->client->resp.radio_partner->inuse)
        { // just in case RadioThink hasn't caught it yet... avoid any problems
                ent->client->resp.radio_partner = NULL;
        }

        if (ent->client->resp.radio_partner)
        {
                gi.centerprintf(ent, "You already have a partner, %s\n", 
                        ent->client->resp.radio_partner->client->pers.netname);
                return;
        }

        target = DetermineViewedTeammate(ent);

        if (target == NULL)
        {
                gi.centerprintf(ent, "No potential partner selected\n");
                return;
        }

        if (target->client->resp.radio_partner)
        {
                gi.centerprintf(ent, "%s already has a partner\n", target->client->pers.netname);
                return;
        }

        if (target->client->resp.partner_last_offered_to == ent &&
                ent->client->resp.partner_last_offered_from == target)
        {
                gi.centerprintf(ent, "%s is now your partner\n", target->client->pers.netname);
                gi.centerprintf(target, "%s is now your partner\n", ent->client->pers.netname);
                ent->client->resp.radio_partner = target;
                target->client->resp.radio_partner = ent;
                ent->client->resp.partner_last_offered_from = NULL;
                target->client->resp.partner_last_offered_to = NULL;
                return;
        }

        if (target->client->resp.partner_last_denied_from == ent)
        {
                gi.centerprintf(ent, "%s has already denied you\n",             
                                target->client->pers.netname);
                return;
        }

        if (target == ent->client->resp.partner_last_offered_to)
        {
                if (IsFemale(target))
                        genderstr = "her";
                        else if (IsNeutral(target))
                        genderstr = "it";
                        else
                        genderstr = "him";

                gi.centerprintf(ent, "Already awaiting confirmation from %s\n", genderstr);
                return;
        }

        if (IsFemale(ent))
                genderstr = "her";
                else if (IsNeutral(ent))
                genderstr = "it";
                else
                genderstr = "him";

        gi.centerprintf(ent, "Awaiting confirmation from %s\n", target->client->pers.netname);
        gi.centerprintf(target, "%s offers to be your partner\n"
                                "To accept:\nView %s and use the 'partner' command\n"
                                "To deny:\nUse the 'deny' command\n",
                ent->client->pers.netname, genderstr);

        ent->client->resp.partner_last_offered_to = target;
        target->client->resp.partner_last_offered_from = ent;
}

void Cmd_Unpartner_f(edict_t *ent)
{
        edict_t *target;

        if (!teamplay->value)
        {
                if (!((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS)))
                        return;  // don't allow in a non-team setup...
        }

        if (ent->client->resp.radio_partner &&
                !ent->client->resp.radio_partner->inuse)
        { // just in case RadioThink hasn't caught it yet... avoid any problems
                ent->client->resp.radio_partner = NULL;
        }

        target = ent->client->resp.radio_partner;

        if (target == NULL)
        {
                gi.centerprintf(ent, "You don't have a partner\n");
                return;
        }

        if (target->client->resp.radio_partner == ent)
        {
                gi.centerprintf(target, "%s broke your partnership\n",  
                        ent->client->pers.netname);
                target->client->resp.radio_partner = NULL;
        }

        gi.centerprintf(ent, "You broke your partnership with %s\n", 
                        target->client->pers.netname);
        ent->client->resp.radio_partner = NULL;
}

void Cmd_Deny_f(edict_t *ent)
{
        edict_t *target;

        if (!teamplay->value)
        {
                if (!((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS)))
                        return;  // don't allow in a non-team setup...
        }

        if (ent->deadflag == DEAD_DEAD || ent->solid == SOLID_NOT)
                return; 

        target = ent->client->resp.partner_last_offered_from;
        if (target && target->inuse)
        {
                gi.centerprintf(ent, "You denied %s\n",
                        target->client->pers.netname);
                gi.centerprintf(target, "%s has denied you\n",
                        ent->client->pers.netname);
                ent->client->resp.partner_last_denied_from = target;
                ent->client->resp.partner_last_offered_from = NULL;
                if (target->client->resp.partner_last_offered_to == ent)
                        target->client->resp.partner_last_offered_to = NULL;
        }
                else
        {
                gi.centerprintf(ent, "No one has offered to be your partner\n");
                return;
        }
}

void Cmd_Say_partner_f(edict_t *ent)
{
        if (!teamplay->value)
        {
                if (!((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS)))
                        return;  // don't allow in a non-team setup...
        }

        if (ent->client->resp.radio_partner == NULL)
        {
                gi.cprintf(ent, PRINT_HIGH, "You don't have a partner.\n");
                return;
        }

        Cmd_Say_f(ent, false, false, true);
}
