package org.jcon.ui;

import java.awt.*;

import javax.swing.UIManager;

/**
 * General window related static methods.
 * <p> <pre>
 * Note we plan to add methods such as: ***
 * - Center one window over another
 * - Postion one window adjascent to another
 * - Postion window at upper center, etc </pre>
 *
 * @author Jack Harich
 */
public class WindowLib {

//---------- Public Fields -------------------------------
public static final int CENTER       = 1;
public static final int UPPER_LEFT   = 2;
public static final int UPPER_RIGHT  = 3;
public static final int LOWER_RIGHT  = 4;
public static final int LOWER_LEFT   = 5;
public static final int MIDDLE_LEFT  = 6;
public static final int MIDDLE_RIGHT = 7;
public static final int UPPER_CENTER = 8;

// Stretches window to screen height, puts on right
public static final int STRETCH_RIGHT = 9;

/**
* Centers the window over the active window
* This must be called before the window is shown, so that
* the active window can be found.
*/
//public static final int CENTER_OVER_ACTIVE = 10;

//---------- Private Fields ------------------------------
private static int defaultDown = 2;
private static int mainMenuHeight = -1;

//---------- Public Methods ------------------------------
public static void beep() {
    Toolkit.getDefaultToolkit().beep();
}
/**
 * Positions the window using the constants CENTER,
 * UPPER_LEFT, etc. Also enforces not larger than screen.
 * <p>
 * Using this method allows a window to easily have a
 * "Position" property.
 */
public static void position(Window win, int position) {
    //print(".position() - Entered, position = " + position);

    enforceNotLargerThanScreen(win);
    int x, y;
    int xRight = getScreenWidth() - win.getSize().width;
    int yLower = getScreenHeight() - win.getSize().height;
    switch (position ) {
        case CENTER:
            center(win);
            return;
        case UPPER_LEFT:
            x = 0; y = 0; break;
        case UPPER_RIGHT:
            x = xRight; y = 0; break;
        case LOWER_RIGHT:
            x = xRight; y = yLower; break;
        case LOWER_LEFT:
            x = 0; y = yLower; break;
        case MIDDLE_LEFT:
            x = 0; y = yLower / 2; break;
        case MIDDLE_RIGHT:
            x = xRight; y = yLower / 2; break;
        case UPPER_CENTER:
            x = (getScreenWidth() - win.getSize().width) / 2;
            y = 0; break;
        case STRETCH_RIGHT:
            x = xRight; y = 0;
            win.setSize(win.getSize().width, getScreenHeight());
            break;
        default:
            throw new IllegalArgumentException(
                "Unknown postion '" + position + "'.");
    }
    win.setLocation(x, y);
}
/**
 * Same as other position() except accepts a String
 * for the position, thus making parameter driven code
 * easier. The Strings are Center, UpperLeft, etc and are
 * case sensitive.
 */
public static void position(Window win, String position) {
    //print(".position() - Entered, position = " + position);
    String pos = position.intern();
    if (pos == "Center") {
        position(win, CENTER);

    } else if (pos == "UpperLeft") {
        position(win, UPPER_LEFT);

    } else if (pos == "UpperRight") {
        position(win, UPPER_RIGHT);

    } else if (pos == "LowerRight") {
        position(win, LOWER_RIGHT);

    } else if (pos == "LowerLeft") {
        position(win, LOWER_LEFT);

    } else if (pos == "MiddleLeft") {
        position(win, MIDDLE_LEFT);

    } else if (pos == "MiddleRight") {
        position(win, MIDDLE_RIGHT);
        
    } else if (pos == "UpperCenter") {
        position(win, UPPER_CENTER);        
        
    } else if (pos == "StretchRight") {
        position(win, STRETCH_RIGHT);        

    } else if (pos.startsWith("Active")) {
        String relativePosition = pos.substring(6);
        positionRelativeToActive(win, relativePosition);

    } else {
        throw new IllegalArgumentException(
            "Unknown postion '" + position + "'.");
    }
}
/**
 * Positions the window relative to the active Window,
 * which must be known to WindowMgr. Valid values for
 * relativePosition are Right, Left, Lower, Center (more later).
 * <p>
 * If the user moves a window and then opens another
 * window relative to it, the second window may be off the
 * screen. If the second window is modal, the application
 * is now hung. To prevent this we disallow putting more
 * than half the window off the screen by using
 * setLocationHalfOnScreen().
 * <p>
 * If there is no active Window then the window is centered.
 *
 * @param win  the window to position.
 * @param relativePostition  may be Right, Left, Lower
 *                           or Center.
 */
public static void positionRelativeToActive(Window win,
        String relativePosition) {

    //print(".positionRelativeToActive() - Entered: " + relativePosition);
    Window window = WindowMgr.getLastActiveWindow();
    //print(".positionRelativeToActive() - After getLastActiveWindow(): " + relativePosition);
    
    if (window == null) {
        center(win);
        return;
    }
    Rectangle activeBounds = window.getBounds();
    Rectangle winBounds = win.getBounds();
    int x, y;

    if (relativePosition.equals("Right")) {
        x = activeBounds.x + activeBounds.width;
        y = activeBounds.y;

    } else if (relativePosition.equals("Left")) {
        x = activeBounds.x - winBounds.width;
        y = activeBounds.y;
        
    } else if (relativePosition.equals("Lower")) {
        x = (getScreenWidth() - winBounds.width) / 2;
        y = activeBounds.y + activeBounds.height;    
        
    } else if (relativePosition.equals("LowerAlignLeft")) {
        x = activeBounds.x;
        y = activeBounds.y + activeBounds.height; 
        
    } else if (relativePosition.equals("Center")) {
        x = activeBounds.x + (activeBounds.width - winBounds.width) / 2;
        y = activeBounds.y + (activeBounds.height - winBounds.height) / 2;
    
    } else {
        throw new IllegalArgumentException(
            "Unknown relativePosition '" + relativePosition + "'.");
    }
    // *** win.setLocation(x, y);
    setLocationHalfOnScreen(win, x, y);
}
/**
* Set the window's location and ensures at least half of
* the window is on the screen for x and that the title bar
* is not above or below the screen
*/
public static void setLocationHalfOnScreen(
                            Window win, int x, int y) {
    // Check x                            
    int winHalfWidth = win.getSize().width / 2;                                
    if (x >= 0) {                                
        // Right side case
        if (x + winHalfWidth > getScreenWidth()) {
            x = getScreenWidth() - winHalfWidth;
        }
    } else {
        // Left side case
        if (x < (winHalfWidth * -1)) {
            x = winHalfWidth * -1;   
        }    
    }
    // Check y
    if (y < 0) {
        y = 0;
    } else if (y > (getScreenHeight() * 7) / 8) { // 7/8 is kludge, should check titlebar height
        y = getScreenHeight() / 2;    
    }
    // Set location
    win.setLocation(x, y);
}    
/**
 * Sets the down value used by centerWindow, which is
 * 2 by default. 2 centers the window perfectly.
 * <p>
 * The Microsoft standard is 1/3 the way down
 * for a "centered" window, so to meet that standard one
 * would call WindowLib.setCenterDefaultDown(3);
 * when starting an application.
 */
public static void setCenterDefaultDown(int down) {
    defaultDown = down;
}
/**
 * Centers the window using the defaultDown. If the
 * window is larger than the screen it is automatically
 * reduced to fit the screen.
 */
public static void center(Window win) {
    centerDown(win, defaultDown);
}
/**
 * Centers the window over the active window. If there is
 * no active window it is centered on the screen. It also
 * enforces not larger than screen.
 */
public static void centerOverActiveWindow(Window win) {
    //print(".centerOverActiveWindow() - Entered");
    enforceNotLargerThanScreen(win);
    position(win, "ActiveCenter");
}
/**
 * Behaves like center() except @param down is used
 * to determine the screen fraction down from the top.
 * For example centerWindow() uses 2 as down. Using 8
 * as down puts the window 1/8 the way down.
 */
public static void centerDown(Window win, int down) {
    enforceNotLargerThanScreen(win);
    // Center Frame, Dialogue or Window on screen
    int x = (getScreenWidth() - win.getSize().width) / 2;
    int y = (getScreenHeight() - win.getSize().height) / down;
    win.setLocation(x, y);
}
/**
 * If the window is larger than the screen in either
 * dimension (height or width) it will be resized to fit
 * within the screen.
 */
public static void enforceNotLargerThanScreen(Window window) {
    // If larger than screen width reduce window width
    if (getScreenWidth() < window.getSize().width) {
        window.setSize(getScreenWidth(), window.getSize().height);
    }
    // If larger than screen height reduce window height
    if (getScreenHeight() < window.getSize().height) {
        window.setSize(window.getSize().width, getScreenHeight());
    }
}
/**
 * If the component is smaller that the minWidth and/or
 * minHeight it will be resized accordingly. Useful for
 * enforcing a minimum window or widget size.
 */
public static void enforceMinimumSize(Component comp,
        int minWidth, int minHeight) {
    if (comp.getSize().width < minWidth) {
        comp.setSize(minWidth, comp.getSize().height);
    }
    if (comp.getSize().height < minHeight) {
        comp.setSize(comp.getSize().width, minHeight);
    }
}
/**
 * Returns the correct height for a "main menu" style Frame,
 * which has only a title and MenuBar.
 * <p>
 * This is An awt bug workaround and convenience method. If too
 * many Menus are in a MenuBar then pack() will cause an
 * excessive insets.top, apparently due to MenuBar wrapping.
 * By using only one Menu we get a correct insets.top,
 * which is the Frame height for a "main menu" Frame.
 */
public static int getMainMenuHeight() {
    if (mainMenuHeight < 0) {
        // Prepare main menu with one menu, pack()
        Frame  frame = new Frame();
        MenuBar menuBar = new MenuBar();
        menuBar.add(new Menu("First"));
        frame.setMenuBar(menuBar);
        frame.pack();
        // Now save that elusive height
        mainMenuHeight = frame.getInsets().top;
        frame.dispose();
    }
    return mainMenuHeight;
}
/**
 * Finds and returns the Window for the component.
 * Returns null if none.
 */
public static Window findWindow(Component component) {
    Container parent = component.getParent();
    while (parent != null) {
        if (parent instanceof Window) {
            return (Window)parent;
        } else {
            parent = parent.getParent();
        }
    }
    return null;
}

public static int getScreenWidth() {
    return Toolkit.getDefaultToolkit().getScreenSize().width;   
} 
public static int getScreenHeight() {
    return Toolkit.getDefaultToolkit().getScreenSize().height;   
}   
//--- Std
private static void print(String text) {
    System.out.println("WindowLib" + text);
}

} // End class
