/*
 * 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.util;

import icy.common.listener.weak.WeakComponentListener;
import icy.common.listener.weak.WeakIcyFrameListener;
import icy.common.listener.weak.WeakWindowListener;
import icy.gui.component.ExternalizablePanel;
import icy.gui.component.ExternalizablePanel.StateListener;
import icy.gui.component.ExternalizablePanel.WeakStateListener;
import icy.gui.frame.IcyFrame;
import icy.gui.frame.IcyFrameAdapter;
import icy.gui.frame.IcyFrameEvent;
import icy.gui.main.MainFrame;
import icy.preferences.IcyPreferences;
import icy.preferences.XMLPreferences;

import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JComponent;
import javax.swing.JFrame;

/**
 * Load and Save you window position in the registry
 * 
 * @author fab & stephane
 */
public class WindowPositionSaver
{
    final private static String ID_EXTERNALIZED = "Externalized";
    final private static String ID_PANELIZED = "Panelized";

    final private static String ID_XC = "XC";
    final private static String ID_YC = "YC";
    final private static String ID_WC = "WidthC";
    final private static String ID_HC = "HeightC";

    final private static String ID_XI = "XI";
    final private static String ID_YI = "YI";
    final private static String ID_WI = "WidthI";
    final private static String ID_HI = "HeightI";
    final private static String ID_MAXIMIZEDI = "MaximizedI";

    final private static String ID_XE = "XE";
    final private static String ID_YE = "YE";
    final private static String ID_WE = "WidthE";
    final private static String ID_HE = "HeightE";
    final private static String ID_MAXIMIZEDE = "MaximizedE";

    final XMLPreferences preferences;

    final private MainFrame mainFrame;
    final private IcyFrame icyFrame;
    final private ExternalizablePanel extPanel;
    final private JFrame jFrame;
    final private JComponent component;

    final private boolean hasLoc;
    final private boolean hasDim;

    final WindowAdapter windowAdapter;
    final IcyFrameAdapter icyFrameAdapter;
    final ComponentAdapter componentAdapter;
    final StateListener stateListener;

    private WindowPositionSaver(final MainFrame mainFrame, final IcyFrame icyFrame, final ExternalizablePanel extPanel,
            final JFrame jFrame, final JComponent component, final String key, final Point defLoc,
            final Dimension defDim)
    {
        preferences = IcyPreferences.root().node(key);

        this.mainFrame = mainFrame;
        this.icyFrame = icyFrame;
        this.extPanel = extPanel;
        this.jFrame = jFrame;
        this.component = component;

        hasLoc = defLoc != null;
        hasDim = defDim != null;

        // directly load location and dimension
        if (hasLoc)
            loadLocation(defLoc);
        if (hasDim)
            loadDimension(defDim);
        loadState();

        // keep hard reference on it
        componentAdapter = new ComponentAdapter()
        {
            @Override
            public void componentResized(ComponentEvent e)
            {
                // event comes from panel itself
                if ((extPanel == null) || (e.getSource() == extPanel))
                    saveAll();
            }

            @Override
            public void componentMoved(ComponentEvent e)
            {
                // event comes from panel itself
                if ((extPanel == null) || (e.getSource() == extPanel))
                    saveAll();
            }

            @Override
            public void componentShown(ComponentEvent e)
            {
                // correctly save internalized / externalized state
                saveAll();
            }

            @Override
            public void componentHidden(ComponentEvent e)
            {
                // correctly save internalized / externalized state
                saveAll();
            }
        };

        // keep hard reference on it
        windowAdapter = new WindowAdapter()
        {
            @Override
            public void windowClosing(WindowEvent e)
            {
                saveAll();
            }
        };

        // keep hard reference on it
        icyFrameAdapter = new IcyFrameAdapter()
        {
            @Override
            public void icyFrameClosing(IcyFrameEvent e)
            {
                saveAll();
            }
        };

        // keep hard reference on it
        stateListener = new StateListener()
        {
            @Override
            public void stateChanged(ExternalizablePanel source, boolean externalized)
            {
                saveAll();
            }
        };

        if (mainFrame != null)
        {
            mainFrame.addWindowListener(new WeakWindowListener(windowAdapter));
            mainFrame.addComponentListener(new WeakComponentListener(componentAdapter));
        }
        else if (icyFrame != null)
        {
            icyFrame.addFrameListener(new WeakIcyFrameListener(icyFrameAdapter));
            icyFrame.addComponentListener(new WeakComponentListener(componentAdapter));
        }
        else if (extPanel != null)
        {
            extPanel.addComponentListener(new WeakComponentListener(componentAdapter));
            extPanel.addStateListener(new WeakStateListener(stateListener));
            extPanel.getFrame().addFrameListener(new WeakIcyFrameListener(icyFrameAdapter));
            extPanel.getFrame().addComponentListener(new WeakComponentListener(componentAdapter));
        }
        else if (jFrame != null)
        {
            jFrame.addWindowListener(new WeakWindowListener(windowAdapter));
            jFrame.addComponentListener(new WeakComponentListener(componentAdapter));
        }
        else if (component != null)
        {
            component.addComponentListener(new WeakComponentListener(componentAdapter));
        }
    }

    public WindowPositionSaver(MainFrame frame, String key, Point defLoc, Dimension defDim)
    {
        this(frame, null, null, null, null, key, defLoc, defDim);
    }

    public WindowPositionSaver(MainFrame frame, String key, Point defLoc)
    {
        this(frame, null, null, null, null, key, defLoc, null);
    }

    public WindowPositionSaver(MainFrame frame, String key, Dimension defDim)
    {
        this(frame, null, null, null, null, key, null, defDim);
    }

    public WindowPositionSaver(IcyFrame frame, String key, Point defLoc, Dimension defDim)
    {
        this(null, frame, null, null, null, key, defLoc, defDim);
    }

    public WindowPositionSaver(IcyFrame frame, String key, Point defLoc)
    {
        this(null, frame, null, null, null, key, defLoc, null);
    }

    public WindowPositionSaver(IcyFrame frame, String key, Dimension defDim)
    {
        this(null, frame, null, null, null, key, null, defDim);
    }

    public WindowPositionSaver(ExternalizablePanel extPanel, String key, Point defLoc, Dimension defDim)
    {
        this(null, null, extPanel, null, null, key, defLoc, defDim);
    }

    public WindowPositionSaver(ExternalizablePanel extPanel, String key, Point defLoc)
    {
        this(null, null, extPanel, null, null, key, defLoc, null);
    }

    public WindowPositionSaver(ExternalizablePanel extPanel, String key, Dimension defDim)
    {
        this(null, null, extPanel, null, null, key, null, defDim);
    }

    public WindowPositionSaver(JFrame frame, String key, Point defLoc, Dimension defDim)
    {
        this(null, null, null, frame, null, key, defLoc, defDim);
    }

    public WindowPositionSaver(JFrame frame, String key, Point defLoc)
    {
        this(null, null, null, frame, null, key, defLoc, null);
    }

    public WindowPositionSaver(JFrame frame, String key, Dimension defDim)
    {
        this(null, null, null, frame, null, key, null, defDim);
    }

    public WindowPositionSaver(JComponent component, String key, Point defLoc, Dimension defDim)
    {
        this(null, null, null, null, component, key, defLoc, defDim);
    }

    public WindowPositionSaver(JComponent component, String key, Point defLoc)
    {
        this(null, null, null, null, component, key, defLoc, null);
    }

    public WindowPositionSaver(JComponent component, String key, Dimension defDim)
    {
        this(null, null, null, null, component, key, null, defDim);
    }

    // void checkPosition()
    // {
    // final Rectangle rect;
    //
    // ComponentUtil.fixPosition(rect, rect);
    //
    // // only for frame
    // if (mainFrame != null)
    // ComponentUtil.fixP
    // rect = mainFrame.getBounds();
    // else if (icyFrame != null)
    // rect = icyFrame.getBounds();
    // // check position only for frame
    // else if ((extPanel != null) && extPanel.isExternalized())
    // rect = extPanel.getFrame().getBounds();
    // else if (jFrame != null)
    // rect = jFrame.getBounds();
    // else
    // return;
    //
    // if (fixPosition(rect))
    // {
    // if (mainFrame != null)
    // mainFrame.setBounds(rect);
    // else if (icyFrame != null)
    // icyFrame.setBounds(rect);
    // // check position only for frame
    // else if ((extPanel != null) && extPanel.isExternalized())
    // extPanel.getFrame().setBounds(rect);
    // else if (jFrame != null)
    // jFrame.setBounds(rect);
    // }
    // }
    //
    // public static boolean fixPosition(Rectangle rect)
    // {
    // Rectangle desktopRect = SystemUtil.getDesktopBounds();
    // boolean result = false;
    // int limit;
    //
    // limit = (int) (desktopRect.getMaxX() - DESKTOP_MARGIN);
    // if (rect.x >= limit)
    // {
    // rect.x = limit;
    // result = true;
    // }
    // else
    // {
    // limit = (int) desktopRect.getMinX();
    // if (((rect.x + rect.width) - DESKTOP_MARGIN) < limit)
    // {
    // rect.x = DESKTOP_MARGIN - rect.width;
    // result = true;
    // }
    // }
    // limit = (int) (desktopRect.getMaxY() - DESKTOP_MARGIN);
    // if (rect.y >= limit)
    // {
    // rect.y = limit;
    // result = true;
    // }
    // else
    // {
    // limit = (int) desktopRect.getMinY();
    // if (rect.y < limit)
    // {
    // rect.y = limit;
    // result = true;
    // }
    // }
    //
    // return result;
    // }

    public XMLPreferences getPreferences()
    {
        return preferences;
    }

    public void loadLocation(Point defaultPos)
    {
        final int x, y;

        if (defaultPos == null)
        {
            x = 0;
            y = 0;
        }
        else
        {
            x = defaultPos.x;
            y = defaultPos.y;
        }

        final Point positionC = new Point(preferences.getInt(ID_XC, x), preferences.getInt(ID_YC, y));
        final Point positionI = new Point(preferences.getInt(ID_XI, x), preferences.getInt(ID_YI, y));
        final Point positionE = new Point(preferences.getInt(ID_XE, x), preferences.getInt(ID_YE, y));

        if (mainFrame != null)
        {
            mainFrame.setLocation(positionE);
        }
        else if (icyFrame != null)
        {
            icyFrame.setLocationInternal(positionI);
            icyFrame.setLocationExternal(positionE);
        }
        else if (extPanel != null)
        {
            extPanel.setLocation(positionC);

            // get the panel frame
            final IcyFrame f = extPanel.getFrame();

            f.setLocationInternal(positionI);
            f.setLocationExternal(positionE);
        }
        else if (jFrame != null)
        {
            jFrame.setLocation(positionE);
        }
        else if (component != null)
        {
            component.setLocation(positionC);
        }
    }

    public void loadDimension(Dimension defaultDim)
    {
        final int w, h;

        if (defaultDim == null)
        {
            w = 300;
            h = 300;
        }
        else
        {
            w = defaultDim.width;
            h = defaultDim.height;
        }

        // minimum size is 10 pixels
        int widthC = Math.max(preferences.getInt(ID_WC, w), 10);
        int heightC = Math.max(preferences.getInt(ID_HC, h), 10);
        int widthI = Math.max(preferences.getInt(ID_WI, w), 10);
        int heightI = Math.max(preferences.getInt(ID_HI, h), 10);
        int widthE = Math.max(preferences.getInt(ID_WE, w), 10);
        int heightE = Math.max(preferences.getInt(ID_HE, h), 10);

        final Dimension dimC = new Dimension(widthC, heightC);
        final Dimension dimI = new Dimension(widthI, heightI);
        final Dimension dimE = new Dimension(widthE, heightE);

        if (mainFrame != null)
        {
            // set size only else we cannot pack anymore the frame for detached mode
            mainFrame.setSize(dimE);
        }
        else if (icyFrame != null)
        {
            icyFrame.setPreferredSizeExternal(dimE);
            icyFrame.setSizeExternal(dimE);
            icyFrame.setPreferredSizeInternal(dimI);
            icyFrame.setSizeInternal(dimI);
        }
        else if (extPanel != null)
        {
            extPanel.setPreferredSize(dimC);

            // get the panel frame
            final IcyFrame f = extPanel.getFrame();

            f.setPreferredSizeExternal(dimE);
            f.setSizeExternal(dimE);
            f.setPreferredSizeInternal(dimI);
            f.setSizeInternal(dimI);
        }
        else if (jFrame != null)
        {
            jFrame.setPreferredSize(dimE);
            jFrame.setSize(dimE);
        }
        else if (component != null)
        {
            component.setPreferredSize(dimC);
        }
    }

    public void loadState()
    {
        // default is internalized and panelized
        final boolean externalized = preferences.getBoolean(ID_EXTERNALIZED, false);
        final boolean panelized = preferences.getBoolean(ID_PANELIZED, true);
        final boolean maximizedE = preferences.getBoolean(ID_MAXIMIZEDE, false);
        final boolean maximizedI = preferences.getBoolean(ID_MAXIMIZEDI, false);

        if (mainFrame != null)
        {
            ComponentUtil.setMaximized(mainFrame, maximizedE);
        }
        else if (icyFrame != null)
        {
            if (externalized)
                icyFrame.externalize();
            else
                icyFrame.internalize();
            icyFrame.setMaximizedExternal(maximizedE);
            icyFrame.setMaximizedInternal(maximizedI);
        }
        else if (extPanel != null)
        {
            // get the panel frame
            final IcyFrame f = extPanel.getFrame();

            if (externalized)
                f.externalize();
            else
                f.internalize();
            f.setMaximizedExternal(maximizedE);
            f.setMaximizedInternal(maximizedI);

            if (panelized)
                extPanel.internalize();
            else
                extPanel.externalize();
        }
        else if (jFrame != null)
        {
            ComponentUtil.setMaximized(jFrame, maximizedE);
        }
    }

    public void saveLocation()
    {
        if (mainFrame != null)
        {
            if (!(ComponentUtil.isMaximized(mainFrame) || ComponentUtil.isMinimized(mainFrame)))
            {
                preferences.putInt(ID_XE, mainFrame.getX());
                preferences.putInt(ID_YE, mainFrame.getY());
            }
        }
        else if (icyFrame != null)
        {
            if (!(icyFrame.isMaximizedExternal() || icyFrame.isMinimizedExternal()))
            {
                preferences.putInt(ID_XE, icyFrame.getXExternal());
                preferences.putInt(ID_YE, icyFrame.getYExternal());
            }
            if (!(icyFrame.isMaximizedInternal() || icyFrame.isMinimizedInternal()))
            {
                preferences.putInt(ID_XI, icyFrame.getXInternal());
                preferences.putInt(ID_YI, icyFrame.getYInternal());
            }
        }
        else if (extPanel != null)
        {
            preferences.putInt(ID_XC, extPanel.getX());
            preferences.putInt(ID_YC, extPanel.getY());

            // get the panel frame
            final IcyFrame f = extPanel.getFrame();

            if (!(f.isMaximizedExternal() || f.isMinimizedExternal()))
            {
                preferences.putInt(ID_XE, f.getXExternal());
                preferences.putInt(ID_YE, f.getYExternal());
            }
            if (!(f.isMaximizedInternal() || f.isMinimizedInternal()))
            {
                preferences.putInt(ID_XI, f.getXInternal());
                preferences.putInt(ID_YI, f.getYInternal());
            }
        }
        else if (jFrame != null)
        {
            if (!(ComponentUtil.isMaximized(jFrame) || ComponentUtil.isMinimized(jFrame)))
            {
                preferences.putInt(ID_XE, jFrame.getX());
                preferences.putInt(ID_YE, jFrame.getY());
            }
        }
        else if (component != null)
        {
            preferences.putInt(ID_XC, component.getX());
            preferences.putInt(ID_YC, component.getY());
        }
    }

    public void saveDimension()
    {
        if (mainFrame != null)
        {
            if (!(ComponentUtil.isMaximized(mainFrame) || ComponentUtil.isMinimized(mainFrame)))
            {
                final Dimension dim;

                if (mainFrame.isDetachedMode())
                    dim = new Dimension(mainFrame.getWidth(), mainFrame.getPreviousHeight());
                else
                    dim = mainFrame.getSize();

                preferences.putInt(ID_WE, dim.width);
                preferences.putInt(ID_HE, dim.height);
            }
        }
        else if (icyFrame != null)
        {
            if (!(icyFrame.isMaximizedExternal() || icyFrame.isMinimizedExternal()))
            {
                preferences.putInt(ID_WE, icyFrame.getWidthExternal());
                preferences.putInt(ID_HE, icyFrame.getHeightExternal());
            }
            if (!(icyFrame.isMaximizedInternal() || icyFrame.isMinimizedInternal()))
            {
                preferences.putInt(ID_WI, icyFrame.getWidthInternal());
                preferences.putInt(ID_HI, icyFrame.getHeightInternal());
            }
        }
        else if (extPanel != null)
        {
            preferences.putInt(ID_WC, extPanel.getWidth());
            preferences.putInt(ID_HC, extPanel.getHeight());

            // get the panel frame
            final IcyFrame f = extPanel.getFrame();

            if (!(f.isMaximizedExternal() || f.isMinimizedExternal()))
            {
                preferences.putInt(ID_WE, f.getWidthExternal());
                preferences.putInt(ID_HE, f.getHeightExternal());
            }
            if (!(f.isMaximizedInternal() || f.isMinimizedInternal()))
            {
                preferences.putInt(ID_WI, f.getWidthInternal());
                preferences.putInt(ID_HI, f.getHeightInternal());
            }
        }
        else if (jFrame != null)
        {
            if (!(ComponentUtil.isMaximized(jFrame) || ComponentUtil.isMinimized(jFrame)))
            {
                preferences.putInt(ID_WE, jFrame.getWidth());
                preferences.putInt(ID_HE, jFrame.getHeight());
            }
        }
        else if (component != null)
        {
            preferences.putInt(ID_WC, component.getWidth());
            preferences.putInt(ID_HC, component.getHeight());
        }
    }

    public void saveState()
    {
        if (mainFrame != null)
        {
            final boolean b;

            if (mainFrame.isDetachedMode())
                b = mainFrame.getPreviousMaximized();
            else
                b = ComponentUtil.isMaximized(mainFrame);

            preferences.putBoolean(ID_MAXIMIZEDE, b);
        }
        if (icyFrame != null)
        {
            preferences.putBoolean(ID_EXTERNALIZED, icyFrame.isExternalized());
            preferences.putBoolean(ID_MAXIMIZEDI, icyFrame.isMaximizedInternal());
            preferences.putBoolean(ID_MAXIMIZEDE, icyFrame.isMaximizedExternal());
        }
        else if (extPanel != null)
        {
            preferences.putBoolean(ID_PANELIZED, extPanel.isInternalized());

            // get the panel frame
            final IcyFrame f = extPanel.getFrame();

            preferences.putBoolean(ID_EXTERNALIZED, f.isExternalized());
            preferences.putBoolean(ID_MAXIMIZEDI, f.isMaximizedInternal());
            preferences.putBoolean(ID_MAXIMIZEDE, f.isMaximizedExternal());
        }
        else if (jFrame != null)
        {
            preferences.putBoolean(ID_MAXIMIZEDE, ComponentUtil.isMaximized(jFrame));
        }
    }

    public void saveAll()
    {
        if (hasLoc)
            saveLocation();
        if (hasDim)
            saveDimension();
        saveState();
    }
}