//
// 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 "h2def.h"
#include "m_random.h"
#include "p_local.h"

//============================================================================
//
//      T_Light
//
//============================================================================

void T_Light(thinker_t *thinker)
{
    light_t *light = (light_t *) thinker;
    if (light->count)
    {
        light->count--;
        return;
    }
    switch (light->type)
    {
        case LITE_FADE:
            light->sector->lightlevel =
                ((light->sector->lightlevel << FRACBITS) +
                 light->value2) >> FRACBITS;
            if (light->tics2 == 1)
            {
                if (light->sector->lightlevel >= light->value1)
                {
                    light->sector->lightlevel = light->value1;
                    P_RemoveThinker(&light->thinker);
                }
            }
            else if (light->sector->lightlevel <= light->value1)
            {
                light->sector->lightlevel = light->value1;
                P_RemoveThinker(&light->thinker);
            }
            break;
        case LITE_GLOW:
            light->sector->lightlevel =
                ((light->sector->lightlevel << FRACBITS) +
                 light->tics1) >> FRACBITS;
            if (light->tics2 == 1)
            {
                if (light->sector->lightlevel >= light->value1)
                {
                    light->sector->lightlevel = light->value1;
                    light->tics1 = -light->tics1;
                    light->tics2 = -1;  // reverse direction
                }
            }
            else if (light->sector->lightlevel <= light->value2)
            {
                light->sector->lightlevel = light->value2;
                light->tics1 = -light->tics1;
                light->tics2 = 1;       // reverse direction
            }
            break;
        case LITE_FLICKER:
            if (light->sector->lightlevel == light->value1)
            {
                light->sector->lightlevel = light->value2;
                light->count = (P_Random() & 7) + 1;
            }
            else
            {
                light->sector->lightlevel = light->value1;
                light->count = (P_Random() & 31) + 1;
            }
            break;
        case LITE_STROBE:
            if (light->sector->lightlevel == light->value1)
            {
                light->sector->lightlevel = light->value2;
                light->count = light->tics2;
            }
            else
            {
                light->sector->lightlevel = light->value1;
                light->count = light->tics1;
            }
            break;
        default:
            break;
    }
}

//============================================================================
//
//      EV_SpawnLight
//
//============================================================================

boolean EV_SpawnLight(line_t * line, byte * arg, lighttype_t type)
{
    light_t *light;
    sector_t *sec;
    int secNum;
    int arg1, arg2, arg3, arg4;
    boolean think;
    boolean rtn;

    /*
    Original code; redundant considering that a byte value is always
    in the range 0-255:

    arg1 = arg[1] > 255 ? 255 : arg[1];
    arg1 = arg1 < 0 ? 0 : arg1;
    arg2 = arg[2] > 255 ? 255 : arg[2];
    arg2 = arg2 < 0 ? 0 : arg2;
    arg3 = arg[3] > 255 ? 255 : arg[3];
    arg3 = arg3 < 0 ? 0 : arg3;
    arg4 = arg[4] > 255 ? 255 : arg[4];
    arg4 = arg4 < 0 ? 0 : arg4;
    */

    arg1 = arg[1];
    arg2 = arg[2];
    arg3 = arg[3];
    arg4 = arg[4];

    secNum = -1;
    rtn = false;
    while ((secNum = P_FindSectorFromTag(arg[0], secNum)) >= 0)
    {
        think = false;
        sec = &sectors[secNum];

        light = (light_t *) Z_Malloc(sizeof(light_t), PU_LEVSPEC, 0);
        light->type = type;
        light->sector = sec;
        light->count = 0;
        rtn = true;
        switch (type)
        {
            case LITE_RAISEBYVALUE:
                sec->lightlevel += arg1;
                if (sec->lightlevel > 255)
                {
                    sec->lightlevel = 255;
                }
                break;
            case LITE_LOWERBYVALUE:
                sec->lightlevel -= arg1;
                if (sec->lightlevel < 0)
                {
                    sec->lightlevel = 0;
                }
                break;
            case LITE_CHANGETOVALUE:
                sec->lightlevel = arg1;
                if (sec->lightlevel < 0)
                {
                    sec->lightlevel = 0;
                }
                else if (sec->lightlevel > 255)
                {
                    sec->lightlevel = 255;
                }
                break;
            case LITE_FADE:
                think = true;
                light->value1 = arg1;   // destination lightlevel
                light->value2 = FixedDiv((arg1 - sec->lightlevel) << FRACBITS, arg2 << FRACBITS);       // delta lightlevel
                if (sec->lightlevel <= arg1)
                {
                    light->tics2 = 1;   // get brighter
                }
                else
                {
                    light->tics2 = -1;
                }
                break;
            case LITE_GLOW:
                think = true;
                light->value1 = arg1;   // upper lightlevel
                light->value2 = arg2;   // lower lightlevel
                light->tics1 = FixedDiv((arg1 - sec->lightlevel) << FRACBITS, arg3 << FRACBITS);        // lightlevel delta
                if (sec->lightlevel <= arg1)
                {
                    light->tics2 = 1;   // get brighter
                }
                else
                {
                    light->tics2 = -1;
                }
                break;
            case LITE_FLICKER:
                think = true;
                light->value1 = arg1;   // upper lightlevel
                light->value2 = arg2;   // lower lightlevel
                sec->lightlevel = light->value1;
                light->count = (P_Random() & 64) + 1;
                break;
            case LITE_STROBE:
                think = true;
                light->value1 = arg1;   // upper lightlevel
                light->value2 = arg2;   // lower lightlevel
                light->tics1 = arg3;    // upper tics
                light->tics2 = arg4;    // lower tics
                light->count = arg3;
                sec->lightlevel = light->value1;
                break;
            default:
                rtn = false;
                break;
        }
        if (think)
        {
            P_AddThinker(&light->thinker);
            light->thinker.function = T_Light;
        }
        else
        {
            Z_Free(light);
        }
    }
    return rtn;
}

//============================================================================
//
//      T_Phase
//
//============================================================================

int PhaseTable[64] = {
    128, 112, 96, 80, 64, 48, 32, 32,
    16, 16, 16, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 16, 16, 16,
    32, 32, 48, 64, 80, 96, 112, 128
};

void T_Phase(thinker_t *thinker)
{
    phase_t *phase = (phase_t *) thinker;
    phase->index = (phase->index + 1) & 63;
    phase->sector->lightlevel = phase->base + PhaseTable[phase->index];
}

//==========================================================================
//
// P_SpawnPhasedLight
//
//==========================================================================

void P_SpawnPhasedLight(sector_t * sector, int base, int index)
{
    phase_t *phase;

    phase = Z_Malloc(sizeof(*phase), PU_LEVSPEC, 0);
    P_AddThinker(&phase->thinker);
    phase->sector = sector;
    if (index == -1)
    {                           // sector->lightlevel as the index
        phase->index = sector->lightlevel & 63;
    }
    else
    {
        phase->index = index & 63;
    }
    phase->base = base & 255;
    sector->lightlevel = phase->base + PhaseTable[phase->index];
    phase->thinker.function = T_Phase;

    sector->special = 0;
}

//==========================================================================
//
// P_SpawnLightSequence
//
//==========================================================================

void P_SpawnLightSequence(sector_t * sector, int indexStep)
{
    sector_t *sec;
    sector_t *nextSec;
    sector_t *tempSec;
    int seqSpecial;
    int i;
    int count;
    fixed_t index;
    fixed_t indexDelta;
    int base;

    seqSpecial = LIGHT_SEQUENCE;        // look for Light_Sequence, first
    sec = sector;
    count = 1;
    do
    {
        nextSec = NULL;
        sec->special = LIGHT_SEQUENCE_START;    // make sure that the search doesn't back up.
        for (i = 0; i < sec->linecount; i++)
        {
            tempSec = getNextSector(sec->lines[i], sec);
            if (!tempSec)
            {
                continue;
            }
            if (tempSec->special == seqSpecial)
            {
                if (seqSpecial == LIGHT_SEQUENCE)
                {
                    seqSpecial = LIGHT_SEQUENCE_ALT;
                }
                else
                {
                    seqSpecial = LIGHT_SEQUENCE;
                }
                nextSec = tempSec;
                count++;
            }
        }
        sec = nextSec;
    }
    while (sec);

    sec = sector;
    count *= indexStep;
    index = 0;
    indexDelta = FixedDiv(64 * FRACUNIT, count * FRACUNIT);
    base = sector->lightlevel;
    do
    {
        nextSec = NULL;
        if (sec->lightlevel)
        {
            base = sec->lightlevel;
        }
        P_SpawnPhasedLight(sec, base, index >> FRACBITS);
        index += indexDelta;
        for (i = 0; i < sec->linecount; i++)
        {
            tempSec = getNextSector(sec->lines[i], sec);
            if (!tempSec)
            {
                continue;
            }
            if (tempSec->special == LIGHT_SEQUENCE_START)
            {
                nextSec = tempSec;
            }
        }
        sec = nextSec;
    }
    while (sec);
}
