#include "PluginSearchPathEditor.hpp"

using namespace juce;

static std::unique_ptr<XmlElement> vecToXml(const std::string & elemName,
                                            const std::vector<std::string> & v)
{
    auto elem = std::make_unique<XmlElement>(juce::String(elemName));
    for(auto & s : v) {
        auto childElem = std::make_unique<XmlElement>("Elem");
        childElem->setAttribute("v", s);
        elem->addChildElement(childElem.release());
    }
    return elem;
}

std::unique_ptr<XmlElement> SearchPathSettings::toXml() const {
    auto elem = std::make_unique<XmlElement>("SearchPathSettings");
    elem->setAttribute("useVstDefaultSearchPaths", useVstDefaultSearchPaths);
    elem->setAttribute("useVst3DefaultSearchPaths", useVst3DefaultSearchPaths);
    
    elem->addChildElement(vecToXml("vstSearchPaths", vstSearchPaths).release());
    elem->addChildElement(vecToXml("vst3SearchPaths", vst3SearchPaths).release());
    
    return elem;
}

void SearchPathSettings::fromXml(XmlElement* elem) {
    useVstDefaultSearchPaths = elem->getBoolAttribute("useVstDefaultSearchPaths");
    useVst3DefaultSearchPaths = elem->getBoolAttribute("useVstDefaultSearchPaths");
    vstSearchPaths.clear();
    vst3SearchPaths.clear();
    for(auto * childElem : elem->getChildIterator()) {
        auto & dest = (childElem->getTagName() == "vstSearchPaths") ? vstSearchPaths : vst3SearchPaths;
        for(auto * grandchildElem : childElem->getChildIterator()) {
            dest.push_back(grandchildElem->getStringAttribute("v").toStdString());
        }
    }
}

class SearchPathListModel final : public Component, public ListBoxModel
{
public:
    SearchPathListModel(const std::vector<std::string> & paths)
    : paths(paths),
      lastRowSelected(-1)
    { }
    ~SearchPathListModel() { }
    
    int getNumRows() override {
        return (int)paths.size();
    }
    
    void paintListBoxItem(int rowNumber,
                          Graphics& g,
                          int width,
                          int height,
                          bool rowIsSelected)
    override {
        if(rowNumber < 0 || rowNumber >= paths.size()) {
            return;
        }
        if(rowIsSelected) {
            g.fillAll(Colours::mediumblue);
        }
        
        g.setColour(Colours::lightgrey);
        g.drawText(paths[rowNumber], 4, 0, width - 4, height, Justification::left, true);
    }
    
    void selectedRowsChanged(int lastRowSelectedIn) override {
        lastRowSelected = lastRowSelectedIn;
    }
    
    const std::vector<std::string> & paths;
    int lastRowSelected;
};

PluginSearchPathEditor::PluginSearchPathEditor(SearchPathSettings & settings,
                                               std::function<void()> closedHandler,
                                               std::function<void()> settingsEditedHandler)
    : DocumentWindow ("Edit Plugin Search Paths",
                      LookAndFeel::getDefaultLookAndFeel().findColour (ResizableWindow::backgroundColourId),
                      DocumentWindow::minimiseButton | DocumentWindow::closeButton),
      closedHandler(std::move(closedHandler)),
      settingsEditedHandler(std::move(settingsEditedHandler)),
      settings(settings)
{
    setContentOwned(new PluginSearchPathEditorContent(*this), false);
    setUsingNativeTitleBar(true);
    setResizable(false, false);
    setSize(600, 380);
    auto primaryDisplay = Desktop::getInstance().getDisplays().getPrimaryDisplay();
    if(primaryDisplay) {
        auto r = primaryDisplay->userArea;
        setTopLeftPosition((r.getWidth() - getWidth()) / 2, (r.getHeight() - getHeight()) / 2);
    } else {
        setTopLeftPosition(20, 20);
    }
    setOpaque(true);
    setVisible(true);
    toFront(false);
}

PluginSearchPathEditor::~PluginSearchPathEditor() { }

void PluginSearchPathEditor::closeButtonPressed() {
    closedHandler();
}

void PluginSearchPathEditor::edited() {
    settingsEditedHandler();
}

SearchPathEditorWidget::SearchPathEditorWidget(std::vector<std::string> & paths,
                                               std::function<void()> edited)
    : paths(paths),
      edited(edited)
{
    listModel = std::make_unique<SearchPathListModel>(paths);
    listBox.setModel(listModel.get());
    
    addAndMakeVisible(&listBox);
    
    addButton.setButtonText("+");
    addButton.addListener(this);
    addAndMakeVisible(&addButton);
    removeButton.setButtonText("-");
    removeButton.addListener(this);
    addAndMakeVisible(&removeButton);
}

SearchPathEditorWidget::~SearchPathEditorWidget()
{
    listBox.setModel(nullptr);
    addButton.removeListener(this);
    removeButton.removeListener(this);
}

void SearchPathEditorWidget::resized()
{
    listBox.setTopLeftPosition(10, 10);
    listBox.setSize(getWidth() - 20, getHeight() - 50);
    addButton.setTopLeftPosition(getWidth() - 65, getHeight() - 35);
    addButton.setSize(25, 25);
    removeButton.setTopLeftPosition(getWidth() - 35, getHeight() - 35);
    removeButton.setSize(25, 25);
}

void SearchPathEditorWidget::buttonClicked(juce::Button *b)
{
    if(b == &addButton) {
        if(fileChooser) {
            return;
        }
        fileChooser = std::make_unique<FileChooser>("Select search path");
        fileChooser->launchAsync(FileBrowserComponent::openMode
                                 | FileBrowserComponent::canSelectDirectories,
                                 [this](const FileChooser& c) {
            auto res = c.getResults();
            if(res.size() > 0) {
                auto p = res[0].getFullPathName().toStdString();
                for(auto & p0: paths) {
                    if(p0 == p) {
                        return;
                    }
                }
                paths.push_back(res[0].getFullPathName().toStdString());
                listBox.updateContent();
                edited();
            }
            fileChooser.reset();
        });
    } else {
        int rowToDelete = listModel->lastRowSelected;
        listModel->lastRowSelected = -1;
        listBox.setSelectedRows({}, NotificationType::dontSendNotification);
        if(rowToDelete < 0 || rowToDelete >= paths.size()) {
            return;
        }
        paths.erase(paths.begin() + rowToDelete);
        listBox.updateContent();
        edited();
    }
}

PluginSearchPathEditorContent::PluginSearchPathEditorContent(PluginSearchPathEditor & editor)
    : editor(editor),
      e_vst(editor.settings.vstSearchPaths, [this]{ edited(); }),
      e_vst3(editor.settings.vst3SearchPaths, [this]{ edited(); })
{
    l_vst.setText("VST", NotificationType::dontSendNotification);
    l_vst3.setText("VST3", NotificationType::dontSendNotification);
    
    d_vst.setButtonText("Include default search paths");
    d_vst.setToggleState(editor.settings.useVstDefaultSearchPaths, NotificationType::dontSendNotification);
    d_vst.addListener(this);
    d_vst3.setButtonText("Include default search paths");
    d_vst3.setToggleState(editor.settings.useVst3DefaultSearchPaths, NotificationType::dontSendNotification);
    d_vst3.addListener(this);
    
    addAndMakeVisible(&l_vst);
    addAndMakeVisible(&l_vst3);
    addAndMakeVisible(&d_vst);
    addAndMakeVisible(&d_vst3);
    addAndMakeVisible(&e_vst);
    addAndMakeVisible(&e_vst3);
}

PluginSearchPathEditorContent::~PluginSearchPathEditorContent()
{
    d_vst.removeListener(this);
    d_vst3.removeListener(this);
}

void PluginSearchPathEditorContent::resized()
{
    l_vst.setTopLeftPosition(10, 10);
    l_vst.setSize(getWidth() - 20, 20);
    d_vst.setTopLeftPosition(100, 10);
    d_vst.setSize(getWidth() - 100, 20);
    e_vst.setTopLeftPosition(10, 30);
    e_vst.setSize(getWidth() - 20, 150);
    
    l_vst3.setTopLeftPosition(10, 200);
    l_vst3.setSize(getWidth() - 20, 20);
    d_vst3.setTopLeftPosition(100, 200);
    d_vst3.setSize(getWidth() - 100, 20);
    e_vst3.setTopLeftPosition(10, 220);
    e_vst3.setSize(getWidth() - 20, 150);
}

void PluginSearchPathEditorContent::edited()
{
    editor.edited();
}

void PluginSearchPathEditorContent::buttonClicked(juce::Button *b)
{
    editor.settings.useVstDefaultSearchPaths = d_vst.getToggleState();
    editor.settings.useVst3DefaultSearchPaths = d_vst3.getToggleState();
    edited();
}
