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

    ModulationSection.cpp
    Created: 24 Jul 2026
    Author:  Armin

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

#include <JuceHeader.h>

#include "ModulationSection.h"

ModulationSection::ModulationSection(JustaSampleAudioProcessor& processor) :
    apvts(processor.APVTS()),

    filterLabel("", "Filter"),
    lfoLabel("", "LFO"),
    phaserLabel("", "Phaser"),
    ringModLabel("", "Ring Mod"),

    filterTypeAttachment(apvts, PluginParameters::MOD_FILTER_TYPE, filterTypeBox),
    filterCutoffAttachment(apvts, PluginParameters::MOD_FILTER_CUTOFF, filterCutoff),
    filterResonanceAttachment(apvts, PluginParameters::MOD_FILTER_RESONANCE, filterResonance),

    lfoWaveformAttachment(apvts, PluginParameters::MOD_LFO_WAVEFORM, lfoWaveformBox),
    lfoRateAttachment(apvts, PluginParameters::MOD_LFO_RATE, lfoRate),
    lfoSyncAttachment(apvts, PluginParameters::MOD_LFO_SYNC, lfoSyncButton),
    lfoSyncDivAttachment(apvts, PluginParameters::MOD_LFO_SYNC_DIV, lfoSyncDivBox),
    lfoFilterDepthAttachment(apvts, PluginParameters::MOD_LFO_FILTER_DEPTH, lfoFilterDepth),
    lfoSpeedDepthAttachment(apvts, PluginParameters::MOD_LFO_SPEED_DEPTH, lfoSpeedDepth),

    phaserEnabledAttachment(apvts, PluginParameters::MOD_PHASER_ENABLED, phaserEnabled),
    phaserDepthAttachment(apvts, PluginParameters::MOD_PHASER_DEPTH, phaserDepth),
    phaserFeedbackAttachment(apvts, PluginParameters::MOD_PHASER_FEEDBACK, phaserFeedback),
    phaserStagesAttachment(apvts, PluginParameters::MOD_PHASER_STAGES, phaserStages),

    ringModEnabledAttachment(apvts, PluginParameters::MOD_RINGMOD_ENABLED, ringModEnabled),
    ringModMixAttachment(apvts, PluginParameters::MOD_RINGMOD_MIX, ringModMix)
{
    // Labels
    auto setupLabel = [this](juce::Label& label)
    {
        label.setJustificationType(juce::Justification::centred);
        label.setInterceptsMouseClicks(false, false);
        addAndMakeVisible(label);
    };
    setupLabel(filterLabel);
    setupLabel(lfoLabel);
    setupLabel(phaserLabel);
    setupLabel(ringModLabel);

    // Filter type combo
    filterTypeBox.addItemList(PluginParameters::MOD_FILTER_TYPE_LABELS, 1);
    filterTypeBox.setJustificationType(juce::Justification::centred);
    addAndMakeVisible(filterTypeBox);

    // Filter rotaries
    auto setupRotary = [this](CustomRotary& rotary, const juce::String& unit)
    {
        rotary.setSliderStyle(juce::Slider::RotaryVerticalDrag);
        rotary.setMouseDragSensitivity(150);
        rotary.setTextBoxStyle(juce::Slider::TextBoxBelow, false, 0, 0);
        rotary.getProperties().set(ComponentProps::ROTARY_UNIT, unit);
        addAndMakeVisible(rotary);
        rotaries.add(&rotary);
    };

    setupRotary(filterCutoff, PluginParameters::FREQUENCY_UNIT);
    setupRotary(filterResonance, "");

    // LFO waveform combo
    lfoWaveformBox.addItemList(PluginParameters::MOD_LFO_WAVEFORM_LABELS, 1);
    lfoWaveformBox.setJustificationType(juce::Justification::centred);
    addAndMakeVisible(lfoWaveformBox);

    setupRotary(lfoRate, PluginParameters::FREQUENCY_UNIT);

    lfoSyncButton.useShape(juce::Path());
    lfoSyncButton.setButtonText("Sync");
    addAndMakeVisible(lfoSyncButton);

    lfoSyncDivBox.addItemList(PluginParameters::MOD_LFO_SYNC_DIV_LABELS, 1);
    lfoSyncDivBox.setJustificationType(juce::Justification::centred);
    addAndMakeVisible(lfoSyncDivBox);

    setupRotary(lfoFilterDepth, "");
    setupRotary(lfoSpeedDepth, "");

    // Phaser
    phaserEnabled.setButtonText("On");
    addAndMakeVisible(phaserEnabled);
    setupRotary(phaserDepth, "");
    setupRotary(phaserFeedback, "");
    phaserStages.getProperties().set(ComponentProps::ROTARY_UNIT, "stg");
    setupRotary(phaserStages, "stg");

    // Ring mod
    ringModEnabled.setButtonText("On");
    addAndMakeVisible(ringModEnabled);
    setupRotary(ringModMix, "%");
}

void ModulationSection::paint(juce::Graphics& g)
{
    auto colors = getTheme();
    auto bounds = getLocalBounds().toFloat();

    g.setColour(colors.foreground);
    g.fillRect(bounds);

    // Draw section dividers
    g.setColour(colors.slate.withAlpha(0.2f));

    float dividerY1 = bounds.getY() + bounds.getHeight() * 0.125f;
    float dividerY2 = bounds.getY() + bounds.getHeight() * 0.875f;

    // Dividers between Filter | LFO | Phaser | Ring Mod
    float filterEnd = scalef(380.f);
    float lfoEnd = filterEnd + scalef(600.f);
    float phaserEnd = lfoEnd + scalef(530.f);

    g.drawVerticalLine(int(filterEnd), dividerY1, dividerY2);
    g.drawVerticalLine(int(lfoEnd), dividerY1, dividerY2);
    g.drawVerticalLine(int(phaserEnd), dividerY1, dividerY2);
}

void ModulationSection::resized()
{
    auto bounds = getLocalBounds().toFloat();

    // Section labels
    auto filterSection = bounds.removeFromLeft(scalef(380.f));
    auto lfoSection = bounds.removeFromLeft(scalef(600.f));
    auto phaserSection = bounds.removeFromLeft(scalef(530.f));
    auto ringModSection = bounds;

    // Reduce all sections vertically
    filterSection.reduce(scalef(16.f), scalef(8.f));
    lfoSection.reduce(scalef(16.f), scalef(8.f));
    phaserSection.reduce(scalef(16.f), scalef(8.f));
    ringModSection.reduce(scalef(16.f), scalef(8.f));

    // === Filter section ===
    auto filterTitleBounds = filterSection.removeFromTop(scalef(28.f));
    filterLabel.setBounds(filterTitleBounds.toNearestInt());
    filterLabel.setFont(getInriaSans().withHeight(scalef(22.f)));
    filterSection.removeFromTop(scalef(4.f));

    auto filterTypeBounds = filterSection.removeFromTop(scalef(28.f)).reduced(scalef(20.f), 0.f);
    filterTypeBox.setBounds(filterTypeBounds.toNearestInt());
    filterSection.removeFromTop(scalef(6.f));

    float filterRotarySize = scalef(50.f);
    float filterRotaryPad = filterRotarySize * Layout::rotaryPadding;

    auto filterCutoffBounds = filterSection.removeFromLeft(filterRotarySize + 2 * filterRotaryPad);
    filterCutoff.setBounds(filterCutoffBounds.toNearestInt());
    filterCutoff.sendLookAndFeelChange();

    filterSection.removeFromLeft(scalef(16.f));

    auto filterResBounds = filterSection.removeFromLeft(filterRotarySize + 2 * filterRotaryPad);
    filterResonance.setBounds(filterResBounds.toNearestInt());
    filterResonance.sendLookAndFeelChange();

    // === LFO section ===
    auto lfoTitleBounds = lfoSection.removeFromTop(scalef(28.f));
    lfoLabel.setBounds(lfoTitleBounds.toNearestInt());
    lfoLabel.setFont(getInriaSans().withHeight(scalef(22.f)));
    lfoSection.removeFromTop(scalef(4.f));

    auto lfoWaveformBounds = lfoSection.removeFromTop(scalef(28.f)).reduced(scalef(20.f), 0.f);
    lfoWaveformBox.setBounds(lfoWaveformBounds.toNearestInt());
    lfoSection.removeFromTop(scalef(4.f));

    auto lfoTopRow = lfoSection.removeFromTop(scalef(56.f));
    auto lfoBottomRow = lfoSection;

    float lfoRotarySize = scalef(48.f);
    float lfoRotaryPad = lfoRotarySize * Layout::rotaryPadding;

    // Top row: Rate, Sync button, Sync div
    auto lfoRateBounds = lfoTopRow.removeFromLeft(lfoRotarySize + 2 * lfoRotaryPad);
    lfoRate.setBounds(lfoRateBounds.toNearestInt());
    lfoRate.sendLookAndFeelChange();

    lfoTopRow.removeFromLeft(scalef(8.f));
    auto lfoSyncBounds = lfoTopRow.removeFromLeft(scalef(50.f));
    lfoSyncButton.setBounds(lfoSyncBounds.toNearestInt());
    lfoSyncButton.setBorder(scalef(2.f), scalef(4.f));

    lfoTopRow.removeFromLeft(scalef(8.f));
    auto lfoSyncDivBounds = lfoTopRow.removeFromLeft(scalef(70.f));
    lfoSyncDivBox.setBounds(lfoSyncDivBounds.toNearestInt());

    // Bottom row: Filter depth, Speed depth
    auto lfoFilterDepthBounds = lfoBottomRow.removeFromLeft(lfoRotarySize + 2 * lfoRotaryPad);
    lfoFilterDepth.setBounds(lfoFilterDepthBounds.toNearestInt());
    lfoFilterDepth.sendLookAndFeelChange();

    lfoBottomRow.removeFromLeft(scalef(8.f));
    auto lfoSpeedDepthBounds = lfoBottomRow.removeFromLeft(lfoRotarySize + 2 * lfoRotaryPad);
    lfoSpeedDepth.setBounds(lfoSpeedDepthBounds.toNearestInt());
    lfoSpeedDepth.sendLookAndFeelChange();

    // === Phaser section ===
    auto phaserTitleBounds = phaserSection.removeFromTop(scalef(28.f));
    phaserLabel.setBounds(phaserTitleBounds.toNearestInt());
    phaserLabel.setFont(getInriaSans().withHeight(scalef(22.f)));
    phaserSection.removeFromTop(scalef(4.f));

    auto phaserEnableBounds = phaserSection.removeFromTop(scalef(24.f)).reduced(scalef(20.f), 0.f);
    phaserEnabled.setBounds(phaserEnableBounds.toNearestInt());
    phaserSection.removeFromTop(scalef(4.f));

    float phaserRotarySize = scalef(48.f);
    float phaserRotaryPad = phaserRotarySize * Layout::rotaryPadding;

    auto phaserDepthBounds = phaserSection.removeFromLeft(phaserRotarySize + 2 * phaserRotaryPad);
    phaserDepth.setBounds(phaserDepthBounds.toNearestInt());
    phaserDepth.sendLookAndFeelChange();

    phaserSection.removeFromLeft(scalef(8.f));
    auto phaserFbBounds = phaserSection.removeFromLeft(phaserRotarySize + 2 * phaserRotaryPad);
    phaserFeedback.setBounds(phaserFbBounds.toNearestInt());
    phaserFeedback.sendLookAndFeelChange();

    phaserSection.removeFromLeft(scalef(8.f));
    auto phaserStagesBounds = phaserSection.removeFromLeft(phaserRotarySize + 2 * phaserRotaryPad);
    phaserStages.setBounds(phaserStagesBounds.toNearestInt());
    phaserStages.sendLookAndFeelChange();

    // === Ring Mod section ===
    auto ringModTitleBounds = ringModSection.removeFromTop(scalef(28.f));
    ringModLabel.setBounds(ringModTitleBounds.toNearestInt());
    ringModLabel.setFont(getInriaSans().withHeight(scalef(22.f)));
    ringModSection.removeFromTop(scalef(4.f));

    auto ringModEnableBounds = ringModSection.removeFromTop(scalef(24.f)).reduced(scalef(20.f), 0.f);
    ringModEnabled.setBounds(ringModEnableBounds.toNearestInt());
    ringModSection.removeFromTop(scalef(4.f));

    float ringModRotarySize = scalef(48.f);
    float ringModRotaryPad = ringModRotarySize * Layout::rotaryPadding;

    auto ringModMixBounds = ringModSection.removeFromLeft(ringModRotarySize + 2 * ringModRotaryPad);
    ringModMix.setBounds(ringModMixBounds.toNearestInt());
    ringModMix.sendLookAndFeelChange();
}

void ModulationSection::lookAndFeelChanged()
{
    auto colors = getTheme();

    filterLabel.setColour(juce::Label::textColourId, colors.dark);
    lfoLabel.setColour(juce::Label::textColourId, colors.dark);
    phaserLabel.setColour(juce::Label::textColourId, colors.dark);
    ringModLabel.setColour(juce::Label::textColourId, colors.dark);

    phaserEnabled.setColour(juce::ToggleButton::textColourId, colors.dark);
    ringModEnabled.setColour(juce::ToggleButton::textColourId, colors.dark);

    filterTypeBox.setColour(juce::ComboBox::backgroundColourId, colors.darkerSlate);
    filterTypeBox.setColour(juce::ComboBox::textColourId, colors.light);
    filterTypeBox.setColour(juce::ComboBox::arrowColourId, colors.dark);

    lfoWaveformBox.setColour(juce::ComboBox::backgroundColourId, colors.darkerSlate);
    lfoWaveformBox.setColour(juce::ComboBox::textColourId, colors.light);
    lfoWaveformBox.setColour(juce::ComboBox::arrowColourId, colors.dark);

    lfoSyncButton.setColors(colors.darkerSlate, colors.highlight, colors.dark.withAlpha(0.25f));

    lfoSyncDivBox.setColour(juce::ComboBox::backgroundColourId, colors.darkerSlate);
    lfoSyncDivBox.setColour(juce::ComboBox::textColourId, colors.light);
    lfoSyncDivBox.setColour(juce::ComboBox::arrowColourId, colors.dark);
}

void ModulationSection::enablementChanged()
{
    bool enabled = isEnabled();

    filterTypeBox.setEnabled(enabled);
    filterCutoff.setEnabled(enabled);
    filterResonance.setEnabled(enabled);

    lfoWaveformBox.setEnabled(enabled);
    lfoRate.setEnabled(enabled);
    lfoSyncButton.setEnabled(enabled);
    lfoSyncDivBox.setEnabled(enabled);
    lfoFilterDepth.setEnabled(enabled);
    lfoSpeedDepth.setEnabled(enabled);

    phaserEnabled.setEnabled(enabled);
    phaserDepth.setEnabled(enabled);
    phaserFeedback.setEnabled(enabled);
    phaserStages.setEnabled(enabled);

    ringModEnabled.setEnabled(enabled);
    ringModMix.setEnabled(enabled);

    repaint();
}

void ModulationSection::mouseDown(const juce::MouseEvent& event)
{
    auto parent = event.originalComponent->getParentComponent();
    for (auto* rotary : rotaries)
        if (parent == rotary)
            rotary->mouseDown(event.getEventRelativeTo(rotary));
}

void ModulationSection::mouseUp(const juce::MouseEvent& event)
{
    auto parent = event.originalComponent->getParentComponent();
    for (auto* rotary : rotaries)
        if (parent == rotary)
            rotary->mouseUp(event.getEventRelativeTo(rotary));
}

void ModulationSection::mouseDrag(const juce::MouseEvent& event)
{
    auto parent = event.originalComponent->getParentComponent();
    for (auto* rotary : rotaries)
        if (parent == rotary)
            rotary->mouseDrag(event.getEventRelativeTo(rotary));
}
