//-------------------------------------------------------------------------
/*
Copyright (C) 1997, 2005 - 3D Realms Entertainment

This file is part of Shadow Warrior version 1.2

Shadow Warrior 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.

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.

Original Source: 1997 - Frank Maddin and Jim Norwood
Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
*/
//-------------------------------------------------------------------------
#include "ns.h"
#include "build.h"

#include "names2.h"
#include "panel.h"
#include "misc.h"

BEGIN_SW_NS

////////////////////////////////////////////////////////////////////////////////
//
// WARPING - PLANE STYLE
//
////////////////////////////////////////////////////////////////////////////////

extern bool Prediction;
DSWActor* WarpToArea(DSWActor* sp_from, DVector3& pos, sectortype** sect);

bool WarpPlaneSectorInfo(sectortype* sect, DSWActor** sp_ceiling, DSWActor** sp_floor)
{
    *sp_floor = nullptr;
    *sp_ceiling = nullptr;

    if (Prediction)
        return false;

    if (sect== nullptr || !(sect->extra & SECTFX_WARP_SECTOR))
        return false;

    SWStatIterator it(STAT_WARP);
    while (auto actor = it.Next())
    {
        if (actor->sector() == sect)
        {
            // skip - don't teleport
            if (SP_TAG10(actor) == 1)
                continue;

            if (actor->spr.hitag == WARP_CEILING_PLANE)
            {
                *sp_ceiling = actor;
            }
            else if (actor->spr.hitag == WARP_FLOOR_PLANE)
            {
                *sp_floor = actor;
            }
        }
    }

    return true;
}

//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------

DSWActor* WarpPlane(DVector3& pos, sectortype** sect, double testz)
{
    DSWActor* sp_floor,* sp_ceiling;

    if (testz == DBL_MAX) testz = pos.Z;

    if (Prediction)
        return nullptr;

    if (!WarpPlaneSectorInfo(*sect, &sp_ceiling, &sp_floor))
        return nullptr;

    if (sp_ceiling)
    {
        if (pos.Z <= sp_ceiling->spr.pos.Z)
        {
            return WarpToArea(sp_ceiling, pos, sect);
        }
    }

    if (sp_floor)
    {
        if (pos.Z >= sp_floor->spr.pos.Z)
        {
            return WarpToArea(sp_floor, pos, sect);
        }
    }

    return nullptr;
}

//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------

DSWActor* WarpToArea(DSWActor* sp_from, DVector3& pos, sectortype** sect)
{
    short match;
    short to_tag = 0;
    short match_rand[16];
    double z_adj = 0;

    auto off = pos - sp_from->spr.pos;
    match = sp_from->spr.lotag;

#if 0
    TAG 2 = match
            TAG 3 = Type
                    Sprite - 0,32 always teleports you to the center at the angle the sprite is facing
    Offset - 1 always teleports you by the offset.Does not touch the angle
    TAG 4 = angle
            TAG 5 to 8 = random match locations
#endif

    memset(match_rand,0,sizeof(match_rand));

    switch (sp_from->spr.hitag)
    {
    case WARP_TELEPORTER:
        to_tag = WARP_TELEPORTER;

        // if tag 5 has something this is a random teleporter
        if (SP_TAG5(sp_from))
        {
            short ndx = 0;
            match_rand[ndx++] = SP_TAG2(sp_from);
            match_rand[ndx++] = SP_TAG5(sp_from);
            if (SP_TAG6(sp_from))
                match_rand[ndx++] = SP_TAG6(sp_from);
            if (SP_TAG7(sp_from))
                match_rand[ndx++] = SP_TAG7(sp_from);
            if (SP_TAG8(sp_from))
                match_rand[ndx++] = SP_TAG8(sp_from);

            // reset the match you are looking for
            match = match_rand[RandomRange(ndx)];
        }
        break;
    case WARP_CEILING_PLANE:
        to_tag = WARP_FLOOR_PLANE;
        // make sure you warp outside of warp plane
        z_adj = -2;
        break;
    case WARP_FLOOR_PLANE:
        to_tag = WARP_CEILING_PLANE;
        // make sure you warp outside of warp plane
        z_adj = 2;
        break;
    }

    SWStatIterator it(STAT_WARP);
    while (auto actor = it.Next())
    {
        if (actor->spr.lotag == match && actor != sp_from)
        {
            // exp: WARP_CEILING or WARP_CEILING_PLANE
            if (actor->spr.hitag == to_tag)
            {
                if (!actor->insector())
                    return nullptr;

                // determine new x,y,z position
                pos = actor->spr.pos + off;

                // make sure you warp outside of warp plane
                pos.Z += z_adj;

                // get new sector
                *sect = actor->sector();
                updatesector(pos, sect);

                return actor;
            }
        }
    }

    return nullptr;
}

////////////////////////////////////////////////////////////////////////////////
//
// Warp - Teleporter style
//
////////////////////////////////////////////////////////////////////////////////

bool WarpSectorInfo(sectortype* sect, DSWActor** sp_warp)
{
    *sp_warp = nullptr;

    if (!sect || !(sect->extra & SECTFX_WARP_SECTOR))
        return false;

    SWStatIterator it(STAT_WARP);
    while (auto actor = it.Next())
    {
        if (actor->sector() == sect)
        {
            // skip - don't teleport
            if (SP_TAG10(actor) == 1)
                continue;

            if (actor->spr.hitag == WARP_TELEPORTER)
            {
                *sp_warp = actor;
            }
        }
    }

    return true;
}

//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------

DSWActor* Warp(DVector3& pos, sectortype** sect)
{
    DSWActor* sp_warp;

    if (Prediction)
        return nullptr;

    if (!WarpSectorInfo(*sect, &sp_warp))
        return nullptr;

    if (sp_warp)
    {
        return WarpToArea(sp_warp, pos, sect);
    }

    return nullptr;
}
END_SW_NS
