package org.limewire.ui.swing.mainframe;

import java.awt.Dimension;
import java.awt.Frame;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowStateListener;

import javax.swing.JFrame;

import org.limewire.ui.swing.settings.SwingUiSettings;

class FramePositioner {

    /**
     * The last state of the X/Y location and the time it was set.
     * This is necessary to preserve the maximize size & prior size,
     * as on Windows a move event is occasionally triggered when
     * maximizing, prior to the state actually becoming maximized.
     */
    private WindowState lastState;
    
    /** The frame this is listening to. */
    private JFrame frame;
    
    void initialize(JFrame frame) {
        assert this.frame == null : "already initialized on a frame!";
        this.frame = frame;
        listenToWindowPosition();
    }

    
    void setWindowPosition() {
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gd = ge.getDefaultScreenDevice();
        GraphicsConfiguration gc = gd.getDefaultConfiguration();
        Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(gc);
        
        int appWidth  = Math.min(screenSize.width-insets.left-insets.right, SwingUiSettings.APP_WIDTH.getValue());
        int appHeight = Math.min(screenSize.height-insets.top-insets.bottom, SwingUiSettings.APP_HEIGHT.getValue());

        // TODO: Get a real minimum size? 
        frame.setMinimumSize(new Dimension(500, 500));
        frame.setSize(new Dimension(appWidth, appHeight));
        
        // Set the location of our window based on whether or not
        // the user has run the program before, and therefore may have 
        // modified the location of the main window.
        if(SwingUiSettings.POSITIONS_SET.getValue()) {
            
            int locX = Math.max(insets.left, SwingUiSettings.WINDOW_X.getValue());
            int locY = Math.max(insets.top, SwingUiSettings.WINDOW_Y.getValue());
        
            // Make sure the Window is visible and not for example 
            // somewhere in the very bottom right corner.
            if (locX+appWidth > screenSize.width) {
                locX = Math.max(insets.left, screenSize.width - insets.left - insets.right - appWidth);
            }
            
            if (locY+appHeight > screenSize.height) {
                locY = Math.max(insets.top, screenSize.height - insets.top - insets.bottom - appHeight);
            }
        
            frame.setLocation(locX, locY);
            
        } else {
            frame.setLocationRelativeTo(null);
        }
        
        //re-maximize if we shutdown while maximized.
        if(SwingUiSettings.MAXIMIZE_WINDOW.getValue() 
                && Toolkit.getDefaultToolkit().isFrameStateSupported(Frame.MAXIMIZED_BOTH)) {
            frame.setExtendedState(Frame.MAXIMIZED_BOTH);
        }
        
        frame.validate();
    }    
    
    private void listenToWindowPosition() {
        frame.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentMoved(ComponentEvent e) {
                lastState = new WindowState();
                saveWindowState();
            }

            @Override
            public void componentResized(ComponentEvent e) {
                saveWindowState();
            }
        });
        
        frame.addWindowStateListener(new WindowStateListener() {
            @Override
			public void windowStateChanged(WindowEvent e) {
                saveWindowState();
            }
        });
        
        // TODO: Is this listener necessary?  I don't think it is.
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                saveWindowState();
            }
        });
    }    
    
    /** Saves the state of the Window to settings. */
    private void saveWindowState() {
        int state = frame.getExtendedState();
        if(state == Frame.NORMAL) {
            // save the screen size and location 
            Dimension dim = frame.getSize();
            if((dim.height > 100) && (dim.width > 100)) {
                Point loc = frame.getLocation();
                SwingUiSettings.APP_WIDTH.setValue(dim.width);
                SwingUiSettings.APP_HEIGHT.setValue(dim.height);
                SwingUiSettings.WINDOW_X.setValue(loc.x);
                SwingUiSettings.WINDOW_Y.setValue(loc.y);
                SwingUiSettings.MAXIMIZE_WINDOW.setValue(false);
                SwingUiSettings.POSITIONS_SET.setValue(true);
            }
        } else if( (state & Frame.MAXIMIZED_BOTH) == Frame.MAXIMIZED_BOTH) {
            SwingUiSettings.MAXIMIZE_WINDOW.setValue(true);
            if(lastState != null && lastState.time == System.currentTimeMillis()) {
                SwingUiSettings.POSITIONS_SET.setValue(true);
                SwingUiSettings.WINDOW_X.setValue(lastState.x);
                SwingUiSettings.WINDOW_Y.setValue(lastState.y);
                lastState = null;
            }
        }
    }    
   
    /** simple state. */
    private static class WindowState {
        private final int x;
        private final int y;
        private final long time;
        WindowState() {
            x = SwingUiSettings.WINDOW_X.getValue();
            y = SwingUiSettings.WINDOW_Y.getValue();
            time = System.currentTimeMillis();
        }
    }
}
