package CH.ifa.draw.standard;

import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.swing.ButtonGroup;
import javax.swing.JMenuItem;
import javax.swing.JRadioButtonMenuItem;

import org.apache.log4j.Logger;

import bibliothek.gui.dock.common.event.CFocusListener;
import bibliothek.gui.dock.common.intern.CDockable;

import CH.ifa.draw.application.SavedListener;
import de.renew.draw.storables.ontology.Drawing;
import de.renew.draw.storables.ontology.DrawingListener;

/**
 * Handles the topLevelMenu "Windows" and adds/removes entries when a drawing is added/removed.
 * When a drawing is focused the related Windows Menu Entry is selected. When an entry of the Window
 * Menu Entry is selected, the related drawing is being focused.
 */
public class WindowsMenuCreator
    implements DrawingListener, ItemListener, CFocusListener, SavedListener
{
    /**
     * Used for logging errors and warnings to the console.
     */
    private static final Logger LOGGER = Logger.getLogger(WindowsMenuCreator.class);

    /**
     * All entries are added together to a group so only one entry is selected at the all times.
     */
    private final ButtonGroup _buttonGroup = new ButtonGroup();

    /**
     * Wrapper Class to keep track of which Drawing is related to which JRadioButton.
     */
    private final List<DrawingButtonBinding> _drawingButtonBindings = new ArrayList<>();


    /**
     * Constructor for this class.
     */
    public WindowsMenuCreator() {

    }

    /**
     * Creates the WindowsMenu.
     * @return the MenuItems that will be added to the Menu
     */
    public Collection<JMenuItem> createMenus() {
        try {
            WindowsMenuManager.addDrawingListener(this);
            return _drawingButtonBindings.stream().map(DrawingButtonBinding::menuItem)
                .collect(Collectors.toList());
        } catch (Exception e) {
            LOGGER.error(e);
            throw new RuntimeException(e);
        }
    }

    /**
     * When a drawing is closed, the related menu entry is removed from the WindowsMenu.
     * @param drawing the released/closed drawing
     */
    @Override
    public void drawingReleased(de.renew.draw.storables.ontology.Drawing drawing) {
        JRadioButtonMenuItem itemToBeRemoved =
            _drawingButtonBindings.stream().filter(e -> e.drawing().equals(drawing))
                .map(DrawingButtonBinding::menuItem).findFirst().orElse(null);
        _buttonGroup.remove(itemToBeRemoved);
        WindowsMenuManager.removeMenuItem(itemToBeRemoved);
    }

    /**
     * When a drawing is added, a menu entry is created and added to the WindowsMenu.
     * @param drawing the added drawing
     */
    @Override
    public void drawingAdded(de.renew.draw.storables.ontology.Drawing drawing) {
        JRadioButtonMenuItem radioButtonItem = new JRadioButtonMenuItem(drawing.getName());
        UUID uniqueId = UUID.randomUUID();
        radioButtonItem.putClientProperty("ch.ifa.draw.menu.id", uniqueId.toString());
        radioButtonItem.setSelected(true);
        _buttonGroup.add(radioButtonItem);

        _drawingButtonBindings.add(new DrawingButtonBinding(drawing, radioButtonItem));

        radioButtonItem.addItemListener(this);
        WindowsMenuManager.addFocusListenerToDrawing(this);
        WindowsMenuManager.addMenuItem(radioButtonItem);
        WindowsMenuManager.addSavedListener(this);
    }

    /**
     * When an entry of the WindowsMenu is selected, the related drawing is focused.
     * @param e the event to be processed
     */
    @Override
    public void itemStateChanged(ItemEvent e) {
        JRadioButtonMenuItem selectedItem = (JRadioButtonMenuItem) e.getSource();

        de.renew.draw.storables.ontology.Drawing drawingToSelect =
            _drawingButtonBindings.stream().filter(s -> s.menuItem() == selectedItem)
                .map(DrawingButtonBinding::drawing).findFirst().orElse(null);
        WindowsMenuManager.requestFocusForDrawing(drawingToSelect);
    }

    /**
     * When a container of a drawing is being focused, the related menu item is selected.
     * @param cDockable a dockable component of the application
     */
    @Override
    public void focusGained(CDockable cDockable) {
        if (cDockable instanceof StandardDrawingViewContainer) {
            Drawing focusedDrawing = WindowsMenuManager.askForCurrentlyFocusedDrawing();
            for (DrawingButtonBinding binding : _drawingButtonBindings) {
                if (binding.drawing().equals(focusedDrawing) && !binding.menuItem().isSelected()) {
                    binding.menuItem().setSelected(true);
                }
            }

        }
    }


    @Override
    public void focusLost(CDockable cDockable) {
        //not needed as ButtonGroup deselects items automatically when a different JRadioButton is selected
    }

    /**
     * When a drawing is saved its name changes and the menu entry needs to be changed to match that name.
     * @param drawing that was saved
     */
    @Override
    public void drawingWasSaved(de.renew.draw.storables.ontology.Drawing drawing) {
        JRadioButtonMenuItem item =
            _drawingButtonBindings.stream().filter(e -> e.drawing().equals(drawing))
                .map(DrawingButtonBinding::menuItem).findFirst().orElse(null);

        if (item != null) {
            item.setSelected(true);
            item.setText(drawing.getName());

        }
    }
}
