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

This file was auto-generated by the Introjucer!

It contains the basic startup code for a Juce application.

==============================================================================
*/
#include "PluginProcessor.h"
#include "PluginEditor.h"
#include <utility>
#include "Gui/ImageButton.h"
// #include "GUI/BinaryData.h"
#include "Utils.h"


//==============================================================================
ObxdAudioProcessorEditor::ObxdAudioProcessorEditor (ObxdAudioProcessor& ownerFilter)
	: AudioProcessorEditor (&ownerFilter), ScalableComponent(&ownerFilter), processor (ownerFilter),
      skinFolder (processor.getSkinFolder()),
      progStart (3000),
      bankStart (2000),
      skinStart (1000),
      skins (processor.getSkinFiles()),
      banks (processor.getBankFiles())
{
    setLookAndFeel(new CustomLookAndFeel(&this->processor));

    //LookAndFeel& lf = getLookAndFeel();
    // Popup Menu Look and Feel
    //lf.setColour(PopupMenu::backgroundColourId, Colour(20, 20, 20));
    //lf.setColour(PopupMenu::textColourId, Colour(245, 245, 245));
    //lf.setColour(PopupMenu::highlightedBackgroundColourId, Colour(60, 60, 60));
    
    //skinFolder = ownerFilter.getCurrentSkinFolder();  // initialized above
    commandManager.registerAllCommandsForTarget(this);
    commandManager.setFirstCommandTarget(this);

    // reset KeyPressMappingSet
    commandManager.getKeyMappings()->resetToDefaultMappings();

    // having set up the default key-mappings, you might now want to load the last set
    // of mappings that the user configured.
    //commandManager.getKeyMappings()->restoreFromXml(lastSavedKeyMappingsXML);

    // Now tell our top-level window to send any keypresses that arrive to the
    // KeyPressMappingSet, which will use them to invoke the appropriate commands.
    //addKeyListener(commandManager.getKeyMappings());
    getTopLevelComponent()->addKeyListener (commandManager.getKeyMappings());
    
    //Timer::callAfterDelay (100, [this] { this->grabKeyboardFocus(); }); // ensure that key presses are sent to the KeyPressTarget object
    
    if (PluginHostType().isProTools()) { } else { startTimer(100); }; // Fix ProTools file dialog focus issues

	//startTimer(100); // This will fix the issue
    
    DBG("W: " <<getWidth() << " H:" << getHeight());

    loadSkin (processor);

    updateFromHost();
    
    switch(processor.gui_size)
    {
        case 1:
                ScalableComponent::setScaleFactor(1.0f, isHighResolutionDisplay());
            break;
        case 2:
                ScalableComponent::setScaleFactor(1.5f, isHighResolutionDisplay());
            break;
        case 4:
                ScalableComponent::setScaleFactor(2.0f, isHighResolutionDisplay());
            break;
    }
    repaint();
    scaleFactorChanged();
    
}


void ObxdAudioProcessorEditor::resized() {
    if (setPresetNameWindow != nullptr )
    {
        if (auto wrapper = dynamic_cast<ObxdAudioProcessorEditor*>(processor.getActiveEditor()))
        {
            
            auto w = proportionOfWidth(0.25f);
            auto h = proportionOfHeight(0.3f);
            auto x = proportionOfWidth(0.5f) - (w / 2);
            auto y = wrapper->getY();
            
            if (setPresetNameWindow != nullptr)
            {
                y += proportionOfHeight(0.15f);
                setPresetNameWindow->setBounds(x, y, w, h);
            }
        }
    }
    
    
    skinFolder = processor.getCurrentSkinFolder();
    File coords = skinFolder.getChildFile ("coords.xml");
    if (!coords.existsAsFile()) {
        return;
    }
    XmlDocument skin (coords);
    auto doc = skin.getDocumentElement();
    if (doc){
        //int xScreen = getWidth(), yScreen = getHeight();
        if (doc->getTagName() == "PROPERTIES"){
            forEachXmlChildElementWithTagName(*doc, child, "VALUE"){
                if (child->hasAttribute("NAME") && child->hasAttribute("x") && child->hasAttribute("y")) {
                    String name = child->getStringAttribute("NAME");
                    int x = child->getIntAttribute("x");
                    int y = child->getIntAttribute("y");
                    int d = child->getIntAttribute("d");
                    int w = child->getIntAttribute("w");
                    int h = child->getIntAttribute("h");
                    bool tooltipEnabled = child->getBoolAttribute("tooltip", false);
                    DBG(" COmponent : " << name);
                    if (mappingComps[name] != nullptr){
                        if (auto* knob = dynamic_cast<Knob*>(mappingComps[name])){
                            knob->setBounds(transformBounds(x, y, d,d));
                            const auto tooltipBehavior = processor.getTooltipBehavior();
                            if (tooltipBehavior == Tooltip::Disable ||
                                (tooltipBehavior == Tooltip::StandardDisplay && !tooltipEnabled))
                            {
                                knob->setPopupDisplayEnabled(false, false, nullptr);
                            } else
                            {
                                knob->setPopupDisplayEnabled(true, true, knob->getParentComponent());
                            }
                        }
                        else if (dynamic_cast<ButtonList*>(mappingComps[name])){
                            mappingComps[name]->setBounds(transformBounds(x, y,  w, h));
                            //((ButtonList *)mappingComps[name])->getRootMenu()->setLookAndFeel(& getLookAndFeel());
                        }

                        else if (dynamic_cast<TooglableButton*>(mappingComps[name])){
                            mappingComps[name]->setBounds(transformBounds(x, y,  w, h));
                        }
                        else if (dynamic_cast<ImageMenu*>(mappingComps[name])){
                            mappingComps[name]->setBounds(transformBounds(x, y,  d, d));
                        }
                        else if (dynamic_cast<ImageButton*>(mappingComps[name])){
                            mappingComps[name]->setBounds(transformBounds(x, y,  w, h));
                        }
                    }
                }
            }
        }
    }
}
void ObxdAudioProcessorEditor::loadSkin (ObxdAudioProcessor& ownerFilter)
{
    knobAttachments.clear();
    buttonListAttachments.clear();
    toggleAttachments.clear();
    imageButtons.clear();
    popupMenus.clear();
    mappingComps.clear();
    ownerFilter.removeChangeListener (this);

    skinFolder = ownerFilter.getCurrentSkinFolder();
    File coords = skinFolder.getChildFile ("coords.xml");
    bool useClassicSkin = coords.existsAsFile();
    if (!useClassicSkin) {
        addMenuButton (14, 25, 20, "menu");
        rebuildComponents (processor);
        return;
    }
    
    //if (coords.createInputStream())
    
    XmlDocument skin (coords);
    auto doc = skin.getDocumentElement();
    if (!doc) {
        notLoadSkin = true;
        setSize (1440, 450);
    }
    else {
        int xScreen = getWidth(), yScreen = getHeight();
        if (doc->getTagName() == "PROPERTIES"){
           
            forEachXmlChildElementWithTagName(*doc, child, "VALUE"){
                if (child->hasAttribute("NAME") && child->hasAttribute("x") && child->hasAttribute("y")) {
                    String name = child->getStringAttribute("NAME");
                    int x = child->getIntAttribute("x");
                    int y = child->getIntAttribute("y");
                    int d = child->getIntAttribute("d");
                    int w = child->getIntAttribute("w");
                    int h = child->getIntAttribute("h");
                    
                    
                    if (name == "resonanceKnob"){
                        resonanceKnob = addKnob (x, y, d, ownerFilter, RESONANCE, "Resonance", 0);
                        mappingComps["resonanceKnob"] = resonanceKnob;
                    }
                    if (name == "cutoffKnob"){
                        cutoffKnob = addKnob (x, y, d, ownerFilter, CUTOFF, "Cutoff", 0.4);
                        mappingComps["cutoffKnob"] = cutoffKnob;
                    }
                    if (name == "filterEnvelopeAmtKnob"){
                        filterEnvelopeAmtKnob = addKnob (x, y, d, ownerFilter, ENVELOPE_AMT, "Envelope", 0);
                        mappingComps["filterEnvelopeAmtKnob"] = filterEnvelopeAmtKnob;
                    }
                    if (name == "multimodeKnob"){
                        multimodeKnob = addKnob (x, y, d, ownerFilter, MULTIMODE, "Multimode", 0.5);
                        mappingComps["multimodeKnob"] = multimodeKnob;
                    }
                    
                    if (name == "volumeKnob"){
                        volumeKnob = addKnob (x, y, d, ownerFilter, VOLUME, "Volume", 0.4);
                        mappingComps["volumeKnob"] = volumeKnob;
                    }
                    if (name == "portamentoKnob"){
                        portamentoKnob = addKnob (x, y, d, ownerFilter, PORTAMENTO, "Portamento", 0);
                        mappingComps["portamentoKnob"] = portamentoKnob;
                    }
                    if (name == "osc1PitchKnob"){
                        osc1PitchKnob = addKnob (x, y, d, ownerFilter, OSC1P, "Osc1Pitch", 0);
                        osc1PitchKnob->shiftDragCallback = [](double value)
                        {
                            if (value < 0.125) return 0.0;
                            if (value < 0.375) return 0.25;
                            if (value < 0.625) return 0.5;
                            if (value < 0.875) return 0.75;
                            return 1.0;
                        };
                        osc1PitchKnob->altDragCallback = [](double value)
                        {
                            const auto semitoneValue = (int)jmap(value, -24.0, 24.0);
                            return jmap((double)semitoneValue, -24.0, 24.0, 0.0, 1.0);
                        };
                        mappingComps["osc1PitchKnob"] = osc1PitchKnob;
                    }
                    if (name == "pulseWidthKnob"){
                        pulseWidthKnob = addKnob (x, y, d, ownerFilter, PW, "PW", 0);
                        mappingComps["pulseWidthKnob"] = pulseWidthKnob;
                    }
                    if (name == "osc2PitchKnob"){
                        osc2PitchKnob = addKnob (x, y, d, ownerFilter, OSC2P, "Osc2Pitch", 0);
                        osc2PitchKnob->shiftDragCallback = [](double value)
                        {
                            if (value < 0.125) return 0.0;
                            if (value < 0.375) return 0.25;
                            if (value < 0.625) return 0.5;
                            if (value < 0.875) return 0.75;
                            return 1.0;
                        };
                        osc2PitchKnob->altDragCallback = [](double value)
                        {
                            const auto semitoneValue = (int)jmap(value, -24.0, 24.0);
                            return jmap((double)semitoneValue, -24.0, 24.0, 0.0, 1.0);
                        };
                        mappingComps["osc2PitchKnob"] = osc2PitchKnob;
                    }
                    
                    if (name == "osc1MixKnob"){
                        osc1MixKnob = addKnob (x, y, d, ownerFilter, OSC1MIX, "Osc1", 1);
                        mappingComps["osc1MixKnob"] = osc1MixKnob;
                    }
                    if (name == "osc2MixKnob"){
                        osc2MixKnob = addKnob (x, y, d, ownerFilter, OSC2MIX, "Osc2", 1);
                        mappingComps["osc2MixKnob"] = osc2MixKnob;
                    }
                    if (name == "noiseMixKnob"){
                        noiseMixKnob = addKnob (x, y, d, ownerFilter, NOISEMIX, "Noise", 0);
                        mappingComps["noiseMixKnob"] = noiseMixKnob;
                    }
                    
                    if (name == "xmodKnob"){
                        xmodKnob = addKnob (x, y, d, ownerFilter, XMOD, "Xmod", 0);
                        mappingComps["xmodKnob"] = xmodKnob;
                    }
                    if (name == "osc2DetuneKnob"){
                        osc2DetuneKnob = addKnob (x, y, d, ownerFilter, OSC2_DET, "Detune", 0);
                        mappingComps["osc2DetuneKnob"] = osc2DetuneKnob;
                    }
                    
                    if (name == "envPitchModKnob"){
                        envPitchModKnob = addKnob (x, y, d, ownerFilter, ENVPITCH, "PEnv", 0);
                        mappingComps["envPitchModKnob"] = envPitchModKnob;
                    }
                    if (name == "brightnessKnob"){
                        brightnessKnob = addKnob (x, y, d, ownerFilter, BRIGHTNESS, "Bri", 1);
                        mappingComps["brightnessKnob"] = brightnessKnob;
                    }
                    
                    if (name == "attackKnob"){
                        attackKnob = addKnob (x, y, d, ownerFilter, LATK, "Atk", 0);
                        mappingComps["attackKnob"] = attackKnob;
                    }
                    if (name == "decayKnob"){ decayKnob = addKnob (x, y, d, ownerFilter, LDEC, "Dec", 0);
                        mappingComps["decayKnob"] = decayKnob;
                    }
                    if (name == "sustainKnob"){
                        sustainKnob = addKnob (x, y, d, ownerFilter, LSUS, "Sus", 1);
                        mappingComps["sustainKnob"] = sustainKnob;
                    }
                    if (name == "releaseKnob"){
                        releaseKnob = addKnob (x, y, d, ownerFilter, LREL, "Rel", 0);
                        mappingComps["releaseKnob"] = releaseKnob;
                    }
                    
                    if (name == "fattackKnob"){
                        fattackKnob = addKnob (x, y, d, ownerFilter, FATK, "Atk", 0);
                        mappingComps["fattackKnob"] = fattackKnob;
                    }
                    if (name == "fdecayKnob"){
                        fdecayKnob = addKnob (x, y, d, ownerFilter, FDEC, "Dec", 0);
                        mappingComps["fdecayKnob"] = fdecayKnob;
                    }
                    if (name == "fsustainKnob"){
                        fsustainKnob = addKnob (x, y, d, ownerFilter, FSUS, "Sus", 1);
                        mappingComps["fsustainKnob"] = fsustainKnob;
                    }
                    if (name == "freleaseKnob"){
                        freleaseKnob = addKnob (x, y, d, ownerFilter, FREL, "Rel", 0);
                        mappingComps["freleaseKnob"] = freleaseKnob;
                    }
                    
                    if (name == "lfoFrequencyKnob"){
                        lfoFrequencyKnob = addKnob (x, y, d, ownerFilter, LFOFREQ, "Freq", 0);
                        mappingComps["lfoFrequencyKnob"] = lfoFrequencyKnob;
                    }
                    if (name == "lfoAmt1Knob"){
                        lfoAmt1Knob = addKnob (x, y, d, ownerFilter, LFO1AMT, "Pitch", 0);
                        mappingComps["lfoAmt1Knob"] = lfoAmt1Knob;
                    }
                    if (name == "lfoAmt2Knob"){
                        lfoAmt2Knob = addKnob (x, y, d, ownerFilter, LFO2AMT, "PWM", 0);
                        mappingComps["lfoAmt2Knob"] = lfoAmt2Knob;
                    }
                    
                    if (name == "lfoSinButton"){
                        lfoSinButton = addButton (x, y, w, h, ownerFilter, LFOSINWAVE, "Sin");
                        mappingComps["lfoSinButton"] = lfoSinButton;
                    }
                    if (name == "lfoSquareButton"){
                        lfoSquareButton = addButton (x, y,  w, h, ownerFilter, LFOSQUAREWAVE, "SQ");
                        mappingComps["lfoSquareButton"] = lfoSquareButton;
                    }
                    if (name == "lfoSHButton"){
                        lfoSHButton = addButton (x, y,  w, h, ownerFilter, LFOSHWAVE, "S&H");
                        mappingComps["lfoSHButton"] = lfoSHButton;
                    }
                    
                    if (name == "lfoOsc1Button"){
                        lfoOsc1Button = addButton (x, y,  w, h, ownerFilter, LFOOSC1, "Osc1");
                        mappingComps["lfoOsc1Button"] = lfoOsc1Button;
                    }
                    if (name == "lfoOsc2Button"){
                        lfoOsc2Button = addButton (x, y,  w, h, ownerFilter, LFOOSC2, "Osc2");
                        mappingComps["lfoOsc2Button"] = lfoOsc2Button;
                    }
                    if (name == "lfoFilterButton"){
                        lfoFilterButton = addButton (x, y,  w, h, ownerFilter, LFOFILTER, "Filt");
                        mappingComps["lfoFilterButton"] = lfoFilterButton;
                    }
                    
                    if (name == "lfoPwm1Button"){
                        lfoPwm1Button = addButton (x, y,  w, h, ownerFilter, LFOPW1, "Osc1");
                        mappingComps["lfoPwm1Button"] = lfoPwm1Button;

                    }
                    if (name == "lfoPwm2Button"){
                        lfoPwm2Button = addButton (x, y,  w, h, ownerFilter, LFOPW2, "Osc2");
                        mappingComps["lfoPwm2Button"] = lfoPwm2Button;
                    }
                    
                    if (name == "hardSyncButton"){
                        hardSyncButton = addButton (x, y,  w, h, ownerFilter, OSC2HS, "Sync");
                        mappingComps["hardSyncButton"] = hardSyncButton;
                    }
                    if (name == "osc1SawButton"){
                        osc1SawButton = addButton (x, y,  w, h, ownerFilter, OSC1Saw, "S");
                        mappingComps["osc1SawButton"] = osc1SawButton;
                    }
                    if (name == "osc2SawButton"){
                        osc2SawButton = addButton (x, y,  w, h, ownerFilter, OSC2Saw, "S");
                        mappingComps["osc2SawButton"] = osc2SawButton;
                    }
                    
                    if (name == "osc1PulButton"){
                        osc1PulButton = addButton (x, y,  w, h, ownerFilter, OSC1Pul, "P");
                        mappingComps["osc1PulButton"] = osc1PulButton;
                    }
                    if (name == "osc2PulButton"){
                        osc2PulButton = addButton (x, y,  w, h, ownerFilter, OSC2Pul, "P");
                        mappingComps["osc2PulButton"] = osc2PulButton;
                    }
                    
                    if (name == "pitchQuantButton"){
                        pitchQuantButton =  addButton (x, y,  w, h, ownerFilter, OSCQuantize, "Step");
                        pitchQuantButton->onStateChange = [&]
                        {
                            const auto isButtonOn = pitchQuantButton->getToggleState();
                            const auto configureOscKnob = [&](const String& parameter)
                            {
                                if (auto* knob = dynamic_cast<Knob*>(mappingComps[parameter]))
                                {
                                    if (isButtonOn)
                                        knob->alternativeValueMapCallback = [](double value)
                                    {
                                        const auto semitoneValue = (int)jmap(value, -24.0, 24.0);
                                        return jmap((double)semitoneValue, -24.0, 24.0, 0.0, 1.0);
                                    };
                                    else
                                        knob->alternativeValueMapCallback = nullptr;
                                }
                            };
                            configureOscKnob("osc1PitchKnob");
                            configureOscKnob("osc2PitchKnob");
                            
                        };
                        mappingComps["pitchQuantButton"] = pitchQuantButton;
                    }
                    
                    if (name == "filterBPBlendButton"){
                        filterBPBlendButton = addButton (x, y,  w, h, ownerFilter, BANDPASS, "Bp");
                        mappingComps["filterBPBlendButton"] = filterBPBlendButton;
                    }
                    if (name == "fourPoleButton"){
                        fourPoleButton = addButton (x, y,  w, h, ownerFilter, FOURPOLE, "24");
                        mappingComps["fourPoleButton"] = fourPoleButton;
                    }
                    if (name == "filterHQButton"){
                        filterHQButton = addButton (x, y,  w, h, ownerFilter, FILTER_WARM, "HQ");
                        mappingComps["filterHQButton"] = filterHQButton;
                    }
                    
                    if (name == "filterKeyFollowButton"){
                        filterKeyFollowButton =  addButton (x, y,  w, h, ownerFilter, FLT_KF, "Key");
                        mappingComps["filterKeyFollowButton"] = filterKeyFollowButton;
                    }
                    if (name == "unisonButton"){
                        unisonButton = addButton (x, y,  w, h, ownerFilter, UNISON, "Uni");
                        mappingComps["unisonButton"] = unisonButton;
                    }
                    
                    if (name == "tuneKnob"){
                        tuneKnob = addKnob (x, y, d, ownerFilter, TUNE, "Tune", 0.5);
                        mappingComps["tuneKnob"] = tuneKnob;
                    }
                    if (name == "transposeKnob"){
                        transposeKnob = addKnob (x, y, d, ownerFilter, OCTAVE, "Transpose", 0.5);
                        mappingComps["transposeKnob"] = transposeKnob;
                    }
                    
                    if (name == "voiceDetuneKnob"){
                        voiceDetuneKnob =addKnob (x, y, d, ownerFilter, UDET, "VoiceDet", 0);
                        mappingComps["voiceDetuneKnob"] = voiceDetuneKnob;
                    }
                    
                    if (name == "bendLfoRateKnob"){
                        bendLfoRateKnob = addKnob (x, y, d, ownerFilter, BENDLFORATE, "ModRate", 0.4);
                        mappingComps["bendLfoRateKnob"] = bendLfoRateKnob;
                    }
                    if (name == "veloFltEnvKnob"){
                        veloFltEnvKnob = addKnob (x, y, d, ownerFilter, VFLTENV, "VFE", 0);
                        mappingComps["veloFltEnvKnob"] = veloFltEnvKnob;
                    }
                    if (name == "veloAmpEnvKnob"){
                        veloAmpEnvKnob = addKnob (x, y, d, ownerFilter, VAMPENV, "VAE", 0);
                        mappingComps["veloAmpEnvKnob"] = veloAmpEnvKnob;
                    }
                    if (name == "midiLearnButton"){
                        midiLearnButton = addButton (x, y,  w, h, ownerFilter, MIDILEARN, "LEA");
                        mappingComps["midiLearnButton"] = midiLearnButton;
                    }
                    if (name == "midiUnlearnButton"){
                        midiUnlearnButton = addButton (x, y,  w, h, ownerFilter, UNLEARN, "UNL");
                        mappingComps["midiUnlearnButton"] = midiUnlearnButton;
                    }
                    
                    if (name == "pan1Knob"){
                        pan1Knob = addKnob (x, y, d, ownerFilter, PAN1, "1", 0.5);
                        pan1Knob->resetOnShiftClick(true, Action::panReset);
                        pan1Knob->addActionListener(this);
                        mappingComps["pan1Knob"] = pan1Knob;
                    }
                    if (name == "pan2Knob"){
                        pan2Knob = addKnob (x, y, d, ownerFilter, PAN2, "2", 0.5);
                        pan2Knob->resetOnShiftClick(true, Action::panReset);
                        pan2Knob->addActionListener(this);
                        mappingComps["pan2Knob"] = pan2Knob;
                    }
                    if (name == "pan3Knob"){
                        pan3Knob = addKnob (x, y, d, ownerFilter, PAN3, "3", 0.5);
                        pan3Knob->resetOnShiftClick(true, Action::panReset);
                        pan3Knob->addActionListener(this);
                        mappingComps["pan3Knob"] = pan3Knob;
                    }
                    if (name == "pan4Knob"){
                        pan4Knob = addKnob (x, y, d, ownerFilter, PAN4, "4", 0.5);
                        pan4Knob->resetOnShiftClick(true, Action::panReset);
                        pan4Knob->addActionListener(this);
                        mappingComps["pan4Knob"] = pan4Knob;
                    }
                    
                    if (name == "pan5Knob"){
                        pan5Knob = addKnob (x, y, d, ownerFilter, PAN5, "5", 0.5);
                        pan5Knob->resetOnShiftClick(true, Action::panReset);
                        pan5Knob->addActionListener(this);
                        mappingComps["pan5Knob"] = pan5Knob;
                    }
                    if (name == "pan6Knob"){
                        pan6Knob = addKnob (x, y, d, ownerFilter, PAN6, "6", 0.5);
                        pan6Knob->resetOnShiftClick(true, Action::panReset);
                        pan6Knob->addActionListener(this);
                        mappingComps["pan6Knob"] = pan6Knob;
                    }
                    if (name == "pan7Knob"){
                        pan7Knob = addKnob (x, y, d, ownerFilter, PAN7, "7", 0.5);
                        pan7Knob->resetOnShiftClick(true, Action::panReset);
                        pan7Knob->addActionListener(this);
                        mappingComps["pan7Knob"] = pan7Knob;
                    }
                    if (name == "pan8Knob"){
                        pan8Knob = addKnob (x, y, d, ownerFilter, PAN8, "8", 0.5);
                        pan8Knob->resetOnShiftClick(true, Action::panReset);
                        pan8Knob->addActionListener(this);
                        mappingComps["pan8Knob"] = pan8Knob;
                    }
                    
                    if (name == "bendOsc2OnlyButton"){
                        bendOsc2OnlyButton = addButton (x, y,  w, h, ownerFilter, BENDOSC2, "Osc2");
                        mappingComps["bendOsc2OnlyButton"] = bendOsc2OnlyButton;
                    }
                    if (name == "bendRangeButton"){
                        bendRangeButton = addButton (x, y,  w, h, ownerFilter, BENDRANGE, "12");
                        mappingComps["bendRangeButton"] = bendRangeButton;
                    }
                    if (name == "asPlayedAllocButton"){
                        asPlayedAllocButton = addButton (x, y,  w, h, ownerFilter, ASPLAYEDALLOCATION, "APA");
                        mappingComps["asPlayedAllocButton"] = asPlayedAllocButton;
                    }
                    
                    if (name == "filterDetuneKnob"){
                        filterDetuneKnob = addKnob (x, y, d, ownerFilter, FILTERDER, "Flt", 0.2);
                        mappingComps["filterDetuneKnob"] = filterDetuneKnob;
                    }
                    if (name == "portamentoDetuneKnob"){
                        portamentoDetuneKnob = addKnob (x, y, d, ownerFilter, PORTADER, "Port", 0.2);
                        mappingComps["portamentoDetuneKnob"] = portamentoDetuneKnob;
                    }
                    if (name == "envelopeDetuneKnob"){
                        envelopeDetuneKnob = addKnob (x, y, d, ownerFilter, ENVDER, "Env", 0.2);
                        mappingComps["envelopeDetuneKnob"] = envelopeDetuneKnob;
                    }
                                        
                    if (name == "voiceSwitch"){
                        voiceSwitch.reset(addList (x, y, w, h, ownerFilter, VOICE_COUNT, "VoiceCount", "voices"));
                        voiceSwitch->setLookAndFeel(&this->getLookAndFeel());
                        mappingComps["voiceSwitch"] = voiceSwitch.get();
                    }


                    if (name == "legatoSwitch"){
                        legatoSwitch.reset(addList (x, y, w, h, ownerFilter, LEGATOMODE, "Legato", "legato"));
                       
                        legatoSwitch->setLookAndFeel(&this->getLookAndFeel());
                        mappingComps["legatoSwitch"] = legatoSwitch.get();
                    }


                    
                    if (name == "menu")
                    {
                        ImageButton *img = addMenuButton (x, y, d, "menu");
                        mappingComps["menu"] = img;
                    }

                    /*
                    if (name == "guisize") {
                        xScreen = x;
                        yScreen = y;
                        if (processor.getShowPresetBar()) {
                            setSize(xScreen, yScreen + 40);
                        }
                        else {
                            setSize(xScreen, yScreen);
                        }
                    }
                    */
                    
                    //DBG(" Name: " << name << " X: " <<x <<" Y: "<<y);
                }
            }
        }


        presetBar.reset(new PresetBar(*this));
        addAndMakeVisible(*presetBar);
        presetBar->setVisible(processor.getShowPresetBar());
        presetBar->leftClicked = [this](juce::Point<int> &pos){
            PopupMenu menu;
            menu.setLookAndFeel(&this->getLookAndFeel());
            for (int i = 0; i < processor.getNumPrograms(); ++i)
            {
                menu.addItem (i + progStart + 1,
                                  processor.getProgramName (i),
                                  true,
                                  i == processor.getCurrentProgram());
            }
            int result = menu.showAt (Rectangle<int> (pos.getX(), pos.getY(), 1, 1));
        
            if (result >= (progStart + 1) && result <= (progStart + processor.getNumPrograms())){
                result -= 1;
                result -= progStart;
                processor.setCurrentProgram (result);
            }
        };
        resized();
    }
    
    // Prepare data
    if (voiceSwitch)
    {
        for (int i = 1; i <= 32; ++i)
        {
            voiceSwitch->addChoice (String (i));
        }
        
        auto voiceOption = ownerFilter.getPluginState().getParameter (ownerFilter.getEngineParameterId (VOICE_COUNT))->getValue();
        voiceSwitch->setValue (voiceOption, dontSendNotification);
    }
    
    if (legatoSwitch)
    {
        legatoSwitch->addChoice ("Keep All");
        legatoSwitch->addChoice ("Keep Filter Envelope");
        legatoSwitch->addChoice ("Keep Amplitude Envelope");
        legatoSwitch->addChoice ("Retrig");
        auto legatoOption = ownerFilter.getPluginState().getParameter (ownerFilter.getEngineParameterId (LEGATOMODE))->getValue();
        legatoSwitch->setValue (legatoOption, dontSendNotification);
    }
    
    createMenu();

    ownerFilter.addChangeListener (this);
    
    scaleFactorChanged();
    repaint();
}
ObxdAudioProcessorEditor::~ObxdAudioProcessorEditor()
{
	processor.removeChangeListener (this);
//    deleteAllChildren();  // WATCH OUT!
}

void ObxdAudioProcessorEditor::scaleFactorChanged()
{
    // update background
    const bool highResolutionDisplay = isHighResolutionDisplay();
    const float scaleFactor = getScaleFactor();

    // notify child components
    for (int i = 0; i < getNumChildComponents(); i++)
    {
        ScalableComponent* object =
            dynamic_cast<ScalableComponent*>(getChildComponent(i));

        if (object != nullptr)
        {
            object->setScaleFactor(scaleFactor, highResolutionDisplay);
        }
        
        // update look and feel
        CustomLookAndFeel* laf =
            dynamic_cast<CustomLookAndFeel*>(&getChildComponent(i)->getLookAndFeel());
        if (laf != nullptr)
        {
            laf->setScaleFactor(scaleFactor, highResolutionDisplay);
        }
    }
    
    
    // redraw everything
    backgroundImage = getScaledImageFromCache("main", scaleFactor, highResolutionDisplay);

    int width = backgroundImage.getWidth();
    int height = backgroundImage.getHeight();
    
    
    if (!highResolutionDisplay)
    {
        if (scaleFactor == 1.5f)
        {
            width = roundFloatToInt(width * 0.75f);
            height = roundFloatToInt(height * 0.75f);
            
        }
        else if (scaleFactor == 2.0f)
        {
            width /= 2;
            height /= 2;
        }
    } else {
        if (scaleFactor == 1.0f) //2x image
        {
            width /= 2;
            height /= 2;
        }
        else if (scaleFactor == 1.5f) //4x images =>150%
        {
            width = roundFloatToInt(width * (0.25f + 0.125f));
            height = roundFloatToInt(height * (0.25f + 0.125f));
            
        }
        else if (scaleFactor == 2.0f) //4x images =>200x
        {
            width /= 2;
            height /= 2;
        }
    }

    if (processor.getShowPresetBar())
    {
        setSize(width, height + presetBar->getHeight());
        presetBar->setBounds((width - presetBar->getWidth()) / 2, height, presetBar->getWidth(), presetBar->getHeight());
    } else
        setSize(width, height);
}


void ObxdAudioProcessorEditor::placeLabel (int x, int y, String text)
{
	Label* lab = new Label();
	lab->setBounds (x, y, 110, 20);
	lab->setJustificationType (Justification::centred);
	lab->setText (text,dontSendNotification);
    lab->setInterceptsMouseClicks (false, true);
	addAndMakeVisible (lab);
}

ButtonList* ObxdAudioProcessorEditor::addList (int x, int y, int width, int height, ObxdAudioProcessor& filter, int parameter, String /*name*/, String imgName)
{
    #if JUCE_WINDOWS || JUCE_LINUX
    ButtonList *bl = new ButtonList (imgName, height, &processor);
    #else
    ButtonList *bl = new ButtonList (imgName, height, &processor);
    #endif

    buttonListAttachments.add (new ButtonList::ButtonListAttachment (filter.getPluginState(),
                                                                     filter.getEngineParameterId (parameter),
                                                                     *bl));
    
	bl->setBounds (x, y, width, height);
	addAndMakeVisible (bl);
    
	return bl;

}

Knob* ObxdAudioProcessorEditor::addKnob (int x, int y, int d, ObxdAudioProcessor& filter, int parameter, String /*name*/, float defval)
{

    Knob* knob = new Knob ("knob", 48, &processor);


    knobAttachments.add (new Knob::KnobAttachment (filter.getPluginState(),
                                                   filter.getEngineParameterId (parameter),
                                                   *knob));
    
	knob->setSliderStyle (Slider::RotaryVerticalDrag);
	knob->setTextBoxStyle (knob->NoTextBox, true, 0, 0);
	knob->setRange (0, 1);
	knob->setBounds (x, y, d+(d/6), d+(d/6));
	knob->setTextBoxIsEditable (false);
	knob->setDoubleClickReturnValue (true, defval, ModifierKeys::noModifiers);
    knob->setValue (filter.getPluginState().getParameter (filter.getEngineParameterId (parameter))->getValue());
    addAndMakeVisible (knob);
    
	return knob;
}


void ObxdAudioProcessorEditor::clean()
{
    this->removeAllChildren();
}

TooglableButton* ObxdAudioProcessorEditor::addButton (int x, int y, int w, int h, ObxdAudioProcessor& filter, int parameter, String name)
{
    TooglableButton* button = new TooglableButton ("button", &processor);

    if (parameter != UNLEARN){
        toggleAttachments.add (new AudioProcessorValueTreeState::ButtonAttachment (filter.getPluginState(),
                                                                      filter.getEngineParameterId (parameter),
                                                                      *button));
    } else {
        button->addListener(this);
    }
	button->setBounds (x, y, w, h);
	button->setButtonText (name);
    button->setToggleState(filter.getPluginState().getParameter (filter.getEngineParameterId (parameter))->getValue(),
                      dontSendNotification);
    
    addAndMakeVisible (button);
    
	return button;
}

void ObxdAudioProcessorEditor::actionListenerCallback(const String& message)
{
    if (message.equalsIgnoreCase(Action::panReset))
    {
        const StringArray parameters{ "pan1Knob", "pan2Knob", "pan3Knob", "pan4Knob", "pan5Knob", "pan6Knob", "pan7Knob", "pan8Knob" };
        for (const auto& parameter : parameters)
        {
            if (auto* knob = dynamic_cast<Knob*>(mappingComps[parameter]))
            {
                knob->setValue(knob->getDoubleClickReturnValue());
            } 
        }
    }
}

Rectangle<int> ObxdAudioProcessorEditor::transformBounds(int x, int y, int w, int h)
{
    if (getScaleFactor() == 1.0f)
        return Rectangle<int>(x, y, w, h);

    return Rectangle<int>(x, y, w, h).toFloat().transformedBy(AffineTransform::scale(getScaleFactor())).toNearestInt();
}
ImageButton* ObxdAudioProcessorEditor::addMenuButton (int x, int y, int d, String imgName)
{
    
    ImageMenu* imageButton = new ImageMenu(imgName, &processor);
    imageButtons.add (imageButton);
    imageButton->setBounds (x, y, d, d);
    

    imageButton->onClick = [this](){
        ImageButton *imageButton = this->imageButtons[0];
        auto x   = imageButton->getScreenX();
        auto y   = imageButton->getScreenY();
        auto dx  = imageButton->getWidth();
        auto pos = Point<int> (x, y + dx);
        resultFromMenu (pos);
    };
    addAndMakeVisible (imageButton);
    return imageButton;
}

void ObxdAudioProcessorEditor::rebuildComponents (ObxdAudioProcessor& ownerFilter)
{
	skinFolder = ownerFilter.getCurrentSkinFolder();

	ownerFilter.removeChangeListener (this);

    // deleteAllChildren();  // WATCH OUT!

    setSize (1440, 450);

	ownerFilter.addChangeListener (this);
	repaint();
    
}

void ObxdAudioProcessorEditor::createMenu ()
{
#if JUCE_MAC
	bool enablePasteOption = macPasteboard::containsPresetData();	// Check if the clipboard contains data for a Preset
#else
    juce::MemoryBlock memoryBlock;
    memoryBlock.fromBase64Encoding(SystemClipboard::getTextFromClipboard());
    bool enablePasteOption = processor.isMemoryBlockAPreset(memoryBlock);
#endif
    popupMenus.clear();
    PopupMenu* menu = new PopupMenu();
    //menu->setLookAndFeel(new CustomLookAndFeel(&this->processor));
    PopupMenu progMenu;
    PopupMenu bankMenu;
    PopupMenu skinMenu;
    PopupMenu fileMenu;
    //PopupMenu viewMenu;
    PopupMenu midiMenu;
    menu->setLookAndFeel(&this->getLookAndFeel());
    progMenu.setLookAndFeel(&this->getLookAndFeel());
    bankMenu.setLookAndFeel(&this->getLookAndFeel());
    skinMenu.setLookAndFeel(&this->getLookAndFeel());
    fileMenu.setLookAndFeel(&this->getLookAndFeel());
    midiMenu.setLookAndFeel(&this->getLookAndFeel());
    skins = processor.getSkinFiles();
    banks = processor.getBankFiles();
    {
        
        fileMenu.addItem(static_cast<int>(MenuAction::ImportPreset),
                     "Import Preset...",
                     true,
                     false);
        
        fileMenu.addItem(static_cast<int>(MenuAction::ImportBank),
                     "Import Bank...",
                     true,
                     false);
        
        fileMenu.addSeparator();
        
        fileMenu.addItem(static_cast<int>(MenuAction::ExportPreset),
                     "Export Preset...",
                     true,
                     false);
        
        fileMenu.addItem(static_cast<int>(MenuAction::ExportBank),
                     "Export Bank...",
                     true,
                     false);

        fileMenu.addSeparator();
        
        fileMenu.addItem(static_cast<int>(MenuAction::NewPreset),
                     "New Preset...",
                     true,//enableNewPresetOption,
                     false);
        
        fileMenu.addItem(static_cast<int>(MenuAction::RenamePreset),
                         "Rename Preset...",
                         true,
                         false);
        
        fileMenu.addItem(static_cast<int>(MenuAction::SavePreset),
                     "Save Preset...",
                     true,
                     false);
        
        fileMenu.addItem(static_cast<int>(MenuAction::DeletePreset),
                     "Delete Preset...",
                     true,
                     false);
        

		fileMenu.addSeparator();
		
		fileMenu.addItem(static_cast<int>(MenuAction::CopyPreset),
					 "Copy Preset...",
					 true,
					 false);
		
		fileMenu.addItem(static_cast<int>(MenuAction::PastePreset),
					 "Paste Preset...",
					 enablePasteOption,
					 false);

		/*
        fileMenu.addItem(static_cast<int>(MenuAction::DeleteBank),
                     "Delete Bank...",
                     true,
                     false);
        */
        menu->addSubMenu("File", fileMenu);
    }
    
    {
        for (int i = 0; i < processor.getNumPrograms(); ++i)
        {
            progMenu.addItem (i + progStart + 1,
                              processor.getProgramName (i),
                              true,
                              i == processor.getCurrentProgram());
        }
        
        menu->addSubMenu("Programs", progMenu);
    }

    menu->addItem(progStart + 1000, "Preset Bar", true, processor.showPresetBar);
    
    {
        const String currentBank = processor.getCurrentBankFile().getFileName();
        
        for (int i = 0; i < banks.size(); ++i)
        {
            const File bank = banks.getUnchecked (i);
            bankMenu.addItem (i + bankStart + 1,
                              bank.getFileNameWithoutExtension(),
                              true,
                              bank.getFileName() == currentBank);
        }
        
        menu->addSubMenu ("Banks", bankMenu);
    }

    
    {
        for (int i = 0; i < skins.size(); ++i)
        {
            const File skin = skins.getUnchecked (i);
            skinMenu.addItem (i + skinStart + 1,
                              skin.getFileName(),
                              true,
                              skin.getFileName() == skinFolder.getFileName());
        }
        
        menu->addSubMenu ("Themes", skinMenu);
        // About // menu.addItem(1, String("Release ") +  String(JucePlugin_VersionString).dropLastCharacters(2), false);
    }

    //menu->addSubMenu ("View", viewMenu);
    menuMidiNum = progStart +2000;
    createMidi(menuMidiNum, midiMenu);
    menu->addSubMenu ("MIDI", midiMenu);
    popupMenus.add (menu);
    
    PopupMenu scaleMenu;
    menuScaleNum =progStart +2000 + 3000;
    scaleMenu.addItem(menuScaleNum, "1x", true, getScaleFactor() == 1.0f);
    scaleMenu.addItem(menuScaleNum+1, "1.5x", true, getScaleFactor() == 1.5f);
    scaleMenu.addItem(menuScaleNum+2, "2x", true, getScaleFactor() == 2.0f);
    menu->addSubMenu("GUI Size", scaleMenu, true);

    PopupMenu tooltipMenu;
    tooltipMenu.addItem("Disabled", true, processor.getTooltipBehavior() == Tooltip::Disable, [&]
        {
            processor.setTooltipBehavior(Tooltip::Disable);
            resized();
        });
    tooltipMenu.addItem("Standard Display", true, processor.getTooltipBehavior() == Tooltip::StandardDisplay, [&]
        {
            processor.setTooltipBehavior(Tooltip::StandardDisplay);
            resized();
        });
    tooltipMenu.addItem("Full Display", true, processor.getTooltipBehavior() == Tooltip::FullDisplay, [&]
        {
            processor.setTooltipBehavior(Tooltip::FullDisplay);
            resized();
        });
    menu->addSubMenu("Tooltips", tooltipMenu, true);

#ifdef LINUX
    menu->addItem(1, String("Release ") +  String(JucePlugin_VersionString).dropLastCharacters(2), false);
#endif

#if defined(JUCE_MAC) || defined(WIN32)
    PopupMenu helpMenu;
    helpMenu.setLookAndFeel(&this->getLookAndFeel());
    String version = String("Release ") +  String(JucePlugin_VersionString).dropLastCharacters(2);
    helpMenu.addItem(menuScaleNum+4, "Manual", true);
    helpMenu.addItem(menuScaleNum+3, version, false);
    menu->addSubMenu("Help", helpMenu, true);
#endif
}

void ObxdAudioProcessorEditor::createMidi(int menuNo, PopupMenu &menuMidi) {
    File midi_dir = processor.getMidiFolder();
    File default_file = midi_dir.getChildFile("Default.xml");
    if(default_file.exists()){
        if (processor.currentMidiPath != default_file.getFullPathName()){
            menuMidi.addItem(menuNo++, default_file.getFileNameWithoutExtension(), true, false);
        } else {
            menuMidi.addItem(menuNo++, default_file.getFileNameWithoutExtension(), true, true);
        }
        midiFiles.add(default_file.getFullPathName());
    }
    
    File custom_file = midi_dir.getChildFile("Custom.xml");
    
    if(custom_file.exists()){
         if (processor.currentMidiPath != custom_file.getFullPathName()){
             menuMidi.addItem(menuNo++, custom_file.getFileNameWithoutExtension(), true, false);
         } else {
             menuMidi.addItem(menuNo++, custom_file.getFileNameWithoutExtension(), true, true);
         }
        midiFiles.add(custom_file.getFullPathName());
    }
    
    DirectoryIterator iter (midi_dir, true, "*.xml");
    StringArray list;
    while (iter.next())
    {
        list.add(iter.getFile().getFullPathName());
    }
    
    list.sort(true);
    
    for (int i =0; i < list.size() ; i ++){
        File f (list[i]);
        if (f.getFileNameWithoutExtension() != "Default" && f.getFileNameWithoutExtension() != "Custom" && f.getFileNameWithoutExtension() != "Config") {
            if (processor.currentMidiPath != f.getFullPathName()){
                menuMidi.addItem(menuNo++, f.getFileNameWithoutExtension(), true, false);
            } else {
                menuMidi.addItem(menuNo++, f.getFileNameWithoutExtension(), true, true);
            }
            midiFiles.add(f.getFullPathName());
        }
    }
}

void ObxdAudioProcessorEditor::resultFromMenu (const Point<int> pos)
{
    createMenu();
    int result = popupMenus[0]->showAt (Rectangle<int> (pos.getX(), pos.getY(), 1, 1));
    
    if (result >= (skinStart + 1) && result <= (skinStart + skins.size()))
    {
        result -= 1;
        result -= skinStart;
        
        const File newSkinFolder = skins.getUnchecked (result);
        processor.setCurrentSkinFolder (newSkinFolder.getFileName());
        
        //rebuildComponents (processor);
        clean();
        loadSkin (processor);
    }
    else if (result >= (bankStart + 1) && result <= (bankStart + banks.size()))
    {
        result -= 1;
        result -= bankStart;
        
        const File bankFile = banks.getUnchecked (result);
        processor.loadFromFXBFile (bankFile);
        //clean();
        //loadSkin (processor); // Check this
    }
    else if (result >= (progStart + 1) && result <= (progStart + processor.getNumPrograms()))
    {
        result -= 1;
        result -= progStart;
        processor.setCurrentProgram (result);
        //clean();
        //loadSkin (processor); // Check this
    }
    else if (result < progStart){
        MenuActionCallback(result);
    }
    else if (result == progStart + 1000){
        processor.setShowPresetBar(!processor.getShowPresetBar());
        //createMenu();
        updatePresetBar(true);
    }
    else if (result >= menuScaleNum){
        
        if (result ==menuScaleNum){
            ScalableComponent::setScaleFactor(1.0f, isHighResolutionDisplay());
            processor.setGuiSize(1);
        }
        else if (result == menuScaleNum+1){
            ScalableComponent::setScaleFactor(1.5f, isHighResolutionDisplay());
            processor.setGuiSize(2);
        }
        else if (result == menuScaleNum+2) {
            ScalableComponent::setScaleFactor(2.0f, isHighResolutionDisplay());
            processor.setGuiSize(4);
        }
        else if (result == menuScaleNum+4) {
            File manualFile = processor.getDocumentFolder().getChildFile("OB-Xd Manual.pdf");
            openInPdf(manualFile);
        }
    }
    else if (result >= menuMidiNum){
        unsigned int selected_idx = result - menuMidiNum;
        if (selected_idx >= 0 && selected_idx < midiFiles.size()){
            File f(midiFiles[selected_idx]);
            if (f.exists()) {
                processor.currentMidiPath = midiFiles[selected_idx];
                processor.bindings.loadFile(f);
                processor.updateConfig();
                
                //createMenu();
            }
        }
    }
}

void ObxdAudioProcessorEditor::updatePresetBar(bool resize){
    DBG(" H: " << getHeight() <<" W:" <<getWidth() << " CW:"<<presetBar->getWidth() << " CH" <<presetBar->getHeight() << " CX:" <<presetBar->getX()  << " CY: " <<presetBar->getY());
    
    if (processor.getShowPresetBar()) {
        if (resize) {
            this->setSize(this->getWidth(), this->getHeight() + presetBar->getHeight());
        }
        presetBar->setVisible(true);
        presetBar->update();
        presetBar->setBounds((getWidth() - presetBar->getWidth()) / 2, getHeight() -presetBar->getHeight(), presetBar->getWidth(), presetBar->getHeight());
    }
    else if (presetBar->isVisible()) {
        if (resize) {
            this->setSize(this->getWidth(), this->getHeight() - presetBar->getHeight());
        }
        presetBar->setVisible(false);
    }
}

void ObxdAudioProcessorEditor::MenuActionCallback(int action){
    
    
    if (action == MenuAction::ImportBank)
    {
        fileChooser = std::make_unique<juce::FileChooser>("Import Bank (*.fxb)", juce::File(), "*.fxb", true);
        
        if (fileChooser->browseForFileToOpen()) {
            File result = fileChooser->getResult();
            auto name = result.getFileName().replace("%20", " ");
            auto file = processor.getBanksFolder().getChildFile(name);
            
            if (result == file || result.copyFileTo(file)){
                processor.loadFromFXBFile(file);
                processor.scanAndUpdateBanks();
                //createMenu();
            }
        }
    };
    
    if (action == MenuAction::ExportBank)
    {
        auto file = processor.getDocumentFolder().getChildFile("Banks");
        FileChooser myChooser ("Export Bank (*.fxb)", file, "*.fxb", true);
        if(myChooser.browseForFileToSave(true))
        {
            File result = myChooser.getResult();
            
            String temp = result.getFullPathName();
            if (!temp.endsWith(".fxb")) {
                temp += ".fxb";
            }
            processor.saveBank(File(temp));
            
        }
    };
    
    if (action == MenuAction::DeleteBank)
    {
        if(NativeMessageBox::showOkCancelBox(AlertWindow::NoIcon, "Delete Bank", "Delete current bank: " + processor.currentBank + "?")){
            processor.deleteBank();
        }
    }
    
    
    if (action == MenuAction::SavePreset)
    {
        auto presetName = processor.currentPreset;
        if (presetName.isEmpty() )
        {
            processor.saveBank();
            return;
        }
        processor.savePreset();
        processor.saveBank();
    }
    
    if (action == MenuAction::NewPreset)
    {
        setPresetNameWindow = std::make_unique<SetPresetNameWindow>();
        //preventBackgroundClick();
        addAndMakeVisible(setPresetNameWindow.get());
        resized();
       
        auto callback = [this](int i, juce::String name)
        {
            if (i)
            {
                if (name.isNotEmpty())
                {
                    processor.newPreset(name);
                    //createMenu();
                }
            }

            setPresetNameWindow.reset();
            //preventBackgroundClickComponent.reset();
        };
        
        setPresetNameWindow->callback = callback;
        setPresetNameWindow->grabTextEditorFocus();
        
        return;
    }
    
    if (action == MenuAction::RenamePreset)
    {
        setPresetNameWindow = std::make_unique<SetPresetNameWindow>();
        //preventBackgroundClick();
        setPresetNameWindow->setText(processor.getProgramName(processor.getCurrentProgram()));
        addAndMakeVisible(setPresetNameWindow.get());
        resized();
       
        auto callback = [this](int i, juce::String name)
        {
            if (i)
            {
                if (name.isNotEmpty())
                {
                    processor.changePresetName(name);
                    //createMenu();
                }
            }

            setPresetNameWindow.reset();
            //preventBackgroundClickComponent.reset();
        };
        
        setPresetNameWindow->callback = callback;
        setPresetNameWindow->grabTextEditorFocus();
        
        return;
    }
    
    if (action == MenuAction::DeletePreset)
    {
        if(NativeMessageBox::showOkCancelBox(AlertWindow::NoIcon, "Delete Preset", "Delete current preset " + processor.currentPreset + "?")){
            processor.deletePreset();
            //createMenu();
        }
        return;
    }
    

    if (action == MenuAction::ImportPreset)
    {
        DBG("Import Preset");
        fileChooser = std::make_unique<juce::FileChooser>("Import Preset (*.fxp)", juce::File(), "*.fxp", true);

        if (fileChooser->browseForFileToOpen()) {
            File result = fileChooser->getResult();
            //auto name = result.getFileName().replace("%20", " ");
            //auto file = processor.getPresetsFolder().getChildFile(name);
            DBG("Import Preset: " << result.getFileName());
            //if (result == file || result.copyFileTo(file)){
                processor.loadPreset(result);
                //createMenu();
            //}
        }
    };
    
    if (action == MenuAction::ExportPreset)
    {
        
        auto file = processor.getPresetsFolder();
        FileChooser myChooser ("Export Preset (*.fxp)", file, "*.fxp", true);
        if(myChooser.browseForFileToSave(true))
        {
            File result = myChooser.getResult();
            
            String temp = result.getFullPathName();
            if (!temp.endsWith(".fxp")) {
                temp += ".fxp";
            }
            processor.savePreset(File(temp));
            
        }
    };

#if JUCE_MAC
	// Copy to clipboard
	if (action == MenuAction::CopyPreset)
	{
		juce::MemoryBlock serializedData;

		// Serialize the Preset, produces the same data as an export but into memory instead of a file.
		processor.serializePreset(serializedData);

		// Place the data onto the clipboard
		macPasteboard::copyPresetDataToClipboard(serializedData.getData(), serializedData.getSize());
	}

	// Paste from clipboard
	if (action == MenuAction::PastePreset)
	{
		juce::MemoryBlock memoryBlock;

		// Fetch Preset data from the clipboard
		if (macPasteboard::fetchPresetDataFromClipboard(memoryBlock))
		{
			// Load the data
			processor.loadFromMemoryBlock(memoryBlock);	//loadPreset(memoryBlock);
		}
	}
#else
    // Copy to clipboard
    if (action == MenuAction::CopyPreset)
    {
        juce::MemoryBlock serializedData;

        // Serialize the Preset, produces the same data as an export but into memory instead of a file.
        processor.serializePreset(serializedData);
        
        // Place the data onto the clipboard
        SystemClipboard::copyTextToClipboard(serializedData.toBase64Encoding());
    }

    // Paste from clipboard
    if (action == MenuAction::PastePreset)
    {
        juce::MemoryBlock memoryBlock;

        // Fetch Preset data from the clipboard
        memoryBlock.fromBase64Encoding(SystemClipboard::getTextFromClipboard());

        // Load the data
        processor.loadFromMemoryBlock(memoryBlock);	//loadPreset(memoryBlock);
        
    }
#endif
}



void ObxdAudioProcessorEditor::deleteBank()
{
}



void ObxdAudioProcessorEditor::nextProgram() {
    int cur = processor.getCurrentProgram() +  1;
    if (cur == processor.getNumPrograms()) {
        cur = 0;
    }
    processor.setCurrentProgram (cur, false);
    
    needNotifytoHost = true;
    countTimer = 0;
// Define for win and mac?
    //clean();
    //loadSkin (processor); // check
}
void ObxdAudioProcessorEditor::prevProgram() {
    int cur = processor.getCurrentProgram() -  1;
    if (cur < 0) {
        cur = processor.getNumPrograms() - 1;
    }
    processor.setCurrentProgram (cur, false);
    
    needNotifytoHost = true;
    countTimer = 0;
    
    //clean();
    //loadSkin (processor);
}
void ObxdAudioProcessorEditor::buttonClicked (Button* b)
{
    /*
    auto imageButton = dynamic_cast<ImageButton*> (b);
    
    if (imageButton == imageButtons[0])
    {
        auto x   = imageButton->getScreenX();
        auto y   = imageButton->getScreenY();
        auto dx  = imageButton->getWidth();
        auto pos = Point<int> (x, y + dx);

        resultFromMenu (pos);
    }*/
    
    
    auto toggleButton = dynamic_cast<TooglableButton*> (b);
    if (toggleButton == midiUnlearnButton){
        if (midiUnlearnButton->getToggleState()){
            countTimerForLed = 0;
            processor.getMidiMap().reset();
            processor.getMidiMap().set_default();
            processor.sendChangeMessage();
        }
    }
    
}

//==============================================================================

void ObxdAudioProcessorEditor::updateFromHost() {
    for (int i = 0; i < knobAttachments.size(); ++i)
    {
        knobAttachments[i]->updateToSlider();
    }
    /*
    for (int i = 0; i < toggleAttachments.size(); ++i)
    {
        toggleAttachments[i]->updateToSlider();
    }*/
    
    for (int i = 0; i < buttonListAttachments.size(); ++i)
    {
        buttonListAttachments[i]->updateToSlider();
    }
    
    // Set to unlearn to false
    //if ( midiUnlearnButton && midiUnlearnButton->getToggleState()) {
    //    Thread::sleep(500);
    //    midiUnlearnButton->setToggleState(false, NotificationType:: sendNotification);
    //}
    
    repaint();
}
void ObxdAudioProcessorEditor::changeListenerCallback (ChangeBroadcaster* source)
{
    updateFromHost();
}

void ObxdAudioProcessorEditor::mouseUp (const MouseEvent& e)
{
	if (e.mods.isRightButtonDown() || e.mods.isCommandDown())
	{
        resultFromMenu (e.getMouseDownScreenPosition());
	}
}


void ObxdAudioProcessorEditor::handleAsyncUpdate() {
    scaleFactorChanged();
    repaint();
}

void ObxdAudioProcessorEditor::paint(Graphics& g)
{
    
    const float newPhysicalPixelScaleFactor =
        g.getInternalContext().getPhysicalPixelScaleFactor();

    if (newPhysicalPixelScaleFactor != processor.physicalPixelScaleFactor)
    {
        processor.physicalPixelScaleFactor = newPhysicalPixelScaleFactor;
        scaleFactorChanged();
    }

	g.fillAll (Colours::black);
    
    // background gui
    if(processor.showPresetBar){
        g.drawImage(backgroundImage,
                    0, 0, getWidth(), getHeight() - 40,
                    0, 0, backgroundImage.getWidth(), backgroundImage.getHeight());
    } else {
        g.drawImage(backgroundImage,
                    0, 0, getWidth(), getHeight(),
                    0, 0, backgroundImage.getWidth(), backgroundImage.getHeight());
    }
    
    /*
#if JUCE_WINDOWS || JUCE_LINUX
	const File mainFile(skinFolder.getChildFile("main.png"));
#else
    const File mainFile(skinFolder.getChildFile("main@2x.png"));
#endif
    
    if (!notLoadSkin && skinFolder.exists() && mainFile.exists())
	{
        
        const Image image = ImageCache::getFromFile(mainFile);
        
#if JUCE_WINDOWS || JUCE_LINUX
        g.drawImage (image,
                     0, 0, image.getWidth(), image.getHeight(),
                     0, 0, image.getWidth(), image.getHeight());
#else
        g.drawImage (image,
                     0, 0, image.getWidth()/2, image.getHeight()/2,
                     0, 0, image.getWidth(), image.getHeight());
#endif

	}
	else
	{
		const Image image = ImageCache::getFromMemory(BinaryData::main_png, BinaryData::main_pngSize);
        
        // g.setImageResamplingQuality(Graphics::ResamplingQuality::highResamplingQuality);
        
		g.drawImage (image,
					 0, 0, image.getWidth(), image.getHeight(),
					 0, 0, image.getWidth(), image.getHeight());
	}
     */

}


bool ObxdAudioProcessorEditor::isInterestedInFileDrag(const StringArray& files)
{
    StringArray extensions;
    extensions.add(".fxp");
    extensions.add(".fxb");

    if (files.size() == 1) {
        File file = File(files[0]);
        String ext = file.getFileExtension().toLowerCase();
        return file.existsAsFile() && extensions.contains(ext);
    } else {
        for (int q = 0; q < files.size(); q++) {
            File file = File(files[q]);
            String ext = file.getFileExtension().toLowerCase();
            
            if (ext == ".fxb" || ext == ".fxp") {
                return true;
            }
        }
    }
    return false;
}

void ObxdAudioProcessorEditor::filesDropped(const StringArray& files, int x, int y)
{
    if (files.size() == 1) {
        File file = File(files[0]);
        String ext = file.getFileExtension().toLowerCase();
        
        if (ext == ".fxp") {
            processor.loadPreset(file);
            //createMenu();
        } else if (ext == ".fxb") {
            auto name = file.getFileName().replace("%20", " ");
            auto result = processor.getBanksFolder().getChildFile(name);
            
            if (file.copyFileTo(result)){
                processor.loadFromFXBFile(result);
                processor.scanAndUpdateBanks();
                //createMenu();
            }
        } 
    } else {
        int i = processor.getCurrentProgram();

        for (int q = 0; q < files.size(); q++) {
            File file = File(files[q]);
            String ext = file.getFileExtension().toLowerCase();
            if (ext == ".fxp") {
                processor.setCurrentProgram(i++);
                processor.loadPreset(file);
            }
            if (i >=processor.getNumPrograms()){
                i = 0;
            }
        }
        processor.sendChangeMessage();
        //createMenu();
    }
}

const String ObxdAudioProcessorEditor::Action::panReset{ "panReset" };
/*
bool ObxdAudioProcessorEditor::keyPressed(const KeyPress & press) {
    if (press.getKeyCode() == '+' || press.getKeyCode() == KeyPress::numberPadAdd)
    {
        nextProgram();
        return false; // r+eturn true when the keypress was handled
    }
    if (press.getKeyCode() == '-' || press.getKeyCode() == KeyPress::numberPadSubtract)
    {
        prevProgram();
        return false; // return true when the keypress was handled
    }
    
    return false; // return false if you don't handle the keypress
}*/
