/* bzflag
 * Copyright (c) 1993-2025 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/* interface header */
#include "EffectsMenu.h"

/* common implementation headers */
#include "TextUtils.h"
#include "FontManager.h"
#include "StateDatabase.h"

/* local implementation headers */
#include "MainMenu.h"
#include "TrackMarks.h"
#include "HUDuiList.h"
#include "HUDuiLabel.h"
#include "effectsRenderer.h"


EffectsMenu::EffectsMenu()
{
    // add controls
    std::vector<HUDuiControl*>& listHUD = getControls();

    // cache font face ID
    int fontFace = MainMenu::getFontFace();

    // the menu label
    HUDuiLabel* label = new HUDuiLabel;
    label->setFontFace(fontFace);
    label->setString("Effects Settings");
    listHUD.push_back(label);

    // the menu options
    HUDuiList* option;
    std::vector<std::string>* options;

    // Rain Scale
    option = new HUDuiList;
    option->setFontFace(MainMenu::getFontFace());
    option->setLabel("Rain:");
    option->setCallback(callback, "r");
    options = &option->getList();
    options->push_back(std::string("Off"));
    option->createSlider(10);
    option->update();
    listHUD.push_back(option);

    // The Mirror
    option = new HUDuiList;
    option->setFontFace(fontFace);
    option->setLabel("Mirror:");
    option->setCallback(callback, "m");
    options = &option->getList();
    options->push_back(std::string("Off"));
    options->push_back(std::string("On"));
    option->update();
    listHUD.push_back(option);

    // Fog Effect
    option = new HUDuiList;
    option->setFontFace(fontFace);
    option->setLabel("Fog:");
    option->setCallback(callback, "F");
    options = &option->getList();
    options->push_back(std::string("Fast"));
    options->push_back(std::string("Nice"));
    option->update();
    listHUD.push_back(option);

    // Shot Length (Viewport)
    option = new HUDuiList;
    option->setFontFace(MainMenu::getFontFace());
    option->setLabel("Shot Length:");
    option->setCallback(callback, "x");
    options = &option->getList();
    options->push_back(std::string("Off"));
    option->createSlider(10);
    option->update();
    listHUD.push_back(option);

    // Display Treads
    option = new HUDuiList;
    option->setFontFace(fontFace);
    option->setLabel("Display Treads:");
    option->setCallback(callback, "T");
    options = &option->getList();
    options->push_back(std::string("Off"));
    options->push_back(std::string("On"));
    option->update();
    listHUD.push_back(option);

    // Animated Treads
    option = new HUDuiList;
    option->setFontFace(fontFace);
    option->setLabel("Animated Treads:");
    option->setCallback(callback, "a");
    options = &option->getList();
    options->push_back(std::string("Off"));
    options->push_back(std::string("Covered"));
    options->push_back(std::string("Exposed"));
    option->update();
    listHUD.push_back(option);

    // Track Mark Fading Scale
    option = new HUDuiList;
    option->setFontFace(MainMenu::getFontFace());
    option->setLabel("Track Marks:");
    option->setCallback(callback, "t");
    options = &option->getList();
    options->push_back(std::string("Off"));
    option->createSlider(10);
    option->update();
    listHUD.push_back(option);

    // Track Mark Culling Type
    option = new HUDuiList;
    option->setFontFace(MainMenu::getFontFace());
    option->setLabel("Track Mark Culling:");
    option->setCallback(callback, "c");
    options = &option->getList();
    options->push_back(std::string("Off"));
    options->push_back(std::string("Fast"));
    options->push_back(std::string("Best"));
    option->update();
    listHUD.push_back(option);

    // Fancy effects from effectsRenderer
    option = new HUDuiList;
    option->setFontFace(MainMenu::getFontFace());
    option->setLabel("Fancy Effects:");
    option->setCallback(callback, "f");
    options = &option->getList();
    options->push_back(std::string("Off"));
    options->push_back(std::string("On"));
    option->update();
    listHUD.push_back(option);

    std::vector<std::string> optbuf;

    // Fancy effects I: Spawn
    option = new HUDuiList;
    option->setFontFace(MainMenu::getFontFace());
    option->setLabel("Spawn Effect:");
    option->setCallback(callback, "s");
    options = &option->getList();
    optbuf = EFFECTS.getSpawnEffectTypes();
    options->assign(optbuf.begin(), optbuf.end());
    option->update();
    listHUD.push_back(option);

    // Fancy effects Ia: Local spawn
    option = new HUDuiList;
    option->setFontFace(MainMenu::getFontFace());
    option->setLabel("Local Spawn Effect:");
    option->setCallback(callback, "L");
    options = &option->getList();
    options->push_back(std::string("Off"));
    options->push_back(std::string("Same as spawns"));
    option->update();
    listHUD.push_back(option);

    // Fancy effects II: Death
    option = new HUDuiList;
    option->setFontFace(MainMenu::getFontFace());
    option->setLabel("Death Effect:");
    option->setCallback(callback, "d");
    options = &option->getList();
    optbuf = EFFECTS.getDeathEffectTypes();
    options->assign(optbuf.begin(), optbuf.end());
    option->update();
    listHUD.push_back(option);

    // Fancy effects III: Shots
    option = new HUDuiList;
    option->setFontFace(MainMenu::getFontFace());
    option->setLabel("Shot Fired Effect:");
    option->setCallback(callback, "S");
    options = &option->getList();
    optbuf = EFFECTS.getShotEffectTypes();
    options->assign(optbuf.begin(), optbuf.end());
    option->update();
    listHUD.push_back(option);

    // Fancy effects IV: Local shots
    option = new HUDuiList;
    option->setFontFace(MainMenu::getFontFace());
    option->setLabel("Local Shot Effect:");
    option->setCallback(callback, "l");
    options = &option->getList();
    options->push_back(std::string("Off"));
    options->push_back(std::string("Same as shots"));
    option->update();
    listHUD.push_back(option);

    // Fancy effects V: Velocity for shot effects
    option = new HUDuiList;
    option->setFontFace(MainMenu::getFontFace());
    option->setLabel("Use Velocity on Shot Effects:");
    option->setCallback(callback, "V");
    options = &option->getList();
    options->push_back(std::string("Off"));
    options->push_back(std::string("On"));
    option->update();
    listHUD.push_back(option);

    // Fancy effects VI: Landing effects
    option = new HUDuiList;
    option->setFontFace(MainMenu::getFontFace());
    option->setLabel("Landing Effect:");
    option->setCallback(callback, "b");
    options = &option->getList();
    optbuf = EFFECTS.getLandEffectTypes();
    options->assign(optbuf.begin(), optbuf.end());
    option->update();
    listHUD.push_back(option);

    // Fancy effects VII: GM Smoke Trail effects
    option = new HUDuiList;
    option->setFontFace(MainMenu::getFontFace());
    option->setLabel("GM Smoke Effect:");
    option->setCallback(callback, "G");
    options = &option->getList();
    optbuf = EFFECTS.getGMPuffEffectTypes();
    options->assign(optbuf.begin(), optbuf.end());
    option->update();
    listHUD.push_back(option);

    // Fancy effects VIIa: GM puff timing
    option = new HUDuiList;
    option->setFontFace(MainMenu::getFontFace());
    option->setLabel("GM Effect Timing:");
    option->setCallback(callback, "g");
    option->createSlider(11);
    option->update();
    listHUD.push_back(option);

    // Fancy effects VIII: rico effects
    option = new HUDuiList;
    option->setFontFace(MainMenu::getFontFace());
    option->setLabel("  Ricochet Effect:");
    option->setCallback(callback, "R");
    options = &option->getList();
    optbuf = EFFECTS.getRicoEffectTypes();
    options->assign(optbuf.begin(), optbuf.end());
    option->update();
    listHUD.push_back(option);

    // Fancy effects IX: shot Teleporter effects
    option = new HUDuiList;
    option->setFontFace(MainMenu::getFontFace());
    option->setLabel("  Teleport Shot Effect:");
    option->setCallback(callback, "7");
    options = &option->getList();
    optbuf = EFFECTS.getShotTeleportEffectTypes();
    options->assign(optbuf.begin(), optbuf.end());
    option->update();
    listHUD.push_back(option);

    initNavigation(listHUD, 1, listHUD.size() - 1);
}


EffectsMenu::~EffectsMenu()
{
}


void EffectsMenu::execute()
{
}


void EffectsMenu::resize(int _width, int _height)
{
    HUDDialog::resize(_width, _height);

    // use a big font for title, smaller font for the rest
    const float titleFontSize = (float)_height / 15.0f;
    const float fontSize = (float)_height / 60.0f;
    FontManager &fm = FontManager::instance();

    // reposition title
    std::vector<HUDuiControl*>& listHUD = getControls();
    HUDuiLabel* title = (HUDuiLabel*)listHUD[0];
    title->setFontSize(titleFontSize);
    const float titleWidth =
        fm.getStrLength(MainMenu::getFontFace(), titleFontSize, title->getString());
    const float titleHeight =
        fm.getStrHeight(MainMenu::getFontFace(), titleFontSize, " ");
    float x = 0.5f * ((float)_width - titleWidth);
    float y = (float)_height - titleHeight;
    title->setPosition(x, y);

    // reposition options
    x = 0.5f * (float)_width;
    y -= 0.6f * titleHeight;
    const float h = fm.getStrHeight(MainMenu::getFontFace(), fontSize, " ");
    const int count = listHUD.size();
    int i;
    for (i = 1; i < count; i++)
    {
        listHUD[i]->setFontSize(fontSize);
        listHUD[i]->setPosition(x, y);
        // Add extra space after Fog, Shot Length, Animated Treads, and Track Mark Culling
        if ((i == 3) || (i == 4) || (i == 6) || (i == 8))
            y -= 1.75f * h;
        else
            y -= 1.0f * h;
    }

    // load current settings
    i = 1;
    ((HUDuiList*)listHUD[i++])->setIndex(int((BZDB.eval("userRainScale")
                                         * 10.0f) + 0.5f));
    ((HUDuiList*)listHUD[i++])->setIndex(BZDB.isTrue("userMirror") ? 1 : 0);
    ((HUDuiList*)listHUD[i++])->setIndex(BZDB.evalInt("fogEffect"));
    ((HUDuiList*)listHUD[i++])->setIndex(BZDB.evalInt("shotLength"));
    ((HUDuiList*)listHUD[i++])->setIndex(BZDB.isTrue("showTreads") ? 1 : 0);
    int treadIndex = 0;
    if (BZDB.isTrue("animatedTreads"))
    {
        treadIndex++;
        if (BZDB.isTrue("treadStyle"))
            treadIndex++;
    }
    ((HUDuiList*)listHUD[i++])->setIndex(treadIndex);
    ((HUDuiList*)listHUD[i++])->setIndex(int((TrackMarks::getUserFade() * 10.0f)
                                         + 0.5f));
    TrackMarks::AirCullStyle style = TrackMarks::getAirCulling();
    if (style == TrackMarks::NoAirCull)
        ((HUDuiList*)listHUD[i++])->setIndex(0);
    else if (style != TrackMarks::FullAirCull)
        ((HUDuiList*)listHUD[i++])->setIndex(1);
    else
        ((HUDuiList*)listHUD[i++])->setIndex(2);
    ((HUDuiList*)listHUD[i++])->setIndex(BZDB.isTrue("useFancyEffects") ? 1 : 0);
    ((HUDuiList*)listHUD[i++])->setIndex(BZDB.evalInt("spawnEffect"));
    ((HUDuiList*)listHUD[i++])->setIndex(BZDB.isTrue("enableLocalSpawnEffect") ? 1 : 0);
    ((HUDuiList*)listHUD[i++])->setIndex(BZDB.evalInt("deathEffect"));
    ((HUDuiList*)listHUD[i++])->setIndex(BZDB.evalInt("shotEffect"));
    ((HUDuiList*)listHUD[i++])->setIndex(BZDB.isTrue("enableLocalShotEffect") ? 1 : 0);
    ((HUDuiList*)listHUD[i++])->setIndex(BZDB.isTrue("useVelOnShotEffects") ? 1 : 0);
    ((HUDuiList*)listHUD[i++])->setIndex(BZDB.evalInt("landEffect"));
    ((HUDuiList*)listHUD[i++])->setIndex(BZDB.evalInt("gmPuffEffect"));
    ((HUDuiList*)listHUD[i++])->setIndex(static_cast<int>(1 / BZDB.eval("gmPuffTime") - 3));
    ((HUDuiList*)listHUD[i++])->setIndex(BZDB.evalInt("ricoEffect"));
    ((HUDuiList*)listHUD[i++])->setIndex(BZDB.evalInt("tpEffect"));
}


void EffectsMenu::callback(HUDuiControl* w, const void* data)
{
    HUDuiList* list = (HUDuiList*)w;

    switch (((const char*)data)[0])
    {
    case 'r':
    {
        int scale = list->getIndex();
        BZDB.setFloat("userRainScale", float(scale) / 10.0f);
        break;
    }
    case 'm':
    {
        BZDB.set("userMirror", list->getIndex() ? "1" : "0");
        break;
    }
    case 'F':
    {
        BZDB.setInt("fogEffect", list->getIndex());
        break;
    }
    case 'T':
    {
        BZDB.set("showTreads", list->getIndex() ? "1" : "0");
        break;
    }
    case 'a':
    {
        switch (list->getIndex())
        {
        case 1:
            BZDB.set("animatedTreads", "1");
            BZDB.set("treadStyle", "0");
            break;
        case 2:
            BZDB.set("animatedTreads", "1");
            BZDB.set("treadStyle", "1");
            break;
        default:
            BZDB.set("animatedTreads", "0");
            BZDB.set("treadStyle", "0");
            break;
        }
        RENDERER.setRebuildTanks();
        break;
    }
    case 't':
    {
        int fade = list->getIndex();
        TrackMarks::setUserFade(float(fade) / 10.0f);
        break;
    }
    case 'c':
    {
        int culling = list->getIndex();
        if (culling <= 0)
            TrackMarks::setAirCulling(TrackMarks::NoAirCull);
        else if (culling == 1)
            TrackMarks::setAirCulling(TrackMarks::InitAirCull);
        else
            TrackMarks::setAirCulling(TrackMarks::FullAirCull);
        break;
    }
    case 'x':
    {
        BZDB.setInt("shotLength", list->getIndex());
        RENDERER.notifyStyleChange(); // bolt glBlendFunc() may change
        break;
    }
    case 'f':
    {
        BZDB.set("useFancyEffects", list->getIndex() ? "1" : "0");
        break;
    }
    case 's':
    {
        BZDB.set("spawnEffect", TextUtils::format("%d", list->getIndex()));
        break;
    }
    case 'L':
    {
        BZDB.set("enableLocalSpawnEffect", list->getIndex() ? "1" : "0");
        break;
    }
    case 'd':
    {
        BZDB.set("deathEffect", TextUtils::format("%d", list->getIndex()));
        break;
    }
    case 'S':
    {
        BZDB.set("shotEffect", TextUtils::format("%d", list->getIndex()));
        break;
    }
    case 'l':
    {
        BZDB.set("enableLocalShotEffect", list->getIndex() ? "1" : "0");
        break;
    }
    case 'V':
    {
        BZDB.set("useVelOnShotEffects", list->getIndex() ? "1" : "0");
        break;
    }
    case 'b':
    {
        BZDB.set("landEffect", TextUtils::format("%d", list->getIndex()));
        break;
    }
    case 'G':
    {
        BZDB.set("gmPuffEffect", TextUtils::format("%d", list->getIndex()));
        break;
    }
    case 'g':
    {
        BZDB.set("gmPuffTime", TextUtils::format("1/(%d+3)", list->getIndex()));
        break;
    }
    case 'R':
    {
        BZDB.set("ricoEffect", TextUtils::format("%d", list->getIndex()));
        break;
    }
    case '7':
    {
        BZDB.set("tpEffect", TextUtils::format("%d", list->getIndex()));
        break;
    }
    }
}


// Local Variables: ***
// mode: C++ ***
// tab-width: 4 ***
// c-basic-offset: 4 ***
// indent-tabs-mode: nil ***
// End: ***
// ex: shiftwidth=4 tabstop=4
