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

   This file is part of the JUCE library.
   Copyright (c) 2022 - Raw Material Software Limited

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
   Agreement and JUCE Privacy Policy.

   End User License Agreement: www.juce.com/juce-7-licence
   Privacy Policy: www.juce.com/juce-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

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

#include "../jucer_Headers.h"
#include "../jucer_Application.h"

#include "jucer_StartPageComponent.h"
#include "jucer_StartPageTreeHolder.h"
#include "jucer_NewProjectTemplates.h"
#include "jucer_ContentComponents.h"

//==============================================================================
struct ContentComponent  : public Component
{
    ContentComponent()
    {
        setTitle ("Content");
        setFocusContainerType (FocusContainerType::focusContainer);
    }

    void resized() override
    {
        if (content != nullptr)
            content->setBounds (getLocalBounds());
    }

    void setContent (std::unique_ptr<Component>&& newContent)
    {
        if (content.get() != newContent.get())
        {
            content = std::move (newContent);
            addAndMakeVisible (content.get());
            resized();
        }
    }

private:
    std::unique_ptr<Component> content;

    //==============================================================================
    JUCE_LEAK_DETECTOR (ContentComponent)
};

//==============================================================================
static File findExampleFile (int dirIndex, int index)
{
    auto dir = ProjucerApplication::getSortedExampleDirectories()[dirIndex];
    return ProjucerApplication::getSortedExampleFilesInDirectory (dir)[index];
}

static std::unique_ptr<Component> createExampleProjectsTab (ContentComponent& content, std::function<void (const File&)> cb)
{
    StringArray exampleCategories;
    std::vector<StringArray> examples;

    for (auto& dir : ProjucerApplication::getSortedExampleDirectories())
    {
        exampleCategories.add (dir.getFileName());

        StringArray ex;
        for (auto& f : ProjucerApplication::getSortedExampleFilesInDirectory (dir))
            ex.add (f.getFileNameWithoutExtension());

        examples.push_back (ex);
    }

    if (exampleCategories.isEmpty())
        return nullptr;

    auto selectedCallback = [&, cb] (int category, int index) mutable
    {
        content.setContent (std::make_unique<ExampleComponent> (findExampleFile (category, index), cb));
    };

    return std::make_unique<StartPageTreeHolder> ("Examples",
                                                  exampleCategories,
                                                  examples,
                                                  std::move (selectedCallback),
                                                  StartPageTreeHolder::Open::no);
}

//==============================================================================
static StringArray getAllTemplateCategoryStrings()
{
    StringArray categories;

    for (auto& t : NewProjectTemplates::getAllTemplates())
        categories.addIfNotAlreadyThere (NewProjectTemplates::getProjectCategoryString (t.category));

    return categories;
}

static std::vector<NewProjectTemplates::ProjectTemplate> getTemplatesInCategory (const String& category)
{
    std::vector<NewProjectTemplates::ProjectTemplate> templates;

    for (auto& t : NewProjectTemplates::getAllTemplates())
        if (NewProjectTemplates::getProjectCategoryString (t.category) == category)
            templates.push_back (t);

    return templates;
}

static StringArray getAllTemplateNamesForCategory (const String& category)
{
    StringArray types;

    for (auto& t : getTemplatesInCategory (category))
        types.add (t.displayName);

    return types;
}

static std::unique_ptr<Component> createProjectTemplatesTab (ContentComponent& content,
                                                             std::function<void (std::unique_ptr<Project>&&)>&& cb)
{
    auto categories = getAllTemplateCategoryStrings();

    std::vector<StringArray> templateNames;

    for (auto& c : categories)
        templateNames.push_back (getAllTemplateNamesForCategory (c));

    auto selectedCallback = [&, cb] (int category, int index)
    {
        auto categoryString = getAllTemplateCategoryStrings()[category];
        auto templates = getTemplatesInCategory (categoryString);

        content.setContent (std::make_unique<TemplateComponent> (templates[(size_t) index], std::move (cb)));
    };

    auto holder = std::make_unique<StartPageTreeHolder> ("Templates",
                                                         categories,
                                                         templateNames,
                                                         std::move (selectedCallback),
                                                         StartPageTreeHolder::Open::yes);
    holder->setSelectedItem (categories[0], 1);

    JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wredundant-move")
    return std::move (holder);
    JUCE_END_IGNORE_WARNINGS_GCC_LIKE
}

//==============================================================================
struct ProjectTemplatesAndExamples  : public TabbedComponent
{
    ProjectTemplatesAndExamples (ContentComponent& c,
                                 std::function<void (std::unique_ptr<Project>&&)>&& newProjectCb,
                                 std::function<void (const File&)>&& exampleCb)
        : TabbedComponent (TabbedButtonBar::Orientation::TabsAtTop),
          content (c),
          exampleSelectedCallback (std::move (exampleCb))
    {
        setTitle ("Templates and Examples");
        setFocusContainerType (FocusContainerType::focusContainer);

        addTab ("New Project",
                Colours::transparentBlack,
                createProjectTemplatesTab (content, std::move (newProjectCb)).release(),
                true);

        refreshExamplesTab();
    }

    void refreshExamplesTab()
    {
        auto wasOpen = (getCurrentTabIndex() == 1);

        removeTab (1);

        auto exampleTabs = createExampleProjectsTab (content, exampleSelectedCallback);

        addTab ("Open Example",
                Colours::transparentBlack,
                exampleTabs == nullptr ? new SetJUCEPathComponent (*this) : exampleTabs.release(),
                true);

        if (wasOpen)
            setCurrentTabIndex (1);
    }

private:
    //==============================================================================
    struct SetJUCEPathComponent    : public Component,
                                     private ChangeListener
    {
        explicit SetJUCEPathComponent (ProjectTemplatesAndExamples& o)
            : owner (o)
        {
            getGlobalProperties().addChangeListener (this);

            setPathButton.setButtonText ("Set path to JUCE...");
            setPathButton.onClick = [] { ProjucerApplication::getApp().showPathsWindow (true); };

            addAndMakeVisible (setPathButton);
        }

        ~SetJUCEPathComponent() override
        {
            getGlobalProperties().removeChangeListener (this);
        }

        void paint (Graphics& g) override
        {
            g.fillAll (findColour (secondaryBackgroundColourId));
        }

        void resized() override
        {
            auto bounds = getLocalBounds().reduced (5);
            bounds.removeFromTop (25);

            setPathButton.setBounds (bounds.removeFromTop (25));
        }

    private:
        void changeListenerCallback (ChangeBroadcaster*) override
        {
            if (isValidJUCEExamplesDirectory (ProjucerApplication::getJUCEExamplesDirectoryPathFromGlobal()))
                owner.refreshExamplesTab();
        }

        ProjectTemplatesAndExamples& owner;
        TextButton setPathButton;
    };

    ContentComponent& content;
    std::function<void (const File&)> exampleSelectedCallback;
};

//==============================================================================
StartPageComponent::StartPageComponent (std::function<void (std::unique_ptr<Project>&&)>&& newProjectCb,
                                        std::function<void (const File&)>&& exampleCb)
    : content (std::make_unique<ContentComponent>()),
      tabs (std::make_unique<ProjectTemplatesAndExamples> (*content, std::move (newProjectCb), std::move (exampleCb)))
{
    tabs->setOutline (0);
    addAndMakeVisible (*tabs);

    addAndMakeVisible (openExistingButton);
    openExistingButton.setCommandToTrigger (&ProjucerApplication::getCommandManager(), CommandIDs::open, true);

    addAndMakeVisible (*content);

    setSize (900, 600);
}

void StartPageComponent::paint (Graphics& g)
{
    g.fillAll (findColour (backgroundColourId));
}

void StartPageComponent::resized()
{
    auto bounds = getLocalBounds().reduced (10);

    auto tabBounds = bounds.removeFromLeft (bounds.getWidth() / 3);

    openExistingButton.setBounds (tabBounds.removeFromBottom (30).reduced (10, 0));
    tabBounds.removeFromBottom (5);

    tabs->setBounds (tabBounds);
    bounds.removeFromLeft (10);

    content->setBounds (bounds);
}
