/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on 2005/11/02, 13:50
!  AUTHOR(S): KOGA, Junichiro
!  File : PluginParser.java
!  
!  Contact address :  Phase System Consortium
!                     E-mail: phase_system@nims.go.jp URL https://azuma.nims.go.jp
!
!
!   Since 2002, this program set had been intensively developed as a part of the following 
!  national projects supported by the Ministry of Education, Culture, Sports, Science and
!  Technology (MEXT) of Japan; "Frontier Simulation Software for Industrial Science
!  (FSIS)" from 2002 to 2005, "Revolutionary Simulation Software (RSS21)" from 2006 to
!  2008. "Research and Development of Innovative Simulation Software (RISS)" from 2008
!  to 2013. These projects is lead by the Center for Research on Innovative Simulation 
!  Software (CISS), the Institute of Industrial Science (IIS), the University of Tokyo.
!   Since 2013, this program set has been further developed centering on PHASE System
!  Consortium. 
!   The activity of development of this program set has been supervised by Takahisa Ohno.
!
!=======================================================================
 */

package ciss.phase_viewer.acviewer;

import java.awt.event.MouseAdapter;
import java.net.URL;
import java.util.Vector;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;

import org.apache.log4j.Logger;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;

import ciss.phase_viewer.common.MenuTree;
import ciss.phase_viewer.common.MyJMenu;
import ciss.phase_viewer.common.MyJMenuItem;
import ciss.phase_viewer.jdom.MyElement;
import ciss.phase_viewer.jdom.XMLUtils;

/**
 * プラグインを検地し, 必要な情報を構築するクラス.
 * 
 * @author
 */
public class PluginParser {
    private Logger logger = Logger.getLogger(PluginParser.class.getName());
    public static final String PLUGIN_ACV = "/ciss/phase_viewer/acviewer/plugins.xml";
    public static final String PLUGIN_KSPACE = "/ciss/phase_viewer/acviewer/fbz/plugins.xml";

    private J3DPanel parent;

    private Vector buttonVector = new Vector();
    private Vector menuVector = new Vector();

    private String pluginPath = PLUGIN_ACV;

    /**
     * Creates a new instance of PluginParser
     * 
     * @param parent
     *            原子配置ビューアーの起点のクラス. ここから画面上の色々な情報を参照することができる.
     */
    public PluginParser(J3DPanel parent) {
        this.parent = parent;
        init();
        parse();
    }

    /**
     * Creates a new instance of PluginParser. Pluginの情報が記述してあるパスも渡せる.
     * 
     * @param parent
     *            原子配置ビューアーの起点のクラス. ここから画面上の色々な情報を参照することができる.
     * @param pluginPath
     *            プラグイン情報の記述してあるファイル.
     */
    public PluginParser(J3DPanel parent, String pluginPath) {
        this.parent = parent;
        this.pluginPath = pluginPath;
        init();
        parse();
    }

    private long delay = 1000L;

    private void init() {
        try {
            delay = Long.parseLong(parent.getJ3DProperties().getProperty(
                    "viewer_button_tooltip_delay"));
        } catch (Exception exc) {
        }
    }

    /**
     * プラグイン設定ファイルを検査した結果, 配備されるべきボタンの配列を返す.
     * 
     * @return プラグイン設定ファイルに記述された属性をもつJButtonの配列.
     */
    public JButton[] getButtons() {
        if (buttonVector.size() == 0) {
            return null;
        }

        JButton[] buttons = new JButton[buttonVector.size()];
        buttonVector.copyInto(buttons);
        return buttons;
    }

    /**
     * プラグイン設定ファイルを検査した結果, 配備されるべきメニューの配列を返す.
     * 
     * @return プラグイン設定ファイルに記述された属性をもつJButtonの配列.
     */
    public JMenu[] getMenus() {
        return tree.getMenus();
    }

    private void parse() {
        URL url = PluginParser.class.getResource(pluginPath);
        Document doc = XMLUtils.getDocumentFromURL(url);
        if (doc == null) {
            logger.error("couldn't read plugin specification file...");
            return;
        }
        Element root = doc.getRootElement();
        java.util.List topmenus = root.getChildren("menus");
        if (topmenus == null || topmenus.size() == 0) {
            logger.warn("no definition for menu found... something is probably wrong.");
            return;
        }
        for (int i = 0; i < topmenus.size(); i++) {
            createMenus((Element) topmenus.get(i));
        }

        java.util.List modules = root.getChildren("module");
        if (modules == null || modules.size() == 0) {
            logger.warn("no plugins found... something is probably wrong.");
            return;
        }

        for (int i = 0; i < modules.size(); i++) {
            parseEachModule((Element) modules.get(i));
        }
    }

    private void createMenus(Element menus) {
        java.util.List children = menus.getChildren();
        for (int i = 0; i < children.size(); i++) {
            Element menu = (Element) children.get(i);
            String[] menuName = menu.getName().split("\\.");
            Attribute attr = menu.getAttribute("shortcut");
            char shortcut = ' ';
            if (attr != null) {
                shortcut = attr.getValue().charAt(0);
            }
            createMenu(menuName, shortcut);
        }
    }

    private MenuTree tree = new MenuTree();

    private void createMenu(String[] menuName, char shortcut) {
        tree.selectRoot();
        for (int i = 0; i < menuName.length; i++) {
            logger.debug("creating menu: " + menuName[i]);
            MyJMenu men = new MyJMenu(menuName[i]);
            men.setMnemonic(shortcut);
            tree.selectBlock(men);
        }
    }

    private void parseEachModule(Element module) {
        String name = module.getChildTextTrim("name");
        if (name == null) {
            name = "";
        }
        String action = module.getChildTextTrim("action");
        java.util.List booters = module.getChildren("booter");
        Vector bootersVec = new Vector();
        for (int i = 0; i < booters.size(); i++) {
            Booter booter = new Booter((Element) booters.get(i));
            bootersVec.addElement(booter);
        }

        Booter[] booterAr = null;
        if (bootersVec.size() != 0) {
            booterAr = new Booter[bootersVec.size()];
            bootersVec.copyInto(booterAr);
        }

        createModule(name, action, booterAr);
    }

    private void createModule(String name, String action, Booter[] booterAr) {
        if (booterAr == null || booterAr.length == 0) {
            return;
        }

        for (int i = 0; i < booterAr.length; i++) {
            int type = booterAr[i].type;
            String text = booterAr[i].tooltiptext;
            String icon = booterAr[i].iconpath;
            String spec = booterAr[i].spec;
            char shortcut = booterAr[i].shortcut;
            boolean hasSep = booterAr[i].hasSep;

            if (type == Booter.BUTTON) {
                JButton btn = createButtonFrom(name, text, icon);
                ACVAction acvaction = ACVAction.getAction(parent, action);
                btn.addActionListener(acvaction);
                buttonVector.addElement(btn);
            } else if (type == Booter.MENU) {
                JMenuItem menuItem = createMenuItemFrom(name, spec, text, icon);
                ACVAction acvaction = ACVAction.getAction(parent, action);
                menuItem.addActionListener(acvaction);
                if (shortcut != ' ') {
                    menuItem.setMnemonic(shortcut);
                }
                addMenuItem(spec, menuItem, hasSep);
            }
        }
    }

    private JButton createButtonFrom(String name, String tooltipText,
            String iconpath) {
        JButton btn = null;

        if (iconpath.trim().length() == 0) {
            btn = new JButton(name);
        } else {
            Icon ico = new ImageIcon(getClass().getResource(iconpath));
            if (ico != null) {
                btn = new JButton(ico);
            } else {
                btn = new JButton(name);
            }
        }
        boolean viewer_button_no_tooltip = new Boolean(parent
                .getJ3DProperties().getProperty("viewer_button_no_tooltip"))
                .booleanValue();
        if (tooltipText.trim().length() != 0 && !viewer_button_no_tooltip) {
            MouseAdapterButton adap = new MouseAdapterButton(tooltipText, delay);
            btn.addMouseListener(adap);
        }
        return btn;
    }

    private JMenuItem createMenuItemFrom(String name, String spec,
            String tooltipText, String iconpath) {
        String[] menuSpec = spec.split("\\.");
        String menuItem = "";
        if (menuSpec.length == 0) {
            menuItem = spec;
        } else {
            menuItem = menuSpec[menuSpec.length - 1];
        }

        MyJMenuItem item = new MyJMenuItem(menuItem);
        if (tooltipText != null && tooltipText.trim().length() != 0) {
            item.setToolTipText(tooltipText);
        }
        if (iconpath != null && iconpath.trim().length() != 0) {
            Icon ico = new ImageIcon(getClass().getResource(iconpath));
            if (ico != null) {
                item.setIcon(ico);
            }
        }
        return item;
    }

    private void addMenuItem(String spec, JMenuItem item, boolean hasSep) {
        String[] menuSpec = spec.split("\\.");
        String menuItem = "";
        tree.selectRoot();
        for (int i = 0; i < menuSpec.length - 1; i++) {
            tree.selectBlock(menuSpec[i]);
        }
        tree.addMenuItem(item, hasSep);
    }
}

class MouseAdapterButton extends MouseAdapter {
    private JPopupMenu popup = new JPopupMenu();
    private JLabel item = new JLabel();
    private long delay;

    protected MouseAdapterButton(String text, long delay) {
        this.delay = delay;
        popup.setLightWeightPopupEnabled(false);
        item.setText(text);
        popup.add(item);
    }

    public void mouseEntered(java.awt.event.MouseEvent e) {
        int x = e.getX() + ((JComponent) e.getSource()).getLocationOnScreen().x;
        int y = e.getY() + ((JComponent) e.getSource()).getLocationOnScreen().y;
        popup.setLocation(x, y);
        try {
            Thread.sleep(delay);
        } catch (Exception exc) {
        }

        popup.setVisible(true);
    }

    public void mouseExited(java.awt.event.MouseEvent e) {
        popup.setVisible(false);
    }

}

class Booter {
    public static int BUTTON = 0;
    public static int MENU = 1;

    protected int type = BUTTON;
    protected String spec;
    protected String tooltiptext = "";
    protected String iconpath = "";
    protected char shortcut = ' ';
    protected boolean hasSep = false;

    protected Booter(Element booterElement) {
        String strtype = booterElement.getChildTextTrim("type");
        if (strtype == null || strtype.length() == 0) {
            type = BUTTON;
        } else if (strtype.equalsIgnoreCase("menu")) {
            type = MENU;
        } else if (strtype.equalsIgnoreCase("button")) {
            type = BUTTON;
        }

        String tmpSpec = booterElement.getChildTextTrim("spec");
        if (tmpSpec != null) {
            spec = tmpSpec;
        }

        try {
            String tmpShort = booterElement.getChild("spec")
                    .getAttribute("shortcut").getValue();
            if (tmpShort != null) {
                shortcut = tmpShort.charAt(0);
            }
            hasSep = new Boolean(booterElement.getChild("spec")
                    .getAttribute("separator").getValue()).booleanValue();
        } catch (Exception ex) {
        }

        String tmpTool = MyElement.decode(booterElement
                .getChildTextTrim("tooltiptext"));
        if (tmpTool != null) {
            tooltiptext = tmpTool;
        }

        String tmpIcon = MyElement.decode(booterElement
                .getChildTextTrim("iconpath"));
        if (tmpIcon != null) {
            iconpath = tmpIcon;
        }
    }

}
