/*
==============================================================================

    This file contains the basic framework code for a JUCE plugin editor.

  ==============================================================================
*/

#pragma once

#include <juce_audio_processors/juce_audio_processors.h>
#include <juce_audio_utils/juce_audio_utils.h>
#include "PluginProcessor.h"
#include "AXIOMDesignSystem.h"

//==============================================================================
/**
*/
using AXIOM::DesignSystem;
using Components = DesignSystem::Components;
using SliderStyles = Components::SliderStyles;
class SpectrumAnalyzer {
    public:
    SpectrumAnalyzer(AudioFIFO& fifoRef, CrystalizerEQAudioProcessor& processor) : audioFIFO(fifoRef), audioProcessor(processor) {
        fftData.resize(2 * FFTSIZE);
        magnitudes.resize(BINS);
        magnitudesDb.resize(BINS);
        emaSmoothedMagnitudesDb.resize(BINS);
        freqSmoothedMagnitudesDb.resize(BINS);
        sampleRate = audioProcessor.getSampleRate();
        deltaF = sampleRate / static_cast<double>(FFTSIZE);
        freqPerBin.resize(BINS);
        peakHoldMagnitudesDb.resize(BINS);
        DELTAT = static_cast<float>(HOPSIZE) / static_cast<float>(sampleRate);
        renderValuesDb.resize(BINS);


    }
    ~SpectrumAnalyzer() = default;


    AudioFIFO& audioFIFO;

    CrystalizerEQAudioProcessor& audioProcessor;

    static constexpr int FFTSIZE = 4096;

    static constexpr int BINS = FFTSIZE / 2 + 1;

    static constexpr int HOPSIZE = FFTSIZE / 2;

    juce::Array<float> fftFrame;

    //CHANGE FOR LOWER/HIGHER EMA-SMOOTHING //LOWER VALUE -> MORE SMOOTHING/SLOW RESPONSE -- HIGHER VALUE -> LESS SMOOTHING/FAST RESPONSE    //MAX VALUE = 1.f
    float smoothingFactor = 0.3f;


    void processSamples();

    void getFftFrame(juce::Array<float> &samples);

    void applyWindowOnFftFrame(std::vector<float> &fullFrame);

    juce::dsp::WindowingFunction<float> window
{
    FFTSIZE,
    juce::dsp::WindowingFunction<float>::hann
};

    void processWindowedFrame(std::vector<float> &windowedFrame);


    static constexpr int FFTORDER = 12;
    juce::dsp::FFT fft { FFTORDER };

    std::vector<float> fftData;



    const float MINDB = -90.f;
    const float MAXDB = 0.f;

    std::vector<float> magnitudes;

    std::vector<float> magnitudesDb;

    std::vector<float> emaSmoothedMagnitudesDb;

    std::vector<float> freqSmoothedMagnitudesDb;

    std::vector<float> peakHoldMagnitudesDb;

    std::vector<float> renderValuesDb;

    double deltaF = 0;
    double sampleRate = 44100;

    //CHANGE DENOMINATOR FOR LOWER/HIGHER FREQ-SMOOTHING //HIGHER VALUE -> HIGHER SMOOTHING -- LESS VALUE -> LESS SMOOTHING
    const float BANDWIDTHOCT = 1.f / 6.f;

    const float OCTAVERADIUS = BANDWIDTHOCT / 2.f;

    std::vector<double> freqPerBin;

    //CHANGE FOR FASTER FALLOFF -- VALUE IS IN DB/S
    const float FALLOFFRATE = 120.f;

    float DELTAT = 0.f;

    float tempPeakHoldDB = 0.f;


    const float MINFREQ = 20.0f;
    const float MAXFREQ = static_cast<float>(sampleRate) / 2;

    void fillFftDataFromFrame(std::vector<float> &windowedFrame);
    void buildMagnitudeSpectrum();
    void convertToDb();
    void applySmoothing();
    void applyEMA();
    void applyFreqSmoothing();
    void applyPeakHoldAndFalloff();
    std::vector<float> getRenderValues();

};


class CrystalizerEQAudioProcessorEditor  : public juce::AudioProcessorEditor, private juce::Timer

{
public:

    CrystalizerEQAudioProcessorEditor (CrystalizerEQAudioProcessor&);
    ~CrystalizerEQAudioProcessorEditor() override;

    void paintAnalyzer(juce::Graphics &g);

    void paintModeBoxBorders(juce::Graphics &g);

    void paintBorderLines(juce::Graphics& g);

    //===================================================================
    void timerCallback();
    void paint (juce::Graphics&) override;


    void resized() override;
    void resetAllCheckboxes();

    using SliderAttach = juce::AudioProcessorValueTreeState::SliderAttachment;

    using ButtonAttach = juce::AudioProcessorValueTreeState::ButtonAttachment;

    using ComboBoxAttach = juce::AudioProcessorValueTreeState::ComboBoxAttachment;

    juce::Slider testNoiseSlider,
    lowBandFreqSlider, lowBandSlopeSlider, lowBandGainSlider, lowBandQSlider,
    peak1FreqSlider, peak1GainSlider, peak1QSlider,
    peak2FreqSlider, peak2GainSlider, peak2QSlider,
    peak3FreqSlider, peak3GainSlider, peak3QSlider,
    highBandFreqSlider, highBandSlopeSlider, highBandGainSlider, highBandQSlider,
    inputSlider, outputSlider;

    const juce::Array<juce::Slider*> sliders = {
        &lowBandGainSlider, &lowBandFreqSlider, &lowBandQSlider, &lowBandSlopeSlider,
        &peak1GainSlider, &peak1FreqSlider, &peak1QSlider,
        &peak2GainSlider, &peak2FreqSlider, &peak2QSlider,
        &peak3GainSlider, &peak3FreqSlider, &peak3QSlider,
        &highBandGainSlider, &highBandFreqSlider, &highBandQSlider, &highBandSlopeSlider,
        &inputSlider, &outputSlider
    };

    std::unique_ptr<SliderAttach> testNoiseAttach,
    lowBandFreqAttach, lowBandSlopeAttach, lowBandGainAttach, lowBandQAttach,
    peak1FreqAttach, peak1GainAttach, peak1QAttach,
    peak2FreqAttach, peak2GainAttach, peak2QAttach,
    peak3FreqAttach, peak3GainAttach, peak3QAttach,
    highBandFreqAttach, highBandSlopeAttach, highBandGainAttach, highBandQAttach,
    inputAttach, outputAttach;

    juce::Label titleLabel, testNoiseLabel,
    lowBandFreqLabel, lowBandSlopeLabel, lowBandGainLabel, lowBandQLabel, lowBandModeLabel,
    low12, low24, low36, low48, lowdBOctLabel,
    peak1FreqLabel, peak1GainLabel, peak1QLabel,
    peak2FreqLabel, peak2GainLabel, peak2QLabel,
    peak3FreqLabel, peak3GainLabel, peak3QLabel,
    highBandFreqLabel, highBandSlopeLabel, highBandGainLabel, highBandQLabel, highBandModeLabel,
    high12, high24, high36, high48, highdBOctLabel,
    inputLabel, outputLabel,
    presetBoxLabel;

    const juce::Array<juce::Label*> sliderLabels = {
        &lowBandFreqLabel, &lowBandSlopeLabel, &lowBandGainLabel, &lowBandQLabel, &lowBandModeLabel, &lowdBOctLabel,
    &peak1FreqLabel, &peak1GainLabel, &peak1QLabel,
    &peak2FreqLabel, &peak2GainLabel, &peak2QLabel,
    &peak3FreqLabel, &peak3GainLabel, &peak3QLabel,
    &highBandFreqLabel, &highBandSlopeLabel, &highBandGainLabel, &highBandQLabel, &highBandModeLabel, &highdBOctLabel,
    &inputLabel, &outputLabel
    };
    const juce::Array<juce::Label*> slopeLabels = {
        &low12, &low24, &low36, &low48,
        &high12, &high24, &high36, &high48
    };



    juce::TextButton testNoiseButton, resetButton, savePresetButton, deletePresetButton;

    juce::ToggleButton masterBypassButton, crystalizeButton, peak1BypassButton, peak2BypassButton, peak3BypassButton, presetMenuButton;

    const juce::Array<juce::ToggleButton*> peakBypassButtons = {&peak1BypassButton, &peak2BypassButton, &peak3BypassButton};

    juce::ComboBox presetBox;

    juce::Component lowBandModeBox, highBandModeBox;

    juce::Array<juce::ToggleButton*> lowBandModeButtons, highBandModeButtons;

    std::array<bool, 4> lowBandBools, highBandBools;


    juce::ToggleButton lowBypass, lowCut, lowBell, lowShelf,
                        highBypass, highCut, highBell, highShelf;

    juce::TextEditor presetNameInput;


private:

    // This reference is provided as a quick way for your editor to
    // access the processor object that created it.
    CrystalizerEQAudioProcessor& audioProcessor;




    void setKnobVisibility();

    void animateCrystalizeButton();

    void setupSliders();

    void setupSliderTextBoxes();

    void handleLowBandModes();

    void handleHighBandModes();

    void setupAttachments();

    void setupDisplayNames();

    void setupToggleButtons();

    void disableLowBand(float target);
    void disableLowMidBand(float target);
    void disableMidBand(float target);
    void disableHighMidBand(float target);
    void disableHighBand(float target);

    void disableEverything(float target);

    void addComponentsToLayout();

    void setupLabels();

    void setupFontsWithColours();

    void setupEventListeners();

    void initPresetSystem();


    std::unique_ptr<AXIOM::DesignSystem::Components::BaseSliderLookAndFeel> baseLookAndFeel;
    std::unique_ptr<DesignSystem::Components::GainSliderLookAndFeel> gainLookAndFeel;
    std::unique_ptr<DesignSystem::Components::FreqQSliderLookAndFeel> freqQLookAndFeel;
    std::unique_ptr<DesignSystem::Components::SlopeSliderLookAndFeel> slopeLookAndFeel;
    std::unique_ptr<DesignSystem::Components::GlobalSliderLookAndFeel> globalLookAndFeel;
    std::unique_ptr<Components::BypassButtonLookAndFeel> bypassButtonLookAndFeel;
    std::unique_ptr<Components::PresetMenuButtonLookAndFeel> presetMenuButtonLookAndFeel;
    std::unique_ptr<Components::lowBandButtonLookAndFeel> lowBandButtonLookAndFeel;
    std::unique_ptr<Components::highBandButtonLookAndFeel> highBandButtonLookAndFeel;
    std::unique_ptr<Components::SvgToggleButtonLookAndFeel> svgToggleButtonLookAndFeel;
    std::unique_ptr<DesignSystem::PresetMenuLookAndFeel> presetMenuLookAndFeel;
    std::unique_ptr<DesignSystem::PresetButtonsLookAndFeel> presetButtonLookAndFeel;
    std::unique_ptr<DesignSystem::PresetComboBoxLookAndFeel> presetComboBoxLookAndFeel;


    //SPECRTRUM ANALYZER


    SpectrumAnalyzer spectrumAnalyzer;

    juce::Rectangle<int> analyzerRect;

    Component headerBar;
    Component mainPanel;
    Component footerBar;

    Component presetArea;
    Component presetMenu;
    Component analyzerArea;

    Component filterArea;
    Component lowFilterArea;
    Component lowMidFilterArea;
    Component midFilterArea;
    Component highMidFilterArea;
    Component highFilterArea;

    const juce::Array<juce::Component*> filterAreas {
        &lowFilterArea, &lowMidFilterArea, &midFilterArea, &highMidFilterArea, &highFilterArea
    };

    Component globalControlArea;



    void scalePluginWindow(juce::Rectangle<int> area);
    void setupMainGrid(juce::Rectangle<int> area);

    void setupLowBandLayout();
    void setupLowMidBandLayout();
    void setupMidBandLayout();
    void setupHighMidBandLayout();
    void setupHighBandLayout();

    void setupHeader();
    void setupBody();
    void setupFooter();

    juce::Array<float> getReferenceCell();

    const float gainMod = SliderStyles::Size::getSliderSizeMod(SliderStyles::Size::SizeMode::Gain);
    const float freqMod = SliderStyles::Size::getSliderSizeMod(SliderStyles::Size::SizeMode::Freq);
    const float qMod = SliderStyles::Size::getSliderSizeMod(SliderStyles::Size::SizeMode::Q);
    const float slopeMod = SliderStyles::Size::getSliderSizeMod(SliderStyles::Size::SizeMode::Slope);
    const float globalMod = SliderStyles::Size::getSliderSizeMod(SliderStyles::Size::SizeMode::Global);

    const float filterAreaMargin = 15.0f;

    bool isAnimatingCrystalize = false;
    bool isFadingToActive = false;

    std::unique_ptr<juce::Drawable> logoDrawable;

    DesignSystem::PresetMenu* test;
    juce::Component::SafePointer<DesignSystem::PresetMenu> presetMenuSafePtr;

    float mapFrequencyToX(float freq, float minFreq, float maxFreq, float width);
    int getClosestBinForFrequency(float freq);
    void drawFrequencyGrid(juce::Graphics& g, const float dbRange);
    float getInterpolatedDb(float exactBin);

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CrystalizerEQAudioProcessorEditor)
};
