//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 sirlemonhead, Nuke.YKT
This file is part of PCExhumed.
PCExhumed 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"
#include "compat.h"
#include "init.h"
#include "runlist.h"
#include "switch.h"
#include "object.h"
#include "aistuff.h"
#include "player.h"
#include "mummy.h"
#include "move.h"
#include "ra.h"
#include "view.h"
#include "runlist.h"
#include "engine.h"
#include "sound.h"
#include "exhumed.h"
#include "items.h"
#include "light.h"
#include "map.h"
#include "menu.h"
#include "lighting.h"
#include "anims.h"
#include "ps_input.h"
#include "util.h"
#include "mapinfo.h"
#include "gamecontrol.h"
#include "v_video.h"
#include <stdio.h>
#include <string.h>

BEGIN_PS_NS

enum
{
    kTagRamses = 61,
};

void uploadCinemaPalettes();
ClockTicks ototalclock = 0;

int initx, inity, initz;
short inita, initsect;

short nCurChunkNum = 0;

short nBodyGunSprite[50];
int movefifoend;
int movefifopos;

short nCurBodyGunNum;

short SectSoundSect[kMaxSectors] = { 0 };
short SectSound[kMaxSectors]     = { 0 };
short SectFlag[kMaxSectors]      = { 0 };
int   SectDepth[kMaxSectors]     = { 0 };
int   SectAbove[kMaxSectors]     = { 0 };
short SectDamage[kMaxSectors]    = { 0 };
short SectSpeed[kMaxSectors]     = { 0 };
int   SectBelow[kMaxSectors]     = { 0 };


uint8_t bIsVersion6 = kTrue;




uint8_t LoadLevel(int nMap)
{
    initspritelists();

    currentLevel = &mapList[nMap];

    // init stuff
    {
        StopAllSounds();
        nCreaturesLeft = 0;
        nFreeze = 0;
        nSpiritSprite = -1;

        InitLion();
        InitRexs();
        InitSets();
        InitQueens();
        InitRoachs();
        InitWasps();
        InitRats();
        InitBullets();
        InitWeapons();
        InitGrenades();
        InitAnims();
        InitSnakes();
        InitFishes();
        InitLights();
        InitMap();
        InitBubbles();
        InitObjects();
        InitLava();
        InitPushBlocks();
        InitAnubis();
        InitSpider();
        InitMummy();
        InitScorp();
        InitPlayer();
        InitItems();
        InitInput();

        if (nMap == kMap20) {
            InitEnergyTile();
        }
    }

    if (nMap > 15)
    {
        nSwitchSound = 35;
        nStoneSound = 23;
        nElevSound = 51;
        nStopSound = 35;
    }
    else
    {
        nSwitchSound = 33;
        nStoneSound = 23;
        nElevSound = 23;
        nStopSound = 66;
    }

    if (nMap < 0) {
        return kFalse;
    }

    vec3_t startPos;
    int status = engineLoadBoard(currentLevel->fileName, 0, &startPos, &inita, &initsect);
    if (status == -2)
        status = engineLoadBoardV5V6(currentLevel->fileName, 0, &startPos, &inita, &initsect);
    initx = startPos.x;
    inity = startPos.y;
    initz = startPos.z;

#ifdef YAX_ENABLE
    yax_update(1);
#endif

    int i;

    for (i = 0; i < kMaxPlayers; i++)
    {
        PlayerList[i].nSprite = -1;
    }

    psky_t* pSky = tileSetupSky(0);

    pSky->tileofs[0] = 0;
    pSky->tileofs[1] = 0;
    pSky->tileofs[2] = 0;
    pSky->tileofs[3] = 0;
    pSky->yoffs = 256;
    pSky->lognumtiles = 2;
    pSky->horizfrac = 65536;
    pSky->yscale = 65536;
    parallaxtype = 2;
    g_visibility = 2048;
    flash = 0;
    precache();

    LoadObjects();

    levelnum = nMap;

    return kTrue;
}

void ResetEngine()
{
    SetOverscan(BASEPAL);

    EraseScreen(-1);

    resettiming();

    totalclock  = 0;
    ototalclock = totalclock;
    localclock  = (int)totalclock;

    numframes = 0;
}

void InstallEngine()
{
    // initgroupfile("stuff.dat");
	TileFiles.LoadArtSet("tiles%03d.art");

	// TEMP

    //nScreenWidth *= 2;
    //nScreenHeight *= 2;
    bHiRes = kTrue;
    // TEMP

    if (engineInit())
    {
        G_FatalEngineError();
    }
    uploadCinemaPalettes();
    LoadPaletteLookups();
    videoInit();

    enginecompatibility_mode = ENGINECOMPATIBILITY_19950829;
}

void RemoveEngine()
{
    engineUnInit();
}

void SetBelow(short nCurSector, short nBelowSector)
{
    SectBelow[nCurSector] = nBelowSector;
}

void SetAbove(short nCurSector, short nAboveSector)
{
    SectAbove[nCurSector] = nAboveSector;
}

void SnapSectors(short nSectorA, short nSectorB, short b)
{
    // edx - nSectorA
    // eax - nSectorB

    short nWallA = sector[nSectorA].wallptr;
    short nWallB = sector[nSectorB].wallptr;

    short num1 = sector[nSectorA].wallnum;
    short num2 = sector[nSectorB].wallnum;

    int nCount = 0;

    while (num1 > nCount)
    {
        short dx = nWallB;

        int esi = 0x7FFFFFF;
        int edi = esi;

        int x = wall[nWallA].x;
        int y = wall[nWallA].y;

        int var_14 = 0;

        int nCount2 = 0;

        while (nCount2 < num2)
        {
            int eax = x - wall[dx].x;
            int ebx = y - wall[dx].y;

            if (eax < 0) {
                eax = -eax;
            }

            int var_38 = eax;

            if (ebx < 0) {
                ebx = -ebx;
            }

            int var_3C = ebx;

            var_38 += var_3C;

            eax = esi;
            if (eax < 0) {
                eax = -eax;
            }

            var_3C = eax;

            eax = edi;
//			int var_34 = edi;
            if (eax < 0) {
                eax = -eax;
            }

            int var_34 = eax;

            var_34 += var_3C;

            if (var_38 < var_34)
            {
                esi = x - wall[dx].x;
                edi = y - wall[dx].y;
                var_14 = dx;
            }

            dx++;
            nCount2++;
        }

        dragpoint(var_14, wall[var_14].x + esi, wall[var_14].y + edi, 0);

        nCount++;
        nWallA++;
    }

    if (b) {
        sector[nSectorB].ceilingz = sector[nSectorA].floorz;
    }

    if (SectFlag[nSectorA] & 0x1000) {
        SnapBobs(nSectorA, nSectorB);
    }
}

void InitSectFlag()
{
    for (int i = 0; i < kMaxSectors; i++)
    {
        SectSoundSect[i] = -1;
        SectSound[i] = -1;
        SectAbove[i] = -1;
        SectBelow[i] = -1;
        SectDepth[i] = 0;
        SectFlag[i]  = 0;
        SectSpeed[i] = 0;
        SectDamage[i] = 0;
    }
}

void ProcessSpriteTag(short nSprite, short nLotag, short nHitag)
{
    int nChannel = runlist_AllocChannel(nHitag % 1000);

    int nSpeed = nLotag / 1000;
    if (!nSpeed) {
        nSpeed = 1;
    }

    int nVal = nHitag;

    if (nLotag >= 900 && nLotag <= 949)
    {
        ProcessTrailSprite(nSprite, nLotag, nHitag);
        return;
    }

    // handle tags 6 to 60
    switch (nLotag)
    {
        case 8: // M-60 ammo belt
        {
            nVal = 3 * (nHitag / 3);
            // fall through to 6,7 etc
            fallthrough__;
        }
        case 6:
        case 7:
        case 9:
        case 10:
        case 11:
        case 15:
        case 17:
        case 18:
        case 19:
        case 20:
        case 21:
        case 22:
        case 23:
        case 24:
        case 26:
        case 28:
        case 29:
        case 30:
        case 31:
        case 32:
        case 33:
        case 34:
        case 35:
        case 36:
        case 37:
        case 39:
        case 40:
        case 41:
        case 42:
        case 43:
        case 44:
        case 45:
        case 46:
        case 47:
        case 48:
        case 49:
        case 50:
        case 51:
        case 52:
        case 53:
        case 54:
        case 55:
        case 56:
        case 57:
        case 58:
        case 60:
        {
            sprite[nSprite].hitag = nVal;
            changespritestat(nSprite, nLotag + 900);
            sprite[nSprite].cstat &= 0xFEFE;
            BuildItemAnim(nSprite);
            return;
        }
        case 12: // berry twig
        {
            sprite[nSprite].hitag = 40;
            changespritestat(nSprite, nLotag + 900);
            sprite[nSprite].cstat &= 0xFEFE;
            BuildItemAnim(nSprite);
            return;
        }
        case 13: // blood bowl
        {
            sprite[nSprite].hitag = 160;
            changespritestat(nSprite, nLotag + 900);
            sprite[nSprite].cstat &= 0xFEFE;
            BuildItemAnim(nSprite);
            return;
        }
        case 14: // venom bowl
        {
            sprite[nSprite].hitag = -200;
            changespritestat(nSprite, nLotag + 900);
            sprite[nSprite].cstat &= 0xFEFE;
            BuildItemAnim(nSprite);
            return;
        }

        case 16:
            // reserved
            mydeletesprite(nSprite);
            return;

        case 25:
        case 59:
        {
            // extra life or checkpoint scarab. Delete for multiplayer
            if (nNetPlayerCount != 0)
            {
                mydeletesprite(nSprite);
                return;
            }
            else
            {
                sprite[nSprite].hitag = nVal;
                changespritestat(nSprite, nLotag + 900);
                sprite[nSprite].cstat &= 0xFEFE;
                BuildItemAnim(nSprite);
                return;
            }
        }
        case 27:
        {
            sprite[nSprite].hitag = 1;
            changespritestat(nSprite, 9 + 900);
            sprite[nSprite].cstat &= 0xFEFE;
            BuildItemAnim(nSprite);
            return;
        }

        case 38: // raw energy
        {
            nVal++;
            nVal--; // CHECKME ??
            sprite[nSprite].hitag = nVal;
            changespritestat(nSprite, nLotag + 900);
            sprite[nSprite].cstat &= 0xFEFE;
            BuildItemAnim(nSprite);
            return;
        }
    }

    int v6 = nLotag % 1000;

    if (!bNoCreatures || v6 < 100 || v6 > 118)
    {
        if (v6 > 999) {
            mydeletesprite(nSprite);
            return;
        }

        switch (v6)
        {
            case 999:
            {
                AddFlicker(sprite[nSprite].sectnum, nSpeed);
                break;
            }
            case 998:
            {
                AddGlow(sprite[nSprite].sectnum, nSpeed);
                break;
            }
            case 118: // Anubis with drum
            {
                if (bNoCreatures) {
                    mydeletesprite(nSprite);
                    return;
                }

                BuildAnubis(nSprite, 0, 0, 0, 0, 0, 1);
                return;
            }
            case 117:
            {
                if (bNoCreatures) {
                    mydeletesprite(nSprite);
                    return;
                }

                BuildWasp(nSprite, 0, 0, 0, 0, 0);
                return;
            }
            case 116:
            {
                BuildRat(nSprite, 0, 0, 0, 0, -1);
                return;
            }
            case 115: // Rat (eating)
            {
                BuildRat(nSprite, 0, 0, 0, 0, 0);
                return;
            }
            case 113:
            {
                BuildQueen(nSprite, 0, 0, 0, 0, 0, nChannel);
                return;
            }
            case 112:
            {
                BuildScorp(nSprite, 0, 0, 0, 0, 0, nChannel);
                return;
            }
            case 111:
            {
                if (bNoCreatures) {
                    mydeletesprite(nSprite);
                    return;
                }

                BuildSet(nSprite, 0, 0, 0, 0, 0, nChannel);
                return;
            }
            case 108:
            {
                if (bNoCreatures) {
                    mydeletesprite(nSprite);
                    return;
                }

                BuildLava(nSprite, 0, 0, 0, 0, 0, nChannel);
                return;
            }
            case 107:
            {
                if (bNoCreatures) {
                    mydeletesprite(nSprite);
                    return;
                }

                BuildRex(nSprite, 0, 0, 0, 0, 0, nChannel);
                return;
            }
            case 106:
            {
                if (bNoCreatures) {
                    mydeletesprite(nSprite);
                    return;
                }

                BuildFish(nSprite, 0, 0, 0, 0, 0);
                return;
            }
            case 105:
            {
                if (bNoCreatures) {
                    mydeletesprite(nSprite);
                    return;
                }

                BuildSpider(nSprite, 0, 0, 0, 0, 0);
                return;
            }
            case 104:
            {
                if (bNoCreatures) {
                    mydeletesprite(nSprite);
                    return;
                }

                BuildRoach(1, nSprite, 0, 0, 0, 0, 0);
                return;
            }
            case 103:
            {
                if (bNoCreatures) {
                    mydeletesprite(nSprite);
                    return;
                }

                BuildRoach(0, nSprite, 0, 0, 0, 0, 0);
                return;
            }
            case 102:
            {
                if (bNoCreatures) {
                    mydeletesprite(nSprite);
                    return;
                }

                BuildLion(nSprite, 0, 0, 0, 0, 0);
                return;
            }
            case 101:
            {
                if (bNoCreatures) {
                    mydeletesprite(nSprite);
                    return;
                }

                BuildMummy(nSprite, 0, 0, 0, 0, 0);
                return;
            }
            case 100:
            {
                if (bNoCreatures) {
                    mydeletesprite(nSprite);
                    return;
                }

                BuildAnubis(nSprite, 0, 0, 0, 0, 0, 0);
                return;
            }
            case 99: // underwater type 2
            {
                short nSector = sprite[nSprite].sectnum;
                SetAbove(nSector, nHitag);
                SectFlag[nSector] |= kSectUnderwater;

                mydeletesprite(nSprite);
                return;
            }
            case 98:
            {
                short nSector = sprite[nSprite].sectnum;
                SetBelow(nSector, nHitag);
                SnapSectors(nSector, nHitag, 1);

                mydeletesprite(nSprite);
                return;
            }
            case 97:
            {
                AddSectorBob(sprite[nSprite].sectnum, nHitag, 1);

                mydeletesprite(nSprite);
                return;
            }
            case 96: // Lava sector
            {
                int nDamage = nHitag / 4;
                if (!nDamage) {
                    nDamage = 1;
                }

                short nSector = sprite[nSprite].sectnum;

                SectDamage[nSector] = nDamage;
                SectFlag[nSector] |= kSectLava;

                mydeletesprite(nSprite);
                return;
            }
            case 95:
            {
                AddSectorBob(sprite[nSprite].sectnum, nHitag, 0);

                mydeletesprite(nSprite);
                return;
            }
            case 94: // water
            {
                short nSector = sprite[nSprite].sectnum;
                SectDepth[nSector] = nHitag << 8;

                mydeletesprite(nSprite);
                return;
            }
            case 93:
            {
                BuildBubbleMachine(nSprite);
                return;
            }
            case 90:
            {
                BuildObject(nSprite, 3, nHitag);
                return;
            }
            case 79:
            case 89:
            {
                short nSector = sprite[nSprite].sectnum;

                SectSpeed[nSector] = nSpeed;
                SectFlag[nSector] |= sprite[nSprite].ang;

                mydeletesprite(nSprite);
                return;
            }
            case 88:
            {
                AddFlow(nSprite, nSpeed, 0);

                mydeletesprite(nSprite);
                return;
            }
            case 80: // underwater
            {
                short nSector = sprite[nSprite].sectnum;
                SectFlag[nSector] |= kSectUnderwater;

                mydeletesprite(nSprite);
                return;
            }
            case 78:
            {
                AddFlow(nSprite, nSpeed, 1);

                short nSector = sprite[nSprite].sectnum;
                SectFlag[nSector] |= 0x8000;

                mydeletesprite(nSprite);
                return;
            }
            case 77:
            {
                int nArrow = BuildArrow(nSprite, nSpeed);

                runlist_AddRunRec(sRunChannels[nChannel].a, nArrow);
                return;
            }
            case 76: // Explosion Trigger (Exploding Fire Cauldron)
            {
                BuildObject(nSprite, 0, nHitag);
                return;
            }
            case 75: // Explosion Target (Cauldrons, fireballs and grenades will destroy nearby 75 sprites)
            {
                BuildObject(nSprite, 1, nHitag);
                return;
            }
            case 71:
            {
                int nFireball = BuildFireBall(nSprite, nHitag, nSpeed);

                runlist_AddRunRec(sRunChannels[nChannel].a, nFireball);
                return;
            }
            case 70:
            {
                BuildDrip(nSprite);
                return;
            }
            case 63:
            {
                changespritestat(nSprite, 405);
                sprite[nSprite].cstat = 0x8000;
                return;
            }
            case 62:
            {
                nNetStartSprite[nNetStartSprites] = nSprite;
                sprite[nSprite].cstat = 0x8000;

                nNetStartSprites++;
                return;
            }
            case kTagRamses: // Ramses head
            {
                nSpiritSprite = nSprite;
                sprite[nSprite].cstat |= 0x8000;
                return;
            }
            default: // TODO - checkme!
            {
                mydeletesprite(nSprite);
                return;
            }
        }
    }

    mydeletesprite(nSprite);
}

void ExamineSprites()
{
    nNetStartSprites = 0;
    nCurStartSprite = 0;

    for (int nSprite = 0; nSprite < kMaxSprites; nSprite++)
    {
        int nStatus = sprite[nSprite].statnum;
        if (!nStatus)
        {
            short lotag = sprite[nSprite].lotag;
            short hitag = sprite[nSprite].hitag;

            if ((nStatus < kMaxStatus) && lotag)
            {
                sprite[nSprite].lotag = 0;
                sprite[nSprite].hitag = 0;

                ProcessSpriteTag(nSprite, lotag, hitag);
            }
            else
            {
                changespritestat(nSprite, 0);
            }
        }
    }

    if (nNetPlayerCount)
    {
        int nSprite = insertsprite(initsect, 0);
        sprite[nSprite].x = initx;
        sprite[nSprite].y = inity;
        sprite[nSprite].z = initz;
        sprite[nSprite].cstat = 0x8000;
        nNetStartSprite[nNetStartSprites] = nSprite;
        nNetStartSprites++;
    }
}

void LoadObjects()
{
    runlist_InitRun();
    runlist_InitChan();
    InitLink();
    InitPoint();
    InitSlide();
    InitSwitch();
    InitElev();
    InitWallFace();
    InitTimeSlot();
    InitSectFlag();

    for (int nSector = 0; nSector < numsectors; nSector++)
    {
        short hitag = sector[nSector].hitag;
        short lotag = sector[nSector].lotag;

        sector[nSector].hitag = 0;
        sector[nSector].lotag = 0;
        sector[nSector].extra = -1;

        if (hitag || lotag)
        {
            sector[nSector].lotag = runlist_HeadRun() + 1;
            sector[nSector].hitag = lotag;

            runlist_ProcessSectorTag(nSector, lotag, hitag);
        }
    }

    for (int nWall = 0; nWall < numwalls; nWall++)
    {
        wall[nWall].extra = -1;

        short lotag = wall[nWall].lotag;
        short hitag = wall[nWall].hitag;

        wall[nWall].lotag = 0;

        if (hitag || lotag)
        {
            wall[nWall].lotag = runlist_HeadRun() + 1;
            runlist_ProcessWallTag(nWall, lotag, hitag);
        }
    }

    ExamineSprites();
    PostProcess();
    InitRa();
    InitChunks();

    for (int nChannel = 0; nChannel < kMaxChannels; nChannel++)
    {
        runlist_ChangeChannel(nChannel, 0);
        runlist_ReadyChannel(nChannel);
    }

    nCamerax = initx;
    nCameray = inity;
    nCameraz = initz;
}

int myloadconfig()
{

    return 1;
}

int mysaveconfig()
{
    return 1;
}

static SavegameHelper sgh("init",
    SV(initx),
    SV(inity),
    SV(initz),
    SV(inita),
    SV(initsect),
    SV(nCurChunkNum),
    SA(nBodyGunSprite),
    SV(nCurBodyGunNum),
    SA(SectSoundSect),
    SA(SectSound),
    SA(SectFlag),
    SA(SectDepth),
    SA(SectAbove),
    SA(SectDamage),
    SA(SectSpeed),
    SA(SectBelow),
    nullptr);


END_PS_NS
