/*
 * Copyright 2010-2015 Institut Pasteur.
 * 
 * This file is part of Icy.
 * 
 * Icy 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.
 * 
 * Icy 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 Icy. If not, see <http://www.gnu.org/licenses/>.
 */
package icy.gui.frame;

import icy.action.IcyAbstractAction;
import icy.common.MenuCallback;
import icy.gui.util.LookAndFeelUtil;
import icy.resource.ResourceUtil;
import icy.resource.icon.IcyIcon;
import icy.system.IcyExceptionHandler;
import icy.system.SystemUtil;
import icy.system.thread.ThreadUtil;

import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;

import javax.swing.JInternalFrame;
import javax.swing.JMenu;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;

import org.pushingpixels.substance.internal.utils.SubstanceInternalFrameTitlePane;

/**
 * @author Stephane
 */
public class IcyInternalFrame extends JInternalFrame
{
    /**
     * 
     */
    private static final long serialVersionUID = -5445569637723054083L;

    private class CloseAction extends IcyAbstractAction
    {
        /**
         * 
         */
        private static final long serialVersionUID = 4933605299188863452L;

        public CloseAction()
        {
            super("Close", new IcyIcon(ResourceUtil.ICON_CLOSE, 20), "Close window", KeyEvent.VK_F4, SystemUtil
                    .getMenuCtrlMask());
        }

        @Override
        public boolean doAction(ActionEvent e)
        {
            close(false);
            return true;
        }
    }

    /**
     * internals
     */
    SubstanceInternalFrameTitlePane titlePane = null;
    // JMenu systemMenu;
    MenuCallback systemMenuCallback;
    private boolean titleBarVisible;
    private boolean closeItemVisible;
    private boolean initialized = false;

    /**
     * @param title
     * @param resizable
     * @param closable
     * @param maximizable
     * @param iconifiable
     */
    public IcyInternalFrame(String title, boolean resizable, boolean closable, boolean maximizable, boolean iconifiable)
    {
        super(title, resizable, closable, maximizable, iconifiable);

        addPropertyChangeListener("titlePane", new PropertyChangeListener()
        {
            @Override
            public void propertyChange(PropertyChangeEvent evt)
            {
                // invoke later so the titlePane variable is up to date
                ThreadUtil.invokeLater(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        updateTitlePane();
                    }
                });
            }
        });

        addInternalFrameListener(new InternalFrameAdapter()
        {
            @Override
            public void internalFrameClosed(InternalFrameEvent e)
            {
                // release the system menu callback as it can lead to some memory leak
                // (cycling reference)
                systemMenuCallback = null;
            }
        });

        setFrameIcon(ResourceUtil.ICON_ICY_16);
        setVisible(false);

        systemMenuCallback = null;
        closeItemVisible = closable;
        updateTitlePane(LookAndFeelUtil.getTitlePane(this));

        titleBarVisible = true;
        initialized = true;
    }

    /**
     * update internals informations linked to title pane with specified pane
     */
    protected void updateTitlePane(final SubstanceInternalFrameTitlePane pane)
    {
        // update pane save
        if (pane != null)
            titlePane = pane;
        // update menu
        // if (titlePane != null)
        // systemMenuBar = titlePane.getMenuBar();
        // refresh system menu whatever
        updateSystemMenu();
    }

    /**
     * update internals informations linked to title pane and title pane state
     */
    protected void updateTitlePane()
    {
        if (initialized)
        {
            // title pane can have changed
            updateTitlePane(LookAndFeelUtil.getTitlePane(this));

            if (!titleBarVisible)
                setTitleBarVisible(false);
        }
    }

    /**
     * Refresh system menu
     */
    public void updateSystemMenu()
    {
        if ((titlePane != null) && !isClosed())
        {
            final JMenu menu;

            if (systemMenuCallback != null)
                menu = systemMenuCallback.getMenu();
            else
                menu = getDefaultSystemMenu();

            // ensure compatibility with heavyweight component
            menu.getPopupMenu().setLightWeightPopupEnabled(false);

            // rebuild menu
            titlePane.setSystemMenu(menu);
            // systemMenuBar.removeAll();
            // systemMenuBar.add(menu);
            // systemMenuBar.validate();
        }
    }

    /**
     * Close the frame.
     * 
     * @param force
     *        if <code>true</code> the frame is close even if {@link #isClosable()} return <code>false</code>
     */
    public void close(boolean force)
    {
        if (force || isClosable())
            doDefaultCloseAction();
    }

    /**
     * Implement isMinimized method
     */
    public boolean isMinimized()
    {
        return isIcon();
    }

    /**
     * Implement isMaximized method
     */
    public boolean isMaximized()
    {
        return isMaximum();
    }

    /**
     * Implement setMinimized method
     */
    public void setMinimized(final boolean value)
    {
        // only relevant if state changed
        if (isMinimized() ^ value)
        {
            try
            {
                setIcon(value);
            }
            catch (PropertyVetoException e)
            {
                IcyExceptionHandler.showErrorMessage(e, true);
            }
        }
    }

    /**
     * Implement setMaximized method
     */
    public void setMaximized(final boolean value)
    {
        // have to check that else we obtain a null pointer exception
        if (getParent() == null)
            return;

        // only relevant if state changed
        if (isMaximized() ^ value)
        {
            try
            {
                // have to check for parent non null
                setMaximum(value);
            }
            catch (PropertyVetoException e)
            {
                IcyExceptionHandler.showErrorMessage(e, true);
            }
        }
    }

    /**
     * @return the titleBarVisible
     */
    public boolean isTitleBarVisible()
    {
        return titleBarVisible;
    }

    /**
     * @return the closeItemVisible
     */
    public boolean isCloseItemVisible()
    {
        return closeItemVisible;
    }

    /**
     * @param value
     *        the closeItemVisible to set
     */
    public void setCloseItemVisible(boolean value)
    {
        if (closeItemVisible != value)
        {
            closeItemVisible = value;

            ThreadUtil.invokeLater(new Runnable()
            {
                @Override
                public void run()
                {
                    updateSystemMenu();
                }
            });
        }
    }

    @Override
    public void setClosable(boolean b)
    {
        super.setClosable(b);

        if (!b)
            setCloseItemVisible(false);
    }

    /**
     * @param value
     *        the titleBarVisible to set
     */
    public void setTitleBarVisible(boolean value)
    {
        if (value)
            LookAndFeelUtil.setTitlePane(this, titlePane);
        else
            LookAndFeelUtil.setTitlePane(this, null);

        revalidate();

        titleBarVisible = value;
    }

    /**
     * Return the default system menu
     */
    public JMenu getDefaultSystemMenu()
    {
        final JMenu result = new JMenu();

        if (closeItemVisible)
            result.add(new CloseAction());

        return result;
    }

    /**
     * @return the systemMenuCallback
     */
    public MenuCallback getSystemMenuCallback()
    {
        return systemMenuCallback;
    }

    /**
     * @param value
     *        the systemMenuCallback to set
     */
    public void setSystemMenuCallback(MenuCallback value)
    {
        if (systemMenuCallback != value)
        {
            systemMenuCallback = value;

            ThreadUtil.invokeLater(new Runnable()
            {
                @Override
                public void run()
                {
                    updateSystemMenu();
                }
            });
        }
    }
}