//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 Nuke.YKT

This file is part of NBlood.

NBlood is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.

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.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
//-------------------------------------------------------------------------

#include "ns.h"	// Must come before everything else!
#include "build.h"
#include "common_game.h"
#include "dude.h"
#include "seq.h"
#include "view.h"
#include "fx.h"
#include "gib.h"
#include "g_input.h"

BEGIN_BLD_NS

int nPrecacheCount;

void fxPrecache(HitList &hits);
void gibPrecache(HitList &hits);


void tilePreloadTile(int nTile)
{
    if (!r_precache) return;
    int n = 1;
    switch (picanm[nTile].extra & 7)
    {
    case 0:
        n = 1;
        break;
    case 1:
        n = 5;
        break;
    case 2:
        n = 8;
        break;
    case 3:
        n = 2;
        break;
    case 6:
    case 7:
        if (voxelIndex[nTile] < 0 || voxelIndex[nTile] >= kMaxVoxels)
        {
            voxelIndex[nTile] = -1;
            picanm[nTile].extra &= ~7;
        }
        break;
    }

    while (n--)
    {
        if (picanm[nTile].sf & PICANM_ANIMTYPE_MASK)
        {
            for (int frame = picanm[nTile].num; frame >= 0; frame--)
            {
                if ((picanm[nTile].sf & PICANM_ANIMTYPE_MASK) == PICANM_ANIMTYPE_BACK)
                    PrecacheHardwareTextures(nTile - frame);
                else
                    PrecacheHardwareTextures(nTile + frame);
            }
        }
        else
            PrecacheHardwareTextures(nTile);
        nTile += 1 + picanm[nTile].num;
    }
}

void tilePrecacheTile(int nTile, int nType, HitList &hits)
{
    int n = 1;
    switch (picanm[nTile].extra & 7)
    {
    case 0:
        n = 1;
        break;
    case 1:
        n = 5;
        break;
    case 2:
        n = 8;
        break;
    case 3:
        n = 2;
        break;
    }
    while (n--)
    {
        if (picanm[nTile].sf & PICANM_ANIMTYPE_MASK)
        {
            for (int frame = picanm[nTile].num; frame >= 0; frame--)
            {
                int tile;
                if ((picanm[nTile].sf & PICANM_ANIMTYPE_MASK) == PICANM_ANIMTYPE_BACK)
                    tile = nTile - frame;
                else
                    tile = nTile + frame;
                if (!hits[tile])
                {
                    nPrecacheCount++;
                    hits.Set(tile);
                }
            }
        }
        else
        {
            if (!hits[nTile])
            {
                nPrecacheCount++;
                hits.Set(nTile);
            }
        }
        nTile += 1 + picanm[nTile].num;
    }
}


// To do: This needs to handle the sprite palettes as well to properly precache the needed content.

void viewPrecacheTiles(HitList &hits)
{
    tilePrecacheTile(2173, 0, hits);
    tilePrecacheTile(2200, 0, hits);
    tilePrecacheTile(2201, 0, hits);
    tilePrecacheTile(2202, 0, hits);
    tilePrecacheTile(2207, 0, hits);
    tilePrecacheTile(2208, 0, hits);
    tilePrecacheTile(2209, 0, hits);
    tilePrecacheTile(2229, 0, hits);
    tilePrecacheTile(2260, 0, hits);
    tilePrecacheTile(2559, 0, hits);
    tilePrecacheTile(2169, 0, hits);
    tilePrecacheTile(2578, 0, hits);
    tilePrecacheTile(2586, 0, hits);
    tilePrecacheTile(2602, 0, hits);
    for (int i = 0; i < 10; i++)
    {
        tilePrecacheTile(2190 + i, 0, hits);
        tilePrecacheTile(2230 + i, 0, hits);
        tilePrecacheTile(2240 + i, 0, hits);
        tilePrecacheTile(2250 + i, 0, hits);
        tilePrecacheTile(kSBarNumberHealth + i, 0, hits);
        tilePrecacheTile(kSBarNumberAmmo + i, 0, hits);
        tilePrecacheTile(kSBarNumberInv + i, 0, hits);
        tilePrecacheTile(kSBarNumberArmor1 + i, 0, hits);
        tilePrecacheTile(kSBarNumberArmor2 + i, 0, hits);
        tilePrecacheTile(kSBarNumberArmor3 + i, 0, hits);
    }
    /*
    for (int i = 0; i < 5; i++)
    {
        tilePrecacheTile(gPackIcons[i], 0);
        tilePrecacheTile(gPackIcons2[i].nTile, 0);
    }
    */
    for (int i = 0; i < 6; i++)
    {
        tilePrecacheTile(2220 + i, 0, hits);
        tilePrecacheTile(2552 + i, 0, hits);
    }
}



void PrecacheDude(spritetype *pSprite, HitList &hits)
{
    DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
    seqPrecacheId(pDudeInfo->seqStartID  , hits);
    seqPrecacheId(pDudeInfo->seqStartID+5, hits);
    seqPrecacheId(pDudeInfo->seqStartID+1, hits);
    seqPrecacheId(pDudeInfo->seqStartID+2, hits);
    switch (pSprite->type)
    {
    case kDudeCultistTommy:
    case kDudeCultistShotgun:
    case kDudeCultistTesla:
    case kDudeCultistTNT:
        seqPrecacheId(pDudeInfo->seqStartID+6 , hits);
        seqPrecacheId(pDudeInfo->seqStartID+7 , hits);
        seqPrecacheId(pDudeInfo->seqStartID+8 , hits);
        seqPrecacheId(pDudeInfo->seqStartID+9 , hits);
        seqPrecacheId(pDudeInfo->seqStartID+13, hits);
        seqPrecacheId(pDudeInfo->seqStartID+14, hits);
        seqPrecacheId(pDudeInfo->seqStartID+15, hits);
        break;
    case kDudeZombieButcher:
    case kDudeGillBeast:
        seqPrecacheId(pDudeInfo->seqStartID+6, hits);
        seqPrecacheId(pDudeInfo->seqStartID+7, hits);
        seqPrecacheId(pDudeInfo->seqStartID+8, hits);
        seqPrecacheId(pDudeInfo->seqStartID+9, hits);
        seqPrecacheId(pDudeInfo->seqStartID+10, hits);
        seqPrecacheId(pDudeInfo->seqStartID+11, hits);
        break;
    case kDudeGargoyleStatueFlesh:
    case kDudeGargoyleStatueStone:
        seqPrecacheId(pDudeInfo->seqStartID+6, hits);
        seqPrecacheId(pDudeInfo->seqStartID+6, hits); //???
        fallthrough__;
    case kDudeGargoyleFlesh:
    case kDudeGargoyleStone:
        seqPrecacheId(pDudeInfo->seqStartID+6, hits);
        seqPrecacheId(pDudeInfo->seqStartID+7, hits);
        seqPrecacheId(pDudeInfo->seqStartID+8, hits);
        seqPrecacheId(pDudeInfo->seqStartID+9, hits);
        break;
    case kDudePhantasm:
    case kDudeHellHound:
    case kDudeSpiderBrown:
    case kDudeSpiderRed:
    case kDudeSpiderBlack:
    case kDudeSpiderMother:
    case kDudeTchernobog:
        seqPrecacheId(pDudeInfo->seqStartID+6, hits);
        seqPrecacheId(pDudeInfo->seqStartID+7, hits);
        seqPrecacheId(pDudeInfo->seqStartID+8, hits);
        break;
    case kDudeCerberusTwoHead:
        seqPrecacheId(pDudeInfo->seqStartID+6, hits);
        seqPrecacheId(pDudeInfo->seqStartID+7, hits);
        fallthrough__;
    case kDudeHand:
    case kDudeBoneEel:
    case kDudeBat:
    case kDudeRat:
        seqPrecacheId(pDudeInfo->seqStartID+6, hits);
        seqPrecacheId(pDudeInfo->seqStartID+7, hits);
        break;
    case kDudeCultistBeast:
        seqPrecacheId(pDudeInfo->seqStartID+6, hits);
        break;
    case kDudeZombieAxeBuried:
        seqPrecacheId(pDudeInfo->seqStartID+12, hits);
        seqPrecacheId(pDudeInfo->seqStartID+9, hits);
        fallthrough__;
    case kDudeZombieAxeLaying:
        seqPrecacheId(pDudeInfo->seqStartID+10, hits);
        fallthrough__;
    case kDudeZombieAxeNormal:
        seqPrecacheId(pDudeInfo->seqStartID+6, hits);
        seqPrecacheId(pDudeInfo->seqStartID+7, hits);
        seqPrecacheId(pDudeInfo->seqStartID+8, hits);
        seqPrecacheId(pDudeInfo->seqStartID+11, hits);
        seqPrecacheId(pDudeInfo->seqStartID+13, hits);
        seqPrecacheId(pDudeInfo->seqStartID+14, hits);
        break;
    }
}

void PrecacheThing(spritetype *pSprite, HitList &hits) {
    switch (pSprite->type) {
        case kThingGlassWindow: // worthless...
        case kThingFluorescent:
            seqPrecacheId(12, hits);
            break;
        case kThingSpiderWeb:
            seqPrecacheId(15, hits);
            break;
        case kThingMetalGrate:
            seqPrecacheId(21, hits);
            break;
        case kThingFlammableTree:
            seqPrecacheId(25, hits);
            seqPrecacheId(26, hits);
            break;
        case kTrapMachinegun:
            seqPrecacheId(38, hits);
            seqPrecacheId(40, hits);
            seqPrecacheId(28, hits);
            break;
        case kThingObjectGib:
        //case kThingObjectExplode: weird that only gib object is precached and this one is not
            break;
    }
    tilePrecacheTile(pSprite->picnum, -1, hits);
}

void PreloadTiles(HitList & hits)
{
    nPrecacheCount = 0;
    int skyTile = -1;
    hits.Zero();
    // Fonts
    for (int i = 0; i < numsectors; i++)
    {
        tilePrecacheTile(sector[i].floorpicnum, 0, hits);
        tilePrecacheTile(sector[i].ceilingpicnum, 0, hits);
        if ((sector[i].ceilingstat&1) != 0 && skyTile == -1)
            skyTile = sector[i].ceilingpicnum;
    }
    for (int i = 0; i < numwalls; i++)
    {
        tilePrecacheTile(wall[i].picnum, 0, hits);
        if (wall[i].overpicnum >= 0)
            tilePrecacheTile(wall[i].overpicnum, 0, hits);
    }
    for (int i = 0; i < kMaxSprites; i++)
    {
        if (sprite[i].statnum < kMaxStatus)
        {
            spritetype *pSprite = &sprite[i];
            switch (pSprite->statnum)
            {
            case kStatDude:
                PrecacheDude(pSprite, hits);
                break;
            case kStatThing:
                PrecacheThing(pSprite, hits);
                break;
            default:
                tilePrecacheTile(pSprite->picnum, -1, hits);
                break;
            }
        }
    }

    // Precache common SEQs
    for (int i = 0; i < 100; i++)
    {
        seqPrecacheId(i, hits);
    }

    tilePrecacheTile(1147, -1, hits); // water drip
    tilePrecacheTile(1160, -1, hits); // blood drip

    // Player SEQs
    seqPrecacheId(dudeInfo[31].seqStartID+6, hits);
    seqPrecacheId(dudeInfo[31].seqStartID+7, hits);
    seqPrecacheId(dudeInfo[31].seqStartID+8, hits);
    seqPrecacheId(dudeInfo[31].seqStartID+9, hits);
    seqPrecacheId(dudeInfo[31].seqStartID+10, hits);
    seqPrecacheId(dudeInfo[31].seqStartID+14, hits);
    seqPrecacheId(dudeInfo[31].seqStartID+15, hits);
    seqPrecacheId(dudeInfo[31].seqStartID+12, hits);
    seqPrecacheId(dudeInfo[31].seqStartID+16, hits);
    seqPrecacheId(dudeInfo[31].seqStartID+17, hits);
    seqPrecacheId(dudeInfo[31].seqStartID+18, hits);

    if (skyTile > -1 && skyTile < kMaxTiles)
    {
        for (int i = 1; i < gSkyCount; i++)
            tilePrecacheTile(skyTile+i, 0, hits);
    }

    WeaponPrecache(hits);
    viewPrecacheTiles(hits);
    fxPrecache(hits);
    gibPrecache(hits);

    I_GetEvent();
}

void PreloadCache()
{
	if (!r_precache) return;
    HitList hits;
    PreloadTiles(hits);
    int cnt = 0;
    int percentDisplayed = -1;

    for (int i = 0; i < kMaxTiles; i++)
    {
        if (hits[i])
        {
            PrecacheHardwareTextures(i);

            if ((++cnt & 7) == 0)
                I_GetEvent();
        }
    }
}

END_BLD_NS

