/*
 *  Copyright (C) 2010-2015 JPEXS
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.jpexs.decompiler.flash.gui;

import com.jpexs.decompiler.flash.configuration.Configuration;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.SwingUtilities;
import org.pushingpixels.flamingo.api.common.AbstractCommandButton;
import org.pushingpixels.flamingo.api.common.CommandButtonDisplayState;
import org.pushingpixels.flamingo.api.common.CommandToggleButtonGroup;
import org.pushingpixels.flamingo.api.common.JCommandButton;
import org.pushingpixels.flamingo.api.common.JCommandButtonPanel;
import org.pushingpixels.flamingo.api.common.JCommandToggleButton;
import org.pushingpixels.flamingo.api.common.popup.JPopupPanel;
import org.pushingpixels.flamingo.api.common.popup.PopupPanelCallback;
import org.pushingpixels.flamingo.api.ribbon.AbstractRibbonBand;
import org.pushingpixels.flamingo.api.ribbon.JRibbon;
import org.pushingpixels.flamingo.api.ribbon.JRibbonBand;
import org.pushingpixels.flamingo.api.ribbon.JRibbonComponent;
import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenu;
import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenuEntryFooter;
import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenuEntryPrimary;
import org.pushingpixels.flamingo.api.ribbon.RibbonElementPriority;
import org.pushingpixels.flamingo.api.ribbon.RibbonTask;
import org.pushingpixels.flamingo.api.ribbon.resize.BaseRibbonBandResizePolicy;
import org.pushingpixels.flamingo.api.ribbon.resize.CoreRibbonResizePolicies;
import org.pushingpixels.flamingo.api.ribbon.resize.IconRibbonBandResizePolicy;
import org.pushingpixels.flamingo.api.ribbon.resize.RibbonBandResizePolicy;
import org.pushingpixels.flamingo.internal.ui.ribbon.AbstractBandControlPanel;

/**
 *
 * @author JPEXS
 */
public class MainFrameRibbonMenu extends MainFrameMenu {

    private final JRibbon ribbon;

    private final Map<String, Object> menuItems = new HashMap<>();

    private final Map<String, String> menuTitles = new HashMap<>();

    private final Map<String, String> menuIcons = new HashMap<>();

    private final Map<String, ActionListener> menuActions = new HashMap<>();

    private final Map<String, ActionListener> menuLoaders = new HashMap<>();

    private final Map<String, Integer> menuPriorities = new HashMap<>();

    private final Map<String, List<String>> menuSubs = new HashMap<>();

    private final Map<String, Integer> menuType = new HashMap<>();

    private final Map<String, String> menuGroup = new HashMap<>();

    private final Map<String, CommandToggleButtonGroup> menuToggleGroups = new HashMap<>();

    private final Map<String, CommandToggleButtonGroup> menuToToggleGroup = new HashMap<>();

    private static final int TYPE_MENU = 1;

    private static final int TYPE_MENUITEM = 2;

    private static final int TYPE_TOGGLEMENUITEM = 3;

    public MainFrameRibbonMenu(MainFrameRibbon mainFrame, JRibbon ribbon, boolean externalFlashPlayerUnavailable) {
        super(mainFrame, externalFlashPlayerUnavailable);
        this.ribbon = ribbon;
    }

    private String fixCommandTitle(String title) {
        if (title.length() > 2) {
            if (title.charAt(1) == ' ') {
                title = title.charAt(0) + "\u00A0" + title.substring(2);
            }
        }
        return title;
    }

    private RibbonBandResizePolicy titleResizePolicies(final JRibbonBand ribbonBand) {
        return new BaseRibbonBandResizePolicy<AbstractBandControlPanel>(ribbonBand.getControlPanel()) {
            @Override
            public int getPreferredWidth(int i, int i1) {
                return ribbonBand.getGraphics().getFontMetrics(ribbonBand.getFont()).stringWidth(ribbonBand.getTitle()) + 20;
            }

            @Override
            public void install(int i, int i1) {
            }
        };
    }

    private List<RibbonBandResizePolicy> getResizePolicies(JRibbonBand ribbonBand) {
        final List<RibbonBandResizePolicy> myResizePolicies = new ArrayList<>();
        myResizePolicies.add(new CoreRibbonResizePolicies.Mirror(ribbonBand.getControlPanel()));
        myResizePolicies.add(titleResizePolicies(ribbonBand));
        myResizePolicies.add(new IconRibbonBandResizePolicy(ribbonBand.getControlPanel()));

        List<RibbonBandResizePolicy> resizePolicies = new ArrayList<>();

        resizePolicies.add(new RibbonBandResizePolicy() {

            @Override
            public int getPreferredWidth(int i, int i1) {
                int pw = 0;
                for (RibbonBandResizePolicy p : myResizePolicies) {
                    int npw = p.getPreferredWidth(i, i1);
                    if (npw > pw) {
                        pw = npw;
                    }
                }
                return pw;
            }

            @Override
            public void install(int i, int i1) {
                for (RibbonBandResizePolicy p : myResizePolicies) {
                    p.install(i, i1);
                }
            }
        });
        return resizePolicies;
    }

    @Override
    protected void loadRecent(ActionEvent evt) {
        if (evt.getSource() instanceof JPanel) {
            JPanel targetPanel = (JPanel) evt.getSource();
            targetPanel.removeAll();
            JCommandButtonPanel openHistoryPanel = new JCommandButtonPanel(CommandButtonDisplayState.MEDIUM);
            String groupName = translate("menu.recentFiles");
            openHistoryPanel.addButtonGroup(groupName);
            List<String> recentFiles = Configuration.getRecentFiles();
            int j = 0;
            for (int i = recentFiles.size() - 1; i >= 0; i--) {
                String path = recentFiles.get(i);
                RecentFilesButton historyButton = new RecentFilesButton(j + "    " + path, null);
                historyButton.fileName = path;
                historyButton.addActionListener((ActionEvent ae) -> {
                    RecentFilesButton source = (RecentFilesButton) ae.getSource();
                    if (Main.openFile(source.fileName, null) == OpenFileResult.NOT_FOUND) {
                        if (View.showConfirmDialog(null, translate("message.confirm.recentFileNotFound"), translate("message.confirm"), JOptionPane.YES_NO_OPTION) == JOptionPane.YES_NO_OPTION) {
                            Configuration.removeRecentFile(source.fileName);
                        }
                    }
                });
                j++;
                historyButton.setHorizontalAlignment(SwingUtilities.LEFT);
                openHistoryPanel.addButtonToLastGroup(historyButton);
            }
            openHistoryPanel.setMaxButtonColumns(1);
            targetPanel.setLayout(new BorderLayout());
            targetPanel.add(openHistoryPanel, BorderLayout.CENTER);
        }
    }

    @Override
    public void finishMenu(String path) {
        if (!menuTitles.containsKey(path)) {
            throw new IllegalArgumentException("Menu not started: " + path);
        }
        boolean isAppMenu = path.equals("_");
        String title = menuTitles.get(path);
        String icon = menuIcons.get(path);
        ActionListener action = menuActions.get(path);
        int priority = menuPriorities.get(path);
        int type = menuType.get(path);
        if (type != TYPE_MENU) {
            throw new IllegalArgumentException("Not a menu: " + path);
        }

        String parts[] = path.contains("/") ? path.split("\\/") : new String[]{""};
        List<String> subs = menuSubs.get(path);

        boolean onlyCheckboxes = true;
        for (String sub : subs) {
            if (sub.equals("-")) {
                continue;
            }
            int subType = menuType.get(sub);
            if (subType == TYPE_MENUITEM) {
                onlyCheckboxes = false;
                break;
            }
            String subIcon = menuIcons.get(sub);
            if (subIcon != null) {
                onlyCheckboxes = false;
                break;
            }
        }
        if (subs.isEmpty()) {
            onlyCheckboxes = false;
        }

        if (isAppMenu) {
            RibbonApplicationMenu mainMenu = new RibbonApplicationMenu();
            for (String sub : subs) {
                if (sub.equals("-")) {
                    mainMenu.addMenuSeparator();
                    continue;
                }

                String subTitle = menuTitles.get(sub);
                String subIcon = menuIcons.get(sub);
                int subType = menuType.get(sub);
                ActionListener subAction = menuActions.get(sub);
                final ActionListener subLoader = menuLoaders.get(sub);

                if (sub.startsWith("_/$")) //FooterMenu
                {
                    RibbonApplicationMenuEntryFooter footerMenu = new RibbonApplicationMenuEntryFooter(View.getResizableIcon(subIcon, 16), subTitle, subAction);
                    menuItems.put(sub, footerMenu);
                    mainMenu.addFooterEntry(footerMenu);
                } else {
                    RibbonApplicationMenuEntryPrimary menu = new RibbonApplicationMenuEntryPrimary(View.getResizableIcon(subIcon, 32), subTitle, subAction,
                            subType == TYPE_MENU ? JCommandButton.CommandButtonKind.ACTION_AND_POPUP_MAIN_ACTION : JCommandButton.CommandButtonKind.ACTION_ONLY);

                    if (subLoader != null) {
                        menu.setRolloverCallback(new RibbonApplicationMenuEntryPrimary.PrimaryRolloverCallback() {
                            @Override
                            public void menuEntryActivated(JPanel targetPanel) {
                                subLoader.actionPerformed(new ActionEvent(targetPanel, 0, "load:" + sub));
                            }
                        });
                    }
                    menuItems.put(sub, menu);
                    mainMenu.addMenuEntry(menu);
                }

            }

            ribbon.setApplicationMenu(mainMenu);
            return;
        }

        for (String sub : subs) {
            if (sub.equals("-")) {
                continue;
            }
            int subType = menuType.get(sub);
            ActionListener subAction = menuActions.get(sub);
            String subTitle = menuTitles.get(sub);
            String subIcon = menuIcons.get(sub);
            String subGroup = menuGroup.get(sub);
            int subPriority = menuPriorities.get(sub);
            final ActionListener subLoader = menuLoaders.get(sub);
            AbstractCommandButton but = null;
            if (subType == TYPE_MENUITEM || (subType == TYPE_MENU && subAction != null)) {
                JCommandButton cbut = null;
                if (subIcon != null) {
                    cbut = new JCommandButton(fixCommandTitle(subTitle), View.getResizableIcon(subIcon, subPriority == PRIORITY_TOP ? 32 : 16));
                } else {
                    cbut = new JCommandButton(fixCommandTitle(subTitle));
                }
                if (subLoader != null) {
                    cbut.setCommandButtonKind(JCommandButton.CommandButtonKind.ACTION_AND_POPUP_MAIN_ACTION);
                    cbut.setPopupCallback(new PopupPanelCallback() {

                        @Override
                        public JPopupPanel getPopupPanel(JCommandButton jcb) {
                            JPopupPanel jp = new JPopupPanel() {
                            };

                            subLoader.actionPerformed(new ActionEvent(jp, 0, "load:" + sub));
                            return jp;
                        }
                    });
                }
                but = cbut;
            } else if (subType == TYPE_TOGGLEMENUITEM) {
                if (onlyCheckboxes) {
                    JCheckBox cb = new JCheckBox(subTitle);
                    if (subAction != null) {
                        cb.addActionListener(subAction);
                    }
                    menuItems.put(sub, cb);
                } else {
                    if (subIcon != null) {
                        but = new JCommandToggleButton(fixCommandTitle(subTitle), View.getResizableIcon(subIcon, subPriority == PRIORITY_TOP ? 32 : 16));
                    } else {
                        but = new JCommandToggleButton(fixCommandTitle(subTitle));
                    }
                    menuToToggleGroup.get(sub).add((JCommandToggleButton) but);
                }
            }
            if (but != null) {
                if (subAction != null) {
                    but.addActionListener(subAction);
                }
                menuItems.put(sub, but);
            }
        }

        //if (parts.length == 3)
        { //3rd level - it's a Band!
            JRibbonBand band = new JRibbonBand(title, icon != null ? View.getResizableIcon(icon, 16) : null, null);
            band.setResizePolicies(getResizePolicies(band));
            int cnt = 0;
            for (String sub : subs) {
                if (sub.equals("-")) {
                    continue;
                }

                Object o = menuItems.get(sub);
                int subPriority = menuPriorities.get(sub);
                int subType = menuType.get(sub);
                ActionListener subAction = menuActions.get(sub);
                if (subType != TYPE_MENU || (subAction != null)) {
                    if (o instanceof AbstractCommandButton) {
                        RibbonElementPriority ribbonPriority = RibbonElementPriority.MEDIUM;
                        switch (subPriority) {
                            case PRIORITY_LOW:
                                ribbonPriority = RibbonElementPriority.LOW;
                                break;
                            case PRIORITY_MEDIUM:
                                ribbonPriority = RibbonElementPriority.MEDIUM;
                                break;
                            case PRIORITY_TOP:
                                ribbonPriority = RibbonElementPriority.TOP;
                                break;
                        }

                        band.addCommandButton((AbstractCommandButton) o, ribbonPriority);
                        cnt++;
                    } else if (o instanceof JComponent) {
                        band.addRibbonComponent(new JRibbonComponent((JComponent) o));
                        cnt++;
                    }
                }
            }
            if (cnt > 0) {
                if (parts.length != 3) {
                    if (!menuSubs.containsKey(path)) {
                        menuSubs.put(path, new ArrayList<>());
                    }
                    if (!menuSubs.get(path).contains(path + "/_")) {
                        menuSubs.get(path).add(0, path + "/_");
                    }
                    menuItems.put(path + "/_", band);

                } else {
                    menuItems.put(path, band);
                }

            }

        }

        if (parts.length == 1) { //1st level - it's ribbon
            for (String sub : subs) {
                if (sub.equals("-")) {
                    continue;
                }
                if (menuItems.get(sub) instanceof RibbonTask) {
                    ribbon.addTask((RibbonTask) menuItems.get(sub));
                }
            }
        } else if (parts.length == 2) { //2nd level - it's a Task!
            int bandCount = 0;
            for (String sub : subs) {
                if (sub.equals("-")) {
                    continue;
                }

                if (menuItems.get(sub) instanceof AbstractRibbonBand) {
                    bandCount++;
                }
            }
            AbstractRibbonBand bands[] = new AbstractRibbonBand[bandCount];
            int b = 0;
            for (String sub : subs) {
                if (sub.equals("-")) {
                    continue;
                }
                if (menuItems.get(sub) instanceof AbstractRibbonBand) {
                    bands[b++] = (AbstractRibbonBand) menuItems.get(sub);
                }
            }
            if (bands.length > 0) {
                RibbonTask task = new RibbonTask(title, bands);
                menuItems.put(path, task);
            }
        }
    }

    @Override
    public void addMenuItem(String path, String title, String icon, ActionListener action, int priority, ActionListener subLoader, boolean isLeaf) {
        String parentPath = path.contains("/") ? path.substring(0, path.lastIndexOf('/')) : "";
        if (!menuSubs.containsKey(parentPath)) {
            throw new IllegalArgumentException("No parent menu exists: " + parentPath);
        }
        menuSubs.get(parentPath).add(path);
        if (!isLeaf) {
            menuSubs.put(path, new ArrayList<>());
        }
        menuLoaders.put(path, subLoader);
        menuTitles.put(path, title);
        menuIcons.put(path, icon);
        menuActions.put(path, action);
        menuPriorities.put(path, priority);
        menuType.put(path, isLeaf ? TYPE_MENUITEM : TYPE_MENU);
    }

    @Override
    public void addToggleMenuItem(String path, String title, String group, String icon, ActionListener action, int priority) {
        addMenuItem(path, title, icon, action, priority, action, true);
        menuType.put(path, TYPE_TOGGLEMENUITEM);
        menuGroup.put(path, group);
        if (group == null) {
            group = path;
        }
        if (!menuToggleGroups.containsKey(group)) {
            menuToggleGroups.put(group, new CommandToggleButtonGroup());
        }
        menuToToggleGroup.put(path, menuToggleGroups.get(group));
    }

    @Override
    public boolean isMenuChecked(String path) {
        Object o = menuItems.get(path);
        if (o instanceof JCommandToggleButton) {
            JCommandToggleButton t = (JCommandToggleButton) o;
            if (!menuToToggleGroup.containsKey(path)) {
                throw new IllegalArgumentException("No toggle group for " + path);
            }
            return menuToToggleGroup.get(path).getSelected() == t;
        }
        if (o instanceof JCheckBox) {
            return ((JCheckBox) o).isSelected();
        }
        throw new IllegalArgumentException("Not a toggle menu");
    }

    @Override
    public void setMenuChecked(String path, boolean checked) {
        Object o = menuItems.get(path);
        if (o instanceof JCommandToggleButton) {
            JCommandToggleButton t = (JCommandToggleButton) o;
            if (!menuToToggleGroup.containsKey(path)) {
                throw new IllegalArgumentException("No toggle group for " + path);
            }
            menuToToggleGroup.get(path).setSelected(t, checked);
        } else if (o instanceof JToggleButton) {
            ((JToggleButton) o).setSelected(checked);
        } else {
            throw new IllegalArgumentException("Not a toggle menu");
        }
    }

    @Override
    public void setGroupSelection(String group, String selected) {
        if (!menuToggleGroups.containsKey(group)) {
            throw new IllegalArgumentException("Group " + group + " does not exist");
        }
        menuToggleGroups.get(group).clearSelection();
        if (selected == null) {
            return;
        }
        if (!menuItems.containsKey(selected)) {
            throw new IllegalArgumentException("Selection " + selected + " not found");
        }
        JCommandToggleButton c = (JCommandToggleButton) menuItems.get(selected);
        menuToggleGroups.get(group).setSelected(c, true);
    }

    @Override
    public String getGroupSelection(String group) {
        if (!menuToggleGroups.containsKey(group)) {
            throw new IllegalArgumentException("Group " + group + " does not exist");
        }
        JCommandToggleButton c = menuToggleGroups.get(group).getSelected();
        for (String path : menuItems.keySet()) {
            if (menuItems.get(path) == c) {
                return path;
            }
        }
        return null;
    }

    @Override
    public void clearMenu(String path) {

    }

    @Override
    public void setMenuEnabled(String path, boolean enabled) {
        if (!menuItems.containsKey(path)) {
            throw new IllegalArgumentException("Menu not found: " + path);
        }
        Object o = menuItems.get(path);
        if (o instanceof JRibbonBand) {
            ((JRibbonBand) o).setEnabled(enabled);
        } else if (o instanceof AbstractCommandButton) {
            ((AbstractCommandButton) o).setEnabled(enabled);
        } else if (o instanceof RibbonApplicationMenuEntryPrimary) {
            ((RibbonApplicationMenuEntryPrimary) o).setEnabled(enabled);
        } else if (o instanceof RibbonApplicationMenuEntryFooter) {
            ((RibbonApplicationMenuEntryFooter) o).setEnabled(enabled);
        } else if (o instanceof JComponent) {
            ((JComponent) o).setEnabled(enabled);
        } else {
            throw new IllegalArgumentException("Cannot set enabled to: " + path);
        }
    }

    @Override
    public void initMenu() {
        menuSubs.put("", new ArrayList<>());
        menuPriorities.put("", 0);
        menuActions.put("", null);
        menuTitles.put("", null);
        menuIcons.put("", null);
        menuType.put("", TYPE_MENU);
    }

    @Override
    public void addSeparator(String parentPath) {
        if (!menuSubs.containsKey(parentPath)) {
            throw new IllegalArgumentException("Menu does not exist: " + parentPath);
        }
        menuSubs.get(parentPath).add("-");
    }

    @Override
    public boolean supportsMenuAction() {
        return true;
    }

    @Override
    public boolean supportsAppMenu() {
        return true;
    }
}
