/*
 * Copyright 2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.gradle.gradleplugin.userinterface.swing.generic;

import org.gradle.gradleplugin.foundation.GradlePluginLord;
import org.gradle.gradleplugin.foundation.settings.SettingsNode;
import org.gradle.gradleplugin.userinterface.AlternateUIInteraction;
import org.gradle.gradleplugin.userinterface.swing.generic.tabs.CommandLineTab;
import org.gradle.gradleplugin.userinterface.swing.generic.tabs.FavoriteTasksTab;
import org.gradle.gradleplugin.userinterface.swing.generic.tabs.GradleTab;
import org.gradle.gradleplugin.userinterface.swing.generic.tabs.SetupTab;
import org.gradle.gradleplugin.userinterface.swing.generic.tabs.TaskTreeTab;

import javax.swing.*;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;
import java.awt.BorderLayout;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 This is a tabbed pane meant to handle several tabs of gradle-related things.
 To use this, instantiate it, place it some Swing container (dialog, frame),
 then call aboutToShow() before you show the parent container. You can also add
 your own tabs to this (just call addGradleTab before calling aboutToShow()).
 When you shut down, call aboutToClose() before doing so.

 @author mhunsicker
  */
public class MainGradlePanel extends JPanel {
    private static final String CURRENT_TAB = "current-tab";
    private static final String MAIN_PANEL = "main_panel";

    private GradlePluginLord gradlePluginLord;

    private SettingsNode settings;
    private AlternateUIInteraction alternateUIInteraction;

    private List<GradleTab> gradleTabs = new ArrayList<GradleTab>();

    private JTabbedPane tabbedPane;

    public MainGradlePanel(GradlePluginLord gradlePluginLord, SwingGradleExecutionWrapper swingGradleWrapper, SettingsNode settings, AlternateUIInteraction alternateUIInteraction) {
        this.alternateUIInteraction = alternateUIInteraction;
        this.gradlePluginLord = gradlePluginLord;
        this.settings = settings;
        addDefaultTabs(swingGradleWrapper, alternateUIInteraction);
    }

    private void addDefaultTabs(SwingGradleExecutionWrapper swingGradleWrapper, AlternateUIInteraction alternateUIInteraction) {
        //we'll give each tab their own settings node just so we don't have to worry about collisions.
        gradleTabs.add(new TaskTreeTab(gradlePluginLord, swingGradleWrapper, settings.addChildIfNotPresent("task-tab"), alternateUIInteraction));
        gradleTabs.add(new FavoriteTasksTab(gradlePluginLord, swingGradleWrapper, settings.addChildIfNotPresent("favorites-tab")));
        gradleTabs.add(new CommandLineTab(gradlePluginLord, swingGradleWrapper, settings.addChildIfNotPresent("command_line-tab")));
        gradleTabs.add(new SetupTab(gradlePluginLord, swingGradleWrapper, settings.addChildIfNotPresent("setup-tab")));
    }

    private int getGradleTabIndex(Class soughtClass) {
        for (int index = 0; index < gradleTabs.size(); index++) {
            GradleTab gradleTab = gradleTabs.get(index);
            if (gradleTab.getClass() == soughtClass)
                return index;
        }
        return -1;
    }

    private int getGradleTabIndex(String name) {
        if (name != null) {
            for (int index = 0; index < gradleTabs.size(); index++) {
                GradleTab gradleTab = gradleTabs.get(index);
                if (name.equals(gradleTab.getName()))
                    return index;
            }
        }
        return -1;
    }

    /**
       Call this to add one of your own tabs to this. You must call this before
       you call aboutToShow.
    */
    public void addGradleTab(int index, GradleTab gradleTab) {
        //this can ultimately be called via external APIs so let's add a little extra error checking.
        if (index < 0)
            index = 0;
        if (index > gradleTabs.size())
            index = gradleTabs.size();

        gradleTabs.add(index, gradleTab);

        if (tabbedPane != null)   //if we've already displayed the tabs, we'll need to manually add it now to the tabbed pane.
            addGradleTabToTabbedPane(index, gradleTab);
    }

    //this adds the tab. This is only to be used when adding a tab after the tabbed
    //pane has already been displayed and populated with tabs.
    private void addGradleTabToTabbedPane(int index, GradleTab gradleTab) {
        tabbedPane.add(gradleTab.createComponent(), index);
        tabbedPane.setTitleAt(index, gradleTab.getName());
    }

    public void removeGradleTab(GradleTab gradleTab) {
        int existingIndex = gradleTabs.indexOf(gradleTab);
        if (existingIndex == -1)
            return;

        gradleTabs.remove(gradleTab);

        tabbedPane.remove(existingIndex);

        tabbedPane.invalidate();
        tabbedPane.revalidate();
        tabbedPane.repaint();
    }

    /**
       @return the total number of tabs.
    */
    public int getGradleTabCount() {
        return gradleTabs.size();
    }

    /**
       @param  index      the index of the tab
       @return the name of the tab at the specified index.
    */
    public String getGradleTabName(int index) {
        return gradleTabs.get(index).getName();
    }

    /**
       This is called when this about to displayed. Do any kind of initialization
       you need to do here.
    */
    public void aboutToShow() {
        setupUI();

        Iterator<GradleTab> iterator = gradleTabs.iterator();
        while (iterator.hasNext()) {
            GradleTab gradleTab = iterator.next();
            gradleTab.aboutToShow();
        }

        //now start up the plugin now that everything has been initialized
        gradlePluginLord.startExecutionQueue();
    }

    /**
       Notification that we're about to be closed. Here we're going to save our
       current settings.
    */
    public void aboutToClose() {
    }

    private void setupUI() {
        setLayout(new BorderLayout());

        tabbedPane = new JTabbedPane();
        add(tabbedPane, BorderLayout.CENTER);

        addTabs();

        restoreLastTab();

        //add a listener so we can store the current tab when it changes.
        tabbedPane.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                int selection = tabbedPane.getSelectedIndex();
                if (selection >= 0 && selection < gradleTabs.size()) {
                    SettingsNode rootNode = settings.addChildIfNotPresent(MAIN_PANEL);
                    rootNode.setValueOfChild(CURRENT_TAB, gradleTabs.get(selection).getName());
                }
            }
        });
    }

    private void restoreLastTab() {
        //if they're not setup, make the setup tab visible first.
        if (!gradlePluginLord.isSetupComplete()) {
            int tabToSelect = getGradleTabIndex(SetupTab.class);
            if (tabToSelect != -1)
                tabbedPane.setSelectedIndex(tabToSelect);
        } else {  //otherwise, try to get the last-used tab
            int lastTabIndex = -1;

            //all this is to just restore the last selected tab
            SettingsNode rootNode = settings.getChildNode(MAIN_PANEL);
            if (rootNode != null) {
                String lastTabName = rootNode.getValueOfChild(CURRENT_TAB, "");
                lastTabIndex = getGradleTabIndex(lastTabName);
            }

            if (lastTabIndex != -1)
                tabbedPane.setSelectedIndex(lastTabIndex);
        }
    }

    private void addTabs() {
        Iterator<GradleTab> iterator = gradleTabs.iterator();
        while (iterator.hasNext()) {
            GradleTab gradleTab = iterator.next();
            tabbedPane.add(gradleTab.getName(), gradleTab.createComponent());
        }
    }
}
