// GraphTea Project: http://github.com/graphtheorysoftware/GraphTea
// Copyright (C) 2012 Graph Theory Software Foundation: http://GraphTheorySoftware.com
// Copyright (C) 2008 Mathematical Science Department of Sharif University of Technology
// Distributed under the terms of the GNU General Public License (GPL): http://www.gnu.org/licenses/

package graphtea.ui.extension;

import graphtea.graph.graph.GraphModel;
import graphtea.platform.core.AEvent;
import graphtea.platform.core.AbstractAction;
import graphtea.platform.core.BlackBoard;
import graphtea.platform.extension.Extension;
import graphtea.platform.lang.CommandAttitude;
import graphtea.platform.parameter.Parameter;
import graphtea.platform.parameter.Parametrizable;
import graphtea.plugins.graphgenerator.core.extension.GraphGeneratorExtension;
import graphtea.plugins.main.extension.GraphActionExtension;
import graphtea.plugins.reports.extension.GraphReportExtension;
import graphtea.ui.ParameterShower;
import graphtea.ui.UIUtils;
import graphtea.ui.components.ExtensionConfigFrame;
import graphtea.ui.components.GFrame;
import graphtea.ui.components.gmenu.GMenuBar;
import graphtea.ui.components.gmenu.GMenuItem;
import graphtea.ui.xml.UIHandlerImpl;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.List;

/**
 * the base class for creating extension handlers
 * the implementing class will have a menu assigned to it automatically the name of the menu will be
 * from the constructors parameter(sp) and the will also listen to UI.getUIEvent(sp.getName())
 *
 * @author azin azadi
 */
public abstract class AbstractExtensionAction<t extends Extension> extends AbstractAction implements ActionListener {
    //todo: Write how to create new extension types document.
    protected JMenu parentMenu;
    public GMenuItem menuItem;
    ExtensionConfigFrame ecf;
    /**
     * a button which will be added to the right of menu item
     */
    protected JButton extraButton;
    public final String actionId;

    public t getTarget() {
        return target;
    }

    public t target;


    static HashMap<String, JMenu> reportSubMenus = new HashMap<>();
    static HashMap<String, JMenu> reportSubSubMenus = new HashMap<>();
    static HashMap<String, JMenu> generateSubMenus = new HashMap<>();
    static HashMap<String, JMenu> operatorsSubmenus = new HashMap<>();

    public static List<JMenu> ourW = new ArrayList<>();

    public AbstractExtensionAction(BlackBoard bb, t sp) {
        super(bb);
        target = sp;
        String name = getMenuNamePrefix() + sp.getName();
        actionId = name + sp.getDescription() + target.getClass().getName();
        listen4Event(UIUtils.getUIEventKey(actionId));
        if (!name.isEmpty()) {
            menuItem = createMenuItem(name, actionId, bb);
            parentMenu = getParentMenu();
            if (parentMenu.getText().equalsIgnoreCase("reports")) {
                GraphReportExtension temp = (GraphReportExtension) sp;
                if (temp.getCategory() != null) {
                    if(!temp.getCategory().contains("-")) {
                        JMenu categoryMenu;
                        if (!reportSubMenus.containsKey(temp.getCategory())) {
                            categoryMenu = new JMenu(temp.getCategory());
                            reportSubMenus.put(temp.getCategory(), categoryMenu);
                        } else
                            categoryMenu = reportSubMenus.get(temp.getCategory());
                        GMenuBar.insert(parentMenu, categoryMenu, getMenuPlace());
                        GMenuBar.insert(categoryMenu, menuItem, getMenuPlace());
                    } else {
                        JMenu categoryMenu;
                        JMenu subsub;

                        String cat = temp.getCategory();
                        String sub = cat.substring(0, cat.indexOf("-"));
                        String subsubStr = cat.substring(cat.indexOf("-")+1);
                        if (!reportSubMenus.containsKey(sub)) {
                            categoryMenu = new JMenu(sub);
                            reportSubMenus.put(sub, categoryMenu);
                        } else
                            categoryMenu = reportSubMenus.get(sub);

                        if(!reportSubSubMenus.containsKey(subsubStr)) {
                            subsub = new JMenu(subsubStr);
                            reportSubSubMenus.put(subsubStr,subsub);
                        } else {
                            subsub=reportSubSubMenus.get(subsubStr);
                        }

                        GMenuBar.insert(parentMenu, categoryMenu, getMenuPlace());
                        GMenuBar.insert(categoryMenu, subsub, getMenuPlace());
                        GMenuBar.insert(subsub, menuItem, getMenuPlace());
//                        if(temp.getCategory().contains("OurWork")) {
//                            ourW.add(categoryMenu);
//                            categoryMenu.setVisible(false);
//                        }
                    }
                } else
                    GMenuBar.insert(parentMenu, menuItem, getMenuPlace());
            } else if (parentMenu.getText().equalsIgnoreCase("generate graph")) {
                GraphGeneratorExtension temp = (GraphGeneratorExtension) sp;
                if (temp.getCategory() != null) {
                    JMenu categoryMenu;
                    if (!generateSubMenus.containsKey(temp.getCategory())) {
                        categoryMenu = new JMenu(temp.getCategory());
                        generateSubMenus.put(temp.getCategory(), categoryMenu);
                    } else
                        categoryMenu = generateSubMenus.get(temp.getCategory());
                    GMenuBar.insert(parentMenu, categoryMenu, getMenuPlace());
                    GMenuBar.insert(categoryMenu, menuItem, getMenuPlace());
                } else
                    GMenuBar.insert(parentMenu, menuItem, getMenuPlace());
            } else if (parentMenu.getText().equalsIgnoreCase("operators")) {
                GraphActionExtension temp = (GraphActionExtension) sp;
                if (temp.getCategory() != null) {
                    JMenu categoryMenu;
                    if (!operatorsSubmenus.containsKey(temp.getCategory())) {
                        categoryMenu = new JMenu(temp.getCategory());
                        operatorsSubmenus.put(temp.getCategory(), categoryMenu);
                    } else
                        categoryMenu = operatorsSubmenus.get(temp.getCategory());
                    GMenuBar.insert(parentMenu, categoryMenu, getMenuPlace());
                    GMenuBar.insert(categoryMenu, menuItem, getMenuPlace());
                } else
                    GMenuBar.insert(parentMenu, menuItem, getMenuPlace());
            } else if (parentMenu != null)
                GMenuBar.insert(parentMenu, menuItem, getMenuPlace());
        }


        createExtensionCommandsForCommandLine();
    }

    /**
     * It is a XML-Based UI concept
     *
     * @return the place which the extension menu will be inserted in its parents menu, the place is
     *         only a comparative value, it means that Menu Items with bigger place will be inserted after smaller place Menue Items
     */
    protected int getMenuPlace() {
        return target.getName().charAt(0) * 1000 + target.getName().charAt(1);
    }

    /**
     * if you want to create custom menues for your extension (i.e. want to add some extra components to it) you should
     * override this method.
     *
     * @param name The name of the menu
     * @param actionId The action id
     * @param bb The blackboard
     * @return  The menu item
     * @see graphtea.plugins.algorithmanimator.extension.AlgorithmExtensionAction
     */
    protected final GMenuItem createMenuItem(String name, String actionId, BlackBoard bb) {
        int index = Math.max(name.indexOf(UIHandlerImpl.menueIndexChar), 0);
        String labelString = name.replace(UIHandlerImpl.menueIndexChar + "", "");
        if (isInsertExtraButtonToMenuItem()) {
            GMenuItem menuItem = new GMenuItem(labelString, actionId, bb, null, index) {
                public Dimension getPreferredSize() {
                    Dimension preferredSize = super.getPreferredSize();
                    Dimension bps = extraButton.getPreferredSize();
                    preferredSize.width += bps.getWidth() + 4;
                    preferredSize.height = (int) Math.max(preferredSize.height, bps.getHeight()) + 1;
                    return preferredSize;
                }

            };
            String imageURL = "/graphtea/plugins/main/resources/edit16_.gif";
            extraButton = new JButton(new ImageIcon(getClass().getResource(imageURL)));
            extraButton.setBorder(new EmptyBorder(0, 0, 0, 0));
            extraButton.addActionListener(this);
            extraButton.addMouseListener(new MouseAdapter() {
                public void mouseClicked(MouseEvent e) {
                    actionPerformed(null);
                }
            });
            menuItem.setLayout(new BorderLayout());
            menuItem.add(extraButton, BorderLayout.EAST);
            return menuItem;
        } else
            return new GMenuItem(name, actionId, bb);
    }

    /**
     * determines whether to insert an extra button at the right side of menu item,
     * normally this is true whenever the target extension implements Parameterizable (have some parameters)
     * on this mode on pressing the button a dialog for setting the parameters will be shown, and then
     * it will be executed, if the user clicks the menu item directly the extension will be
     * performed without asking of parameters.
     * <p/>
     * if you want to override this method for your own ExtensionAction
     * NOTE that on pressing the button the actionPerformed will be called, so you can
     * do what you want to do by overriding that method.
     *
     * @return true to insert the extra button, false to no insert it.
     * @see Parametrizable
     * @see Parameter
     * @see graphtea.plugins.algorithmanimator.extension.AlgorithmExtensionAction
     */
    protected boolean isInsertExtraButtonToMenuItem() {
        return false; //target instanceof Parameterizable; we don't want this button for now. just go with the simple interface
    }

    /**
     * inorder if you created a new type of extension and for that extension you want to do some thing different
     * when it is called on commandline (i.e. in normal state it uses some GUI functionalities and you want to avoid them)
     * you can override this method and do what you want, which will
     * be called whenever your extension called from commandline
     * for an example see GraphGeneratorExtensionAction
     *
     * @see graphtea.plugins.graphgenerator.core.extension.GraphGeneratorExtensionAction
     */
    public Object performExtensionInCommandLine() {
        performExtension();
        return null;
    }

    protected void createExtensionCommandsForCommandLine() {
        final AbstractExtensionAction<t> ths = this;
        final t trgClass = target;
        final CommandAttitude comati = trgClass.getClass().getAnnotation(CommandAttitude.class);

        String command = "";
        String cname;
        String abrv;
        String desc;
        if (comati != null) {
            //            Shell.set_variable("_" + target.getClass().getSimpleName(), target);
            cname = comati.name();
            abrv = comati.abbreviation();
            desc = comati.description();
            if (desc == null || desc.isEmpty())
                desc = target.getDescription();
        } else {
            cname = target.getClass().getSimpleName();
            abrv = "";
            desc = target.getDescription();
        }
        command += cname + "(";
        String help = "";

        for (Field f : target.getClass().getFields()) {
            if (f.getAnnotation(Parameter.class) != null) {
                command += f.getType().getName()
                        + " "
                        + f.getName() + ",";
                help += "_" + target.getClass().getSimpleName()
                        + "."
                        + f.getName()
                        + " = "
                        + f.getName()
                        + ";\n";
            }
        }
        if (command.endsWith(","))
            command = command.substring(0, command.length() - 1);
        command += ")\n{";
        //        System.out.println(command);
        ExtensionShellCommandProvider.addCommand(ths, trgClass, cname, abrv, command, desc, help);

    }

    /**
     * to put a prefix before the name of your extension menu override this method
     */
    protected String getMenuNamePrefix() {
        return "";
    }

    /**
     * gets a menu for adding the sub menu, if returns null, no submenu will added!
     */
    protected JMenu getParentMenu() {
        GFrame f = UIUtils.getGFrame(blackboard);
        String mname = getParentMenuName();
        return f.getMenu().getUniqueMenu(mname, -1);
    }

    /**
     * first checks if o instanceof Parameterizable if so, shows an
     * editor for it's Parameters.
     *
     * @param o The object
     * @return true if o isn't an instance of Parameterizable or the user cancels the editing
     */
    public boolean testAndSetParameters(Object o) {
        if (o instanceof Parametrizable) {
            ParameterShower ps = new ParameterShower();

            return ps.show((Parametrizable) o);
        } else
            return true;
    }

    /**
     * removes all UI Components that are created for the extension (menus, ...)
     */
    public void removeCreatedUIComponents() {
        if (parentMenu != null && menuItem != null) {
            parentMenu.remove(menuItem);
            //if parent menu is empty remove it and so on.
            if (parentMenu.getMenuComponentCount() == 0) {
                parentMenu.getParent().remove(parentMenu);
            }
        }

    }

    /**
     * returns the menu name that the menuitem of this action is its child
     */
    public abstract String getParentMenuName();

    public final void performAction(String eventKey, Object value) {
        if (testAndSetParameters(target))
            performExtension();
    }

    public void track(){
        blackboard.setData("ATrack", new AEvent().category(getParentMenuName()).action(target.getName()));
    }


    /**
     * occurs whenever extraButton pressed
     * <p/>
     * =
     */
    public void actionPerformed(ActionEvent e) {
        parentMenu.setSelected(false);
        if (testAndSetParameters(target))
            performExtension();
    }

    public abstract void performExtension();
    public GraphModel generateGraph(){return new GraphModel();}

}
