/*
 * OB-Xf - a continuation of the last open source version of OB-Xd.
 *
 * OB-Xd was originally written by Vadim Filatov, and then a version
 * was released under the GPL3 at https://github.com/reales/OB-Xd.
 * Subsequently, the product was continued by DiscoDSP and the copyright
 * holders as an excellent closed source product. For more info,
 * see "HISTORY.md" in the root of this repository.
 *
 * This repository is a successor to the last open source release,
 * a version marked as 2.11. Copyright 2013-2025 by the authors
 * as indicated in the original release, and subsequent authors
 * per the GitHub transaction log.
 *
 * OB-Xf is released under the GNU General Public Licence v3 or later
 * (GPL-3.0-or-later). The license is found in the file "LICENSE"
 * in the root of this repository or at:
 * https://www.gnu.org/licenses/gpl-3.0.en.html
 *
 * Source code is available at https://github.com/surge-synthesizer/OB-Xf
 */

#ifndef OBXF_SRC_GUI_MULTISTATEBUTTON_H
#define OBXF_SRC_GUI_MULTISTATEBUTTON_H

#include <utility>

#include "../src/engine/SynthEngine.h"
#include "../components/ScalingImageCache.h"
#include "HasScaleFactor.h"

class MultiStateButton final : public juce::Slider, public HasScaleFactor, public HasParameterWithID
{
    juce::String img_name;
    ScalingImageCache &imageCache;
    bool isSVG{false};

  public:
    MultiStateButton(juce::String name, ScalingImageCache &cache, ObxfAudioProcessor *owner_,
                     uint8_t states = 3)
        : Slider(name), img_name(std::move(name)), imageCache(cache), numStates(states),
          owner(owner_)
    {
        numFrames = numStates * 2;

        scaleFactorChanged();
        setTextBoxIsEditable(false);

        if (isSVG)
        {
            auto &svgi = imageCache.getSVGDrawable(img_name.toStdString());
            width = svgi->getWidth();
            height = svgi->getHeight();
        }
        else
        {
            width = kni.getWidth();
            height = kni.getHeight();
        }

        h2 = height / numFrames;
        stepSize = 1.f / static_cast<float>(numStates - 1);
        setRange(0.f, 1.f, stepSize);
    }

    void scaleFactorChanged() override
    {
        if (imageCache.isSVG(img_name.toStdString()))
        {
            isSVG = true;
        }
        else
        {
            isSVG = false;
            kni = imageCache.getImageFor(img_name.toStdString(), getWidth(), h2);
        }
        repaint();
    }

    void setOptionalParameter(juce::AudioProcessorParameterWithID *p)
    {
        optionalParameter = dynamic_cast<ObxfParameterFloat *>(p);
    }

    juce::AudioProcessorParameterWithID *getParameterWithID() override { return optionalParameter; }

    juce::String getTextFromValue(double value) override
    {
        if (optionalParameter)
        {
            return optionalParameter
                ->stringFromValue(static_cast<float>(optionalParameter->denormalizedValue(value)),
                                  0)
                .c_str();
        }
        return juce::String(value);
    }

    void resized() override { scaleFactorChanged(); }

    ~MultiStateButton() override = default;

    void showPopupMenu()
    {
        using namespace sst::plugininfra::misc_platform;

        if (optionalParameter)
        {
            juce::PopupMenu menu;

            menu.addSectionHeader(optionalParameter->getName(128));

            menu.addSeparator();

            auto editor = owner->getActiveEditor();

            if (editor)
            {
                if (std::strcmp(juce::PluginHostType().getHostDescription(), "Unknown") != 0)
                {
                    if (auto *c = editor->getHostContext())
                    {
                        if (auto menuInfo = c->getContextMenuForParameter(optionalParameter))
                        {
                            auto hostMenu = menuInfo->getEquivalentPopupMenu();

                            auto lf = obxf::obxfLookAndFeel(editor);

                            if (lf)
                            {
                                hostMenu = lf->modifyHostMenu(hostMenu);
                            }

                            // merge host menu with our usual context menu
                            if (hostMenu.getNumItems() > 0)
                            {
                                menu.addSeparator();

                                juce::PopupMenu::MenuItemIterator it(hostMenu);

                                while (it.next())
                                {
                                    menu.addItem(it.getItem());
                                }
                            }
                        }
                    }
                }
            }

            menu.showMenuAsync(obxf::defaultPopupMenuOptions(this));
        }
    }

    void mouseDown(const juce::MouseEvent &event) override
    {
        if (event.mods.isRightButtonDown())
        {
            showPopupMenu();
        }
        else
        {
            if (owner && optionalParameter)
            {
                if (auto *obxf = dynamic_cast<ObxfAudioProcessor *>(owner))
                {
                    obxf->setLastUsedParameter(optionalParameter->paramID);
                }
            }

            if (event.mods.isLeftButtonDown())
            {
                auto counter = getCounter();
                counter = (counter + numStates + (event.mods.isCommandDown() ? -1 : 1)) % numStates;

                setValue((double)counter / (numStates - 1));
            }

            mouseButtonPressed = true;
            repaint();
        }
    }

    void mouseUp(const juce::MouseEvent & /* event */) override
    {
        mouseButtonPressed = false;

        repaint();
    }

    void mouseDrag(const juce::MouseEvent & /*event*/) override { return; }

    void paint(juce::Graphics &g) override
    {
        const int ofs = (getCounter() * 2) + mouseButtonPressed;

        if (isSVG)
        {
            auto &svgi = imageCache.getSVGDrawable(img_name.toStdString());
            const float scale = 1.f * getWidth() / svgi->getWidth();
            auto tf = juce::AffineTransform().scaled(scale).translated(0, -scale * h2 * ofs);
            svgi->draw(g, 1.f, tf);
        }
        else
        {
            const int zoomLevel =
                imageCache.zoomLevelFor(img_name.toStdString(), getWidth(), getHeight());
            constexpr int baseZoomLevel = 100;
            const float scale = static_cast<float>(zoomLevel) / static_cast<float>(baseZoomLevel);

            const int srcW = width * scale;
            const int srcH = h2 * scale;
            const int srcY = h2 * ofs * scale;

            g.drawImage(kni, 0, 0, getWidth(), getHeight(), 0, srcY, srcW, srcH);
        }
    }

  private:
    juce::Image kni;

    bool mouseButtonPressed{false};
    int numStates{3}, numFrames{6};
    int width, height, h2;
    float stepSize{0.5};

    int getCounter() const
    {
        auto counter = juce::jlimit(0, numStates - 1,
                                    static_cast<int>(std::round(getValue() * (numStates - 1))));
        return counter;
    }

    ObxfParameterFloat *optionalParameter{nullptr};
    juce::AudioProcessor *owner{nullptr};
};

#endif // OBXF_SRC_GUI_MULTISTATEBUTTON_H
