//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 1993-2008 Raven Software
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//

#include <ctype.h>

#include "h2def.h"
#include "s_sound.h"
#include "i_system.h"
#include "i_video.h"
#include "m_misc.h"
#include "p_local.h"
#include "v_video.h"
#include "i_swap.h"

// MACROS ------------------------------------------------------------------

#define	TEXTSPEED 3
#define	TEXTWAIT 140

// TYPES -------------------------------------------------------------------

typedef enum
{
    SINGLE,
    COOPERATIVE,
    DEATHMATCH
} gametype_t;

// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------

// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------

// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------

static void WaitStop(void);
static void Stop(void);
static void LoadPics(void);
static void UnloadPics(void);
static void CheckForSkip(void);
static void InitStats(void);
static void DrDeathTally(void);
static void DrNumber(int val, int x, int y, int wrapThresh);
static void DrNumberBold(int val, int x, int y, int wrapThresh);
static void DrawHubText(void);

// EXTERNAL DATA DECLARATIONS ----------------------------------------------

// PUBLIC DATA DECLARATIONS ------------------------------------------------

boolean intermission;
char ClusterMessage[MAX_INTRMSN_MESSAGE_SIZE];

// PRIVATE DATA DEFINITIONS ------------------------------------------------

static boolean skipintermission;
static int interstate = 0;
static int intertime = -1;
static gametype_t gametype;
static int cnt;
static int slaughterboy;        // in DM, the player with the most kills
static patch_t *patchINTERPIC;
static patch_t *FontBNumbers[10];
static patch_t *FontBNegative;
static patch_t *FontBSlash;
static patch_t *FontBPercent;
static int FontABaseLump;
static int FontBLump;
static int FontBLumpBase;

static signed int totalFrags[MAXPLAYERS];

static int HubCount;
static char *HubText;

// CODE --------------------------------------------------------------------

//========================================================================
//
// IN_Start
//
//========================================================================

extern void AM_Stop(void);

void IN_Start(void)
{
    int i;
    I_SetPalette(W_CacheLumpName("PLAYPAL", PU_CACHE));
    InitStats();
    LoadPics();
    intermission = true;
    interstate = 0;
    skipintermission = false;
    intertime = 0;
    AM_Stop();
    for (i = 0; i < maxplayers; i++)
    {
        players[i].messageTics = 0;
        players[i].message[0] = 0;
    }
    SN_StopAllSequences();
}

//========================================================================
//
// WaitStop
//
//========================================================================

void WaitStop(void)
{
    if (!--cnt)
    {
        Stop();
//              gamestate = GS_LEVEL;
//              G_DoLoadLevel();
        gameaction = ga_leavemap;
//              G_WorldDone();
    }
}

//========================================================================
//
// Stop
//
//========================================================================

static void Stop(void)
{
    intermission = false;
    UnloadPics();
    SB_state = -1;
    BorderNeedRefresh = true;
}

//========================================================================
//
// InitStats
//
//      Initializes the stats for single player mode
//========================================================================

static const char *ClusMsgLumpNames[] = {
    "clus1msg",
    "clus2msg",
    "clus3msg",
    "clus4msg",
    "clus5msg"
};

static void InitStats(void)
{
    int i;
    int j;
    int oldCluster;
    signed int slaughterfrags;
    int posnum;
    int slaughtercount;
    int playercount;
    const char *msgLumpName;
    int msgSize;
    int msgLump;

    extern int LeaveMap;

    if (!deathmatch)
    {
        gametype = SINGLE;
        HubCount = 0;
        oldCluster = P_GetMapCluster(gamemap);
        if (oldCluster != P_GetMapCluster(LeaveMap))
        {
            if (oldCluster >= 1 && oldCluster <= 5)
            {
                msgLumpName = ClusMsgLumpNames[oldCluster - 1];
                msgLump = W_GetNumForName(msgLumpName);
                msgSize = W_LumpLength(msgLump);
                if (msgSize >= MAX_INTRMSN_MESSAGE_SIZE)
                {
                    I_Error("Cluster message too long (%s)", msgLumpName);
                }
                W_ReadLump(msgLump, ClusterMessage);
                ClusterMessage[msgSize] = 0;    // Append terminator
                HubText = ClusterMessage;
                HubCount = strlen(HubText) * TEXTSPEED + TEXTWAIT;
                S_StartSongName("hub", true);
            }
        }
    }
    else
    {
        gametype = DEATHMATCH;
        slaughterboy = 0;
        slaughterfrags = -9999;
        posnum = 0;
        playercount = 0;
        slaughtercount = 0;
        for (i = 0; i < maxplayers; i++)
        {
            totalFrags[i] = 0;
            if (playeringame[i])
            {
                playercount++;
                for (j = 0; j < maxplayers; j++)
                {
                    if (playeringame[j])
                    {
                        totalFrags[i] += players[i].frags[j];
                    }
                }
                posnum++;
            }
            if (totalFrags[i] > slaughterfrags)
            {
                slaughterboy = 1 << i;
                slaughterfrags = totalFrags[i];
                slaughtercount = 1;
            }
            else if (totalFrags[i] == slaughterfrags)
            {
                slaughterboy |= 1 << i;
                slaughtercount++;
            }
        }
        if (playercount == slaughtercount)
        {                       // don't do the slaughter stuff if everyone is equal
            slaughterboy = 0;
        }
        S_StartSongName("hub", true);
    }
}

//========================================================================
//
// LoadPics
//
//========================================================================

static void LoadPics(void)
{
    int i;

    if (HubCount || gametype == DEATHMATCH)
    {
        patchINTERPIC = W_CacheLumpName("INTERPIC", PU_STATIC);
        FontBLumpBase = W_GetNumForName("FONTB16");
        for (i = 0; i < 10; i++)
        {
            FontBNumbers[i] = W_CacheLumpNum(FontBLumpBase + i, PU_STATIC);
        }
        FontBLump = W_GetNumForName("FONTB_S") + 1;
        FontBNegative = W_CacheLumpName("FONTB13", PU_STATIC);
        FontABaseLump = W_GetNumForName("FONTA_S") + 1;

        FontBSlash = W_CacheLumpName("FONTB15", PU_STATIC);
        FontBPercent = W_CacheLumpName("FONTB05", PU_STATIC);
    }
}

//========================================================================
//
// UnloadPics
//
//========================================================================

static void UnloadPics(void)
{
    int i;

    if (HubCount || gametype == DEATHMATCH)
    {
        W_ReleaseLumpName("INTERPIC");

        patchINTERPIC = W_CacheLumpName("INTERPIC", PU_STATIC);
        FontBLumpBase = W_GetNumForName("FONTB16");
        for (i = 0; i < 10; i++)
        {
            W_ReleaseLumpNum(FontBLumpBase + i);
        }
        W_ReleaseLumpName("FONTB13");
        W_ReleaseLumpName("FONTB15");
        W_ReleaseLumpName("FONTB05");
    }
}

//========================================================================
//
// IN_Ticker
//
//========================================================================

void IN_Ticker(void)
{
    if (!intermission)
    {
        return;
    }
    if (interstate)
    {
        WaitStop();
        return;
    }
    skipintermission = false;
    CheckForSkip();
    intertime++;
    if (skipintermission || (gametype == SINGLE && !HubCount))
    {
        interstate = 1;
        cnt = 10;
        skipintermission = false;
        //S_StartSound(NULL, sfx_dorcls);
    }
}

//========================================================================
//
// CheckForSkip
//
//      Check to see if any player hit a key
//========================================================================

static void CheckForSkip(void)
{
    int i;
    player_t *player;
    static boolean triedToSkip;

    for (i = 0, player = players; i < maxplayers; i++, player++)
    {
        if (playeringame[i])
        {
            if (player->cmd.buttons & BT_ATTACK)
            {
                if (!player->attackdown)
                {
                    skipintermission = 1;
                }
                player->attackdown = true;
            }
            else
            {
                player->attackdown = false;
            }
            if (player->cmd.buttons & BT_USE)
            {
                if (!player->usedown)
                {
                    skipintermission = 1;
                }
                player->usedown = true;
            }
            else
            {
                player->usedown = false;
            }
        }
    }
    if (deathmatch && intertime < 140)
    {                           // wait for 4 seconds before allowing a skip
        if (skipintermission == 1)
        {
            triedToSkip = true;
            skipintermission = 0;
        }
    }
    else
    {
        if (triedToSkip)
        {
            skipintermission = 1;
            triedToSkip = false;
        }
    }
}

//========================================================================
//
// IN_Drawer
//
//========================================================================

void IN_Drawer(void)
{
    if (!intermission)
    {
        return;
    }
    if (interstate)
    {
        return;
    }
    UpdateState |= I_FULLSCRN;
    memcpy(I_VideoBuffer, (byte *) patchINTERPIC, SCREENWIDTH * SCREENHEIGHT);

    if (gametype == SINGLE)
    {
        if (HubCount)
        {
            DrawHubText();
        }
    }
    else
    {
        DrDeathTally();
    }
}

//========================================================================
//
// DrDeathTally
//
//========================================================================

#define TALLY_EFFECT_TICKS 20
#define TALLY_FINAL_X_DELTA (23*FRACUNIT)
#define TALLY_FINAL_Y_DELTA (13*FRACUNIT)
#define TALLY_START_XPOS (178*FRACUNIT)
#define TALLY_STOP_XPOS (90*FRACUNIT)
#define TALLY_START_YPOS (132*FRACUNIT)
#define TALLY_STOP_YPOS (83*FRACUNIT)
#define TALLY_TOP_X 85
#define TALLY_TOP_Y 9
#define TALLY_LEFT_X 7
#define TALLY_LEFT_Y 71
#define TALLY_TOTALS_X 291

static void DrDeathTally(void)
{
    int i, j;
    fixed_t xPos, yPos;
    fixed_t xDelta, yDelta;
    fixed_t xStart, scale;
    int x, y;
    boolean bold;
    static boolean showTotals;
    int temp;

    V_DrawPatch(TALLY_TOP_X, TALLY_TOP_Y,
                W_CacheLumpName("tallytop", PU_CACHE));
    V_DrawPatch(TALLY_LEFT_X, TALLY_LEFT_Y,
                W_CacheLumpName("tallylft", PU_CACHE));
    if (intertime < TALLY_EFFECT_TICKS)
    {
        showTotals = false;
        scale = (intertime * FRACUNIT) / TALLY_EFFECT_TICKS;
        xDelta = FixedMul(scale, TALLY_FINAL_X_DELTA);
        yDelta = FixedMul(scale, TALLY_FINAL_Y_DELTA);
        xStart = TALLY_START_XPOS - FixedMul(scale,
                                             TALLY_START_XPOS -
                                             TALLY_STOP_XPOS);
        yPos =
            TALLY_START_YPOS - FixedMul(scale,
                                        TALLY_START_YPOS - TALLY_STOP_YPOS);
    }
    else
    {
        xDelta = TALLY_FINAL_X_DELTA;
        yDelta = TALLY_FINAL_Y_DELTA;
        xStart = TALLY_STOP_XPOS;
        yPos = TALLY_STOP_YPOS;
    }
    if (intertime >= TALLY_EFFECT_TICKS && showTotals == false)
    {
        showTotals = true;
        S_StartSound(NULL, SFX_PLATFORM_STOP);
    }
    y = yPos >> FRACBITS;
    for (i = 0; i < maxplayers; i++)
    {
        xPos = xStart;
        for (j = 0; j < maxplayers; j++, xPos += xDelta)
        {
            x = xPos >> FRACBITS;
            bold = (i == consoleplayer || j == consoleplayer);
            if (playeringame[i] && playeringame[j])
            {
                if (bold)
                {
                    DrNumberBold(players[i].frags[j], x, y, 100);
                }
                else
                {
                    DrNumber(players[i].frags[j], x, y, 100);
                }
            }
            else
            {
                temp = MN_TextAWidth("--") / 2;
                if (bold)
                {
                    MN_DrTextAYellow("--", x - temp, y);
                }
                else
                {
                    MN_DrTextA("--", x - temp, y);
                }
            }
        }
        if (showTotals && playeringame[i]
            && !((slaughterboy & (1 << i)) && !(intertime & 16)))
        {
            DrNumber(totalFrags[i], TALLY_TOTALS_X, y, 1000);
        }
        yPos += yDelta;
        y = yPos >> FRACBITS;
    }
}

//==========================================================================
//
// DrNumber
//
//==========================================================================

static void DrNumber(int val, int x, int y, int wrapThresh)
{
    char buff[8] = "XX";

    if (!(val < -9 && wrapThresh < 1000))
    {
        M_snprintf(buff, sizeof(buff), "%d",
                   val >= wrapThresh ? val % wrapThresh : val);
    }
    MN_DrTextA(buff, x - MN_TextAWidth(buff) / 2, y);
}

//==========================================================================
//
// DrNumberBold
//
//==========================================================================

static void DrNumberBold(int val, int x, int y, int wrapThresh)
{
    char buff[8] = "XX";

    if (!(val < -9 && wrapThresh < 1000))
    {
        M_snprintf(buff, sizeof(buff), "%d",
                   val >= wrapThresh ? val % wrapThresh : val);
    }
    MN_DrTextAYellow(buff, x - MN_TextAWidth(buff) / 2, y);
}

//===========================================================================
//
// DrawHubText
//
//===========================================================================

static void DrawHubText(void)
{
    int count;
    char *ch;
    int c;
    int cx, cy;
    patch_t *w;

    cy = 5;
    cx = 10;
    ch = HubText;
    count = (intertime - 10) / TEXTSPEED;
    if (count < 0)
    {
        count = 0;
    }
    for (; count; count--)
    {
        c = *ch++;
        if (!c)
        {
            break;
        }
        if (c == '\n')
        {
            cx = 10;
            cy += 9;
            continue;
        }
        if (c < 32)
        {
            continue;
        }
        c = toupper(c);
        if (c == 32)
        {
            cx += 5;
            continue;
        }
        w = W_CacheLumpNum(FontABaseLump + c - 33, PU_CACHE);
        if (cx + SHORT(w->width) > SCREENWIDTH)
        {
            break;
        }
        V_DrawPatch(cx, cy, w);
        cx += SHORT(w->width);
    }
}
