package de.renew.navigator.vc;

import java.io.File;
import java.util.HashSet;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JTree;
import javax.swing.tree.MutableTreeNode;

import de.renew.logging.CliColor;
import de.renew.navigator.FilesystemController;
import de.renew.navigator.NavigatorConfigurator;
import de.renew.navigator.NavigatorExtension;
import de.renew.navigator.NavigatorGui;
import de.renew.navigator.gui.FileTreeNode;
import de.renew.navigator.models.NavigatorFileTree;
import de.renew.navigator.models.TreeElement;
import de.renew.navigator.vc.impl.VersionControlAggregatorImpl;
import de.renew.plugin.annotations.Inject;
import de.renew.plugin.annotations.Provides;
import de.renew.plugin.di.DIPlugin;

import static de.renew.navigator.vc.Commit.formatMessage;


/**
 * @author Konstantin Simon Maria Moellers
 * @version 2015-10-13
 */
public final class NavigatorVCPlugin extends DIPlugin implements NavigatorExtension {
    private final VersionControlAggregator _aggregator;
    private final NavigatorGui _gui;
    private final RepositoryTreeCellRenderer _renderer;

    /**
     * Constructor to create an instance of the NavigatorVCPlugin.
     *
     * @param gui the NavigatorGui of the plugin.
     * @param model the NavigatorFileTree of the plugin.
     */
    @Inject
    public NavigatorVCPlugin(NavigatorGui gui, final NavigatorFileTree model) {
        this._gui = gui;
        _aggregator = new VersionControlAggregatorImpl();

        final HashSet<Repository> repositories = new HashSet<>();
        _renderer = new RepositoryTreeCellRenderer(repositories);

        model.addObserver((o, arg) -> {
            for (TreeElement root : model.getTreeRoots()) {
                final Repository repository = _aggregator.findRepository(root.getFile());

                if (null != repository) {
                    repositories.add(repository);
                }
            }
            if (arg instanceof FilesystemController) {
                repositories.forEach(Repository::update);
            }
        });

        gui.addExtension(this);
    }

    @Override
    public boolean cleanup() {
        return _gui.removeExtension(this);
    }

    @Provides
    public VersionControlAggregator getVersionControlAggregator() {
        return _aggregator;
    }

    @Override
    public void configure(NavigatorConfigurator config) {
        config.addFileTreeCellRenderer(_renderer);
    }

    @Override
    public JMenuItem getMenuItem(
        JTree tree, int x, int y, Object lastPathComponent, MutableTreeNode mtn)
    {
        if (!(mtn instanceof FileTreeNode)) {
            return null;
        }
        final File file = ((FileTreeNode) mtn).getFile();

        if (file.isDirectory()) {
            return null;
        }

        final Repository repository = _aggregator.findRepository(file);
        if (null == repository) {
            return null;
        }

        // Build the menu.
        return buildVersionControlMenu(file, repository);
    }

    /**
     * Builds the Version Control menu.
     *
     * @param file the File to build the version control menu.
     * @param repository the Repository to build the version control menu.
     * @return menu the built version control menu as a JMenuItem.
     */
    private JMenuItem buildVersionControlMenu(final File file, final Repository repository) {
        JMenu menu = new JMenu(repository.getVersionControl().getName());

        JMenuItem diffItem = new JMenuItem("Diff with HEAD/BASE");
        diffItem.addActionListener(e -> _aggregator.diff(file));
        menu.add(diffItem);

        JMenuItem logItem = new JMenuItem("Log");
        logItem.addActionListener(e -> _aggregator.log(file));
        menu.add(logItem);

        JMenuItem infoItem = new JMenuItem("Info");
        infoItem.addActionListener(e -> System.out.println(buildRepositoryInfo(repository)));
        menu.add(infoItem);

        return menu;
    }

    /**
     * Builds a CLI info for a repository.
     *
     * @param repository the Repository that includes the info.
     * @return the repository info as a formatted String.
     */
    public String buildRepositoryInfo(Repository repository) {
        return colorStringBold(repository.getVersionControl().getName())
            + colorStringYellow("Root Directory: ", repository.getRootDirectory().getAbsolutePath())
            + colorStringYellow("Remote URL:     ", repository.getRemoteURL())
            + colorStringYellow("Branch:         ", repository.getBranch())
            + colorStringYellow("Revision:       ", repository.getLastCommit().getRevision())
            + colorStringYellow("Author:         ", repository.getLastCommit().getAuthor())
            + colorStringYellow(
                "Message:        ", formatMessage(repository.getLastCommit().getMessage()));
    }

    private String colorStringBold(String stringToBeColored) {
        return CliColor.color(stringToBeColored, CliColor.BOLD) + '\n';
    }

    private String colorStringYellow(String stringToBeColored, String string2) {
        return CliColor.color(stringToBeColored, CliColor.YELLOW) + string2 + '\n';
    }
}