//-------------------------------------------------------------------------
/*
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 "game.h"
#include "light.h"

BEGIN_SW_NS


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

void SectorLightShade(DSWActor* actor, short intensity)
{
    int8_t* wall_shade;
    short base_shade;

    if (TEST_BOOL8(actor))
        intensity = -intensity;

    if (!TEST_BOOL2(actor))
    {
        if (!TEST_BOOL6(actor))
            actor->sector()->floorpal = actor->spr.pal;
        actor->sector()->floorshade = LIGHT_FloorShade(actor) + intensity;     // floor change
    }

    if (!TEST_BOOL3(actor))
    {
        if (!TEST_BOOL6(actor))
            actor->sector()->ceilingpal = actor->spr.pal;
        actor->sector()->ceilingshade = LIGHT_CeilingShade(actor) + intensity;   // ceiling change
    }

    // change wall
    if (!TEST_BOOL4(actor))
    {
        ASSERT(actor->hasU() && actor->user.WallShade.Data());
        wall_shade = actor->user.WallShade.Data();
        int wallcount = 0;

        for(auto &wal : actor->sector()->walls)
        {
            base_shade = wall_shade[wallcount];
            wal.shade = base_shade + intensity;
            if (!TEST_BOOL6(actor))
                wal.pal = actor->spr.pal;
            wallcount++;

            if ((actor->spr.extra & SPRX_BOOL5))
            {
                if (wal.twoSided())
                {
                    auto nextWall = wal.nextWall();
                    base_shade = wall_shade[wallcount];
                    nextWall->shade = base_shade + intensity;
                    if (!TEST_BOOL6(actor))
                        nextWall->pal = actor->spr.pal;
                    wallcount++;
                }
            }
        }
    }
}


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

void DiffuseLighting(DSWActor* actor)
{
    int i;
    short count;
    short shade;

    // diffused lighting
    count = 0;
    SWStatIterator it(STAT_LIGHTING_DIFFUSE);
    while (auto itActor = it.Next())
    {
        // make sure matchs match
        if (LIGHT_Match(itActor) != LIGHT_Match(actor))
            continue;

        shade = actor->spr.shade + ((LIGHT_DiffuseNum(itActor) + 1) * LIGHT_DiffuseMult(itActor));

        if (shade > LIGHT_MaxDark(actor))
            shade = LIGHT_MaxDark(actor);

        if (!TEST_BOOL6(itActor))
            itActor->spr.pal = actor->spr.pal;

        SectorLightShade(itActor, shade);

        count++;
    }
}

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

void DoLightingMatch(short match, short state)
{
    SWStatIterator it(STAT_LIGHTING);
    while (auto itActor = it.Next())
    {
        if (LIGHT_Match(itActor) != match)
            continue;

        switch (LIGHT_Type(itActor))
        {
        case LIGHT_CONSTANT:

            // initialized
            SET_BOOL9(itActor);

            // toggle
            if (state == -1)
                state = !TEST_BOOL1(itActor);

            if (state == 1)
            {
                SET_BOOL1(itActor);
                itActor->spr.shade = -LIGHT_MaxBright(itActor);
                itActor->spr.pal = itActor->user.spal; // on
                SectorLightShade(itActor, itActor->spr.shade);
                DiffuseLighting(itActor);
            }
            else
            {
                RESET_BOOL1(itActor);
                itActor->spr.shade = LIGHT_MaxDark(itActor);
                itActor->spr.pal = 0; // off
                SectorLightShade(itActor, itActor->spr.shade);
                DiffuseLighting(itActor);
            }
            break;

        case LIGHT_FLICKER:
        case LIGHT_FADE:
            // initialized
            SET_BOOL9(itActor);

            // toggle
            if (state == -1)
                state = !TEST_BOOL1(itActor);

            if (state == 1)
            {
                // allow fade or flicker
                SET_BOOL1(itActor);
            }
            else
            {
                RESET_BOOL1(itActor);
                itActor->spr.shade = LIGHT_MaxDark(itActor);
                SectorLightShade(itActor, itActor->spr.shade);
                DiffuseLighting(itActor);
            }
            break;

        case LIGHT_FADE_TO_ON_OFF:

            // initialized
            SET_BOOL9(itActor);

            // toggle
            //if (state == -1)
            //    state = !TEST_BOOL1(itActor);

            if (state == 1)
            {
                if (LIGHT_Dir(itActor) == 1)
                {
                    LIGHT_DirChange(itActor);
                }
            }
            else if (state == 0)
            {
                if (LIGHT_Dir(itActor) == 0)
                {
                    LIGHT_DirChange(itActor);
                }
            }

            // allow fade or flicker
            SET_BOOL1(itActor);
            break;

        case LIGHT_FLICKER_ON:

            // initialized
            SET_BOOL9(itActor);

            // toggle
            if (state == -1)
                state = !TEST_BOOL1(itActor);

            if (state == 1)
            {
                // allow fade or flicker
                SET_BOOL1(itActor);
            }
            else
            {
                // turn it off till next switch
                auto spal = itActor->spr.pal;
                RESET_BOOL1(itActor);
                itActor->spr.pal = 0;
                itActor->spr.shade = LIGHT_MaxDark(itActor);
                SectorLightShade(itActor, itActor->spr.shade);
                DiffuseLighting(itActor);
                itActor->spr.pal = spal;
            }
            break;
        }
    }
}

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

void InitLighting(void)
{
    // processed on level startup
    // puts lights in correct state
    SWStatIterator it(STAT_LIGHTING);
    while (auto actor = it.Next())
    {
        if (!TEST_BOOL9(actor))
        {
            DoLightingMatch(LIGHT_Match(actor), !!TEST_BOOL1(actor));
        }
    }
}

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

void DoLighting(void)
{
    SWStatIterator it(STAT_LIGHTING);
    while (auto itActor = it.Next())
    {
        // on/off test
        if (TEST_BOOL1(itActor) == 0)
            continue;

        switch (LIGHT_Type(itActor))
        {
        case LIGHT_CONSTANT:
            break;

        case LIGHT_FLICKER:

            LIGHT_Tics(itActor) += synctics;
            while (LIGHT_Tics(itActor) >= LIGHT_MaxTics(itActor))
            {
                LIGHT_Tics(itActor) -= LIGHT_MaxTics(itActor);

                if ((RANDOM_P2(128 << 8) >> 8) > 64)
                {
                    itActor->spr.shade = -LIGHT_MaxBright(itActor) + RandomRange(LIGHT_MaxBright(itActor) + LIGHT_MaxDark(itActor));
                    SectorLightShade(itActor, itActor->spr.shade);
                    DiffuseLighting(itActor);
                }
                else
                {
                    // turn off lighting - even colored lighting
                    auto spal = itActor->spr.pal;
                    itActor->spr.pal = 0;
                    itActor->spr.shade = LIGHT_MaxDark(itActor);
                    SectorLightShade(itActor, itActor->spr.shade);
                    DiffuseLighting(itActor);
                    itActor->spr.pal = spal;
                }
            }

            break;

        case LIGHT_FADE:

            LIGHT_Tics(itActor) += synctics;

            while (LIGHT_Tics(itActor) >= LIGHT_MaxTics(itActor))
            {
                LIGHT_Tics(itActor) -= LIGHT_MaxTics(itActor);

                if (LIGHT_Dir(itActor) == 1)
                {
                    itActor->spr.shade += LIGHT_ShadeInc(itActor);
                    if (itActor->spr.shade >= LIGHT_MaxDark(itActor))
                        LIGHT_DirChange(itActor);
                }
                else
                {
                    itActor->spr.shade -= LIGHT_ShadeInc(itActor);
                    if (itActor->spr.shade <= -LIGHT_MaxBright(itActor))
                        LIGHT_DirChange(itActor);
                }

                SectorLightShade(itActor, itActor->spr.shade);
                DiffuseLighting(itActor);
            }

            break;

        case LIGHT_FADE_TO_ON_OFF:

            LIGHT_Tics(itActor) += synctics;

            while (LIGHT_Tics(itActor) >= LIGHT_MaxTics(itActor))
            {
                LIGHT_Tics(itActor) -= LIGHT_MaxTics(itActor);

                if (LIGHT_Dir(itActor) == 1)
                {
                    itActor->spr.shade += LIGHT_ShadeInc(itActor);
                    if (itActor->spr.shade >= LIGHT_MaxDark(itActor))
                    {
                        itActor->spr.pal = 0; // off
                        LIGHT_DirChange(itActor);
                        // stop it until switch is hit
                        RESET_BOOL1(itActor);
                    }
                }
                else
                {
                    itActor->spr.shade -= LIGHT_ShadeInc(itActor);
                    itActor->spr.pal = itActor->user.spal; // on
                    if (itActor->spr.shade <= -LIGHT_MaxBright(itActor))
                    {
                        LIGHT_DirChange(itActor);
                        // stop it until switch is hit
                        RESET_BOOL1(itActor);
                    }
                }

                SectorLightShade(itActor, itActor->spr.shade);
                DiffuseLighting(itActor);
            }

            break;

        case LIGHT_FLICKER_ON:

            LIGHT_Tics(itActor) += synctics;

            while (LIGHT_Tics(itActor) >= LIGHT_MaxTics(itActor))
            {
                LIGHT_Tics(itActor) -= LIGHT_MaxTics(itActor);

                if ((RANDOM_P2(128 << 8) >> 8) > 64)
                {
                    itActor->spr.shade = -LIGHT_MaxBright(itActor) + RandomRange(LIGHT_MaxBright(itActor) + LIGHT_MaxDark(itActor));
                    SectorLightShade(itActor, itActor->spr.shade);
                    DiffuseLighting(itActor);
                }
                else
                {
                    // turn off lighting - even colored lighting
                    auto spal = itActor->spr.pal;
                    itActor->spr.pal = 0;
                    itActor->spr.shade = LIGHT_MaxDark(itActor);
                    SectorLightShade(itActor, itActor->spr.shade);
                    DiffuseLighting(itActor);
                    itActor->spr.pal = spal;
                }

                if ((RANDOM_P2(128 << 8) >> 8) < 8)
                {
                    // set to full brightness
                    itActor->spr.shade = -LIGHT_MaxBright(itActor);
                    SectorLightShade(itActor, itActor->spr.shade);
                    DiffuseLighting(itActor);
                    // turn it off until a swith happens
                    RESET_BOOL1(itActor);
                }
            }
            break;
        }
    }
}
END_SW_NS
