/*
 * $Id$
 *
 * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */
package com.sun.javatest.util;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Image;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.awt.SystemColor;
import java.util.Locale;

/**
 * This class provides a means whereby tools in Personal Java can
 * temporarily give access to a shared GUI frame, such as the
 * one top-level Frame.
 */
public class MainFrame
{
    /**
     * Set a context that can be subsequently accessed via @link(#acquireFrame)
     * and @link(#releaseFrame).
     * @param c The container to be registered
     * @throws ClassCastException if the container being registered is not a frame
     * @see #getContext
     * @deprecated replaced by @link(#setFrame)
     */
    public static synchronized void setContext(Container c) {
        setFrame((Frame)c);
    }

    /**
     * Set a frame that can be subsequently accessed via @link(#acquireFrame)
     * and @link(#releaseFrame).
     * @param f The container to be registered
     */
    public static synchronized void setFrame(Frame f) {
        frame = f;
    }



    /**
     * Try to acquire exclusive access to a shared context previously registered
     * with @link(#setFrame). If the frame is currently in use by someone
     * else, the call will wait until the other owner is releases it.
     * @return The container previously registered with setFrame. It will
     * be returned in as clean a state as possible, with no children, and
     * a new instance of @link(FlowLayout). null is returned if the call is
     * interrupted.
     * @see #setContext
     * @deprecated replaced by acquireFrame.
     */
    public static Container getContext() {
        try {
            return acquireFrame();
        }
        catch (InterruptedException e) {
            return null;
        }
    }

    /**
     * Try to acquire exclusive access to a shared context previously registered
     * with @link(#setContext). If the context is currently in use by someone
     * else, the call will wait until the other owner is releases it.
     * @return The container previously registered with setContext. It will
     * be returned in as clean a state as possible, with no children, and
     * a new instance of @link(FlowLayout).
     * If no frame has been registered, one has been created.
     * @throws InterruptedException if the thread is interrupted while waiting
     * for the context to become available.
     * @see #releaseFrame
     */
    public static synchronized Frame acquireFrame() throws InterruptedException {
        if (frame == null) {
            frame = new Frame("JT Harness Default Frame");
            initialTitle = frame.getTitle();
            initialState = frame.getState();
            initialIconImage = frame.getIconImage();
            initialCursor = frame.getCursor();
            initialFont = frame.getFont();
            initialLocale = frame.getLocale();
            initialResizable = frame.isResizable();
            initialEnabled = frame.isEnabled();
        }

        while (inUse)
            // note: MainFrame.class provides the monitor for a static synchronized
            // method in this class.
            MainFrame.class.wait();

        inUse = true;

        // save state ... should all be free of side effects
        // the properties are all available in PBP 1.0, which is
        // currently the lowest common denominator for all MainFrame users.
        savedLayout = frame.getLayout();
        savedComponents = frame.getComponents();
        savedName = frame.getName();
        savedBounds = frame.getBounds();
        Color bg = frame.getBackground();
        savedBackground = (bg == null ? SystemColor.window : bg);
        Color fg = frame.getForeground();
        savedForeground = (fg == null ? SystemColor.windowText : fg);
        savedTitle = frame.getTitle();
        savedState = frame.getState();
        savedIconImage = frame.getIconImage();
        savedCursor = frame.getCursor();
        savedFont = frame.getFont();
        savedLocale = frame.getLocale();
        savedResizable = frame.isResizable();
        savedEnabled = frame.isEnabled();

        // now we want to clear the context ... do it carefully
        // set the layout to null while we remove the components
        // so as not to affect the savedlayoutManager
        frame.setLayout(null);
        frame.removeAll();
        frame.setLayout(new FlowLayout());
        frame.setTitle(initialTitle);
        frame.setState(initialState);
        frame.setIconImage(initialIconImage);
        frame.setCursor(initialCursor);
        frame.setFont(initialFont);
        frame.setLocale(initialLocale);
        frame.setResizable(initialResizable);
        frame.setEnabled(initialEnabled);

        return frame;
    }


    /**
     * Release access to the previously acquired context. The context is reset
     * to its state before  @link(#acquireContext) was called, and made available
     * to subsequent callers of @link(#acquireContext).
     * @param c The result of previously calling @link(#acquireContext).
     * @throws IllegalStateException if the argument is not the result of
     * calling @link(#acquireFrame), or if it has already been released.
     * @throws ClassCastException if the container is not a frame.
     * @deprecated replaced by releaseFrame.
     */
    public static void restoreContext(Container c) {
        releaseFrame((Frame)c);
    }



    /**
     * Release access to the previously acquired frame. The frame is reset
     * to its state before  @link(#acquireFrame) was called, and made available
     * to subsequent callers of @link(#acquireFrame).
     * @param f The result of previously calling @link(#acquireFrame).
     * @throws IllegalStateException if the argument is not the result of
     * calling @link(#acquireFrame), or if it has already been released.
     * @see #acquireFrame
     */
    public static synchronized void releaseFrame(Frame f) {
        if (f == null)
            return;

        if (f != frame)
            throw new IllegalStateException("wrong frame");

        if (!inUse)
            throw new IllegalStateException("frame not acquired");

        // remove what might have been put in the frame
        frame.setLayout(null);
        frame.removeAll();

        // Restore previously saved stuff, do as much as possible
        // with layout manager set to null, to minimise side effects.
        frame.setForeground(savedForeground);
        frame.setBackground(savedBackground);
        frame.setBounds(savedBounds);
        frame.setName(savedName);
        for (int i = 0; i < savedComponents.length; i++)
            frame.add(savedComponents[i]);
        frame.setLayout(savedLayout);
        frame.setTitle(savedTitle);
        frame.setState(savedState);
        frame.setIconImage(savedIconImage);
        frame.setCursor(savedCursor);
        frame.setFont(savedFont);
        frame.setLocale(savedLocale);
        frame.setResizable(savedResizable);
        frame.setEnabled(savedEnabled);

        inUse = false;
        MainFrame.class.notify();
    }

    private static Frame frame;
    private static boolean inUse;

    // saved state of context while it is in use by others
    private static LayoutManager savedLayout;
    private static Component[] savedComponents;
    private static String savedName;
    private static Rectangle savedBounds;
    private static Color savedBackground;
    private static Color savedForeground;
    private static String savedTitle;
    private static int savedState;
    private static Image savedIconImage;
    private static Cursor savedCursor;
    private static Font savedFont;
    private static Locale savedLocale;
    private static boolean savedResizable;
    private static boolean savedEnabled;

    private static String initialTitle;
    private static int initialState;
    private static Image initialIconImage;
    private static Cursor initialCursor;
    private static Font initialFont;
    private static Locale initialLocale;
    private static boolean initialResizable;
    private static boolean initialEnabled;
}
