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

import java.awt.Container;
import java.awt.Dimension;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Map;

import javax.swing.Action;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.SwingUtilities;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
import com.sun.javatest.util.DynamicArray;
import com.sun.javatest.util.PrefixMap;

class SDIDeskView extends DeskView {

    SDIDeskView(Desktop desktop) {
	super(desktop);
	initFrames();
	uif.setDialogParent(console);
	JDialog.setDefaultLookAndFeelDecorated(false);
    }


    SDIDeskView(DeskView other) {
	this(other.getDesktop());

	Tool[] tools = other.getTools();
	for (int i = 0; i < tools.length; i++) 
	    addTool(tools[i]);

	doCascade();
	setVisible(other.isVisible());
    }

    public void dispose() {
	for (int i = 0; i < frames.length; i++)
	    frames[i].setVisible(false);

	for (int i = 0; i < frames.length; i++)
	    frames[i].dispose();

	super.dispose();
    }

    public boolean isVisible() {
	return visible;
    }

    public void setVisible(boolean v) {
	//System.err.println("SDI: setVisible: " + v);
	if (v != visible) {
	    for (int i = 0; i < frames.length; i++) {
		JFrame f = frames[i];
		f.setVisible(v);
		if (v) {
		    // make sure we don't make frames visible in front of dialogs
		    // (you'd think JDK would do this...)
		    Window[] ww = f.getOwnedWindows();
		    for (int wwi = 0; wwi < ww.length; wwi++)
			ww[wwi].toFront();
		}
	    }
	    visible = v;
	}
    }

    public boolean isEmpty() {
	return (frames.length == 1);
    }

    public Tool[] getTools() {
	// frames[0] is the console
	Tool[] tools = new Tool[frames.length - 1];
	for (int i = 0; i < tools.length; i++) 
	    tools[i] = (Tool) (frames[i + 1].getContentPane());
	return tools;
    }

    public void addTool(final Tool t) {
	DeskView view = t.getDeskView();
	if (view == this)
	    return;

	// save info about dialogs before we remove tool from other view
	ToolDialog[] tds = t.getToolDialogs();
	boolean[] vis = new boolean[tds.length];
	for (int i = 0; i < tds.length; i++)
	    vis[i] = tds[i].isVisible();

	// remove tool from other view (if any)
	if (view != null)
	    view.removeTool(t);

	//System.err.println("SDI: add " + t);
	Action closeAction = (new ToolAction(uif, "sdi.file.close") {
	    public void actionPerformed(ActionEvent e) {
		removeTool(t);
		t.dispose();
	    }
	});
	JFrame f = createFrame(listener, closeAction, "sdi.tool");
	f.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
	addToolMenuItemsToFrameMenuBar(f, t);
	f.setTitle(uif.getI18NString("dt.title.tool.txt", t.getTitle()));
	f.setContentPane(t);
	f.pack();
	t.addObserver(listener);
	f.addWindowListener(new WindowAdapter() {
	    public void windowActivated(WindowEvent e) {
		JFrame f = (JFrame) (e.getSource());
		if (f.getContentPane() instanceof Tool) {
                    Tool t = (Tool) (f.getContentPane());
                    selectedTool = t;
                }
	    }

	    public void windowDeactivated(WindowEvent e) {
		selectedTool = null;
	    }

	    public void windowClosing(WindowEvent e) { 
		JFrame f = (JFrame) (e.getSource());
		if (f.getContentPane() instanceof Tool) {
                    Tool t = (Tool) (f.getContentPane());
                    if (getDesktop().isOKToClose(t, f))
                        f.dispose();
                }
	    }

	    public void windowClosed(WindowEvent e) { 
		JFrame f = (JFrame) (e.getSource());
		//removeToolMenuItemsFromFrameMenuBar(f);
		frames = (JFrame[]) (DynamicArray.remove(frames, f));

		if (f.getContentPane() instanceof Tool) {
		    Tool t = (Tool) (f.getContentPane());
		    t.removeObserver(listener);
		    t.dispose();
		    
		    //f.dispose();  
		    f.getRootPane().removeAll();

		    if (t == selectedTool)
			selectedTool = null;
		}
	    }
	});

	//Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
	Dimension size = f.getSize();
	GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
	f.setLocation(ge.getCenterPoint().x - size.width/2,
		      ge.getCenterPoint().y - size.height/2);
	//f.setLocation(screenSize.width/2 - size.width/2, screenSize.height/2 - size.height/2);
	f.setVisible(visible);

	frames = (JFrame[]) (DynamicArray.append(frames, f));
	
	t.setDeskView(this);

	// update tool dialogs
	for (int i = 0; i < tds.length; i++)
	    tds[i].initDialog(this, vis[i]);
    }

    public void removeTool(Tool t) {
	//System.err.println("SDI: remove " + t);
	JFrame f = getFrameForTool(t);
	frames = (JFrame[]) (DynamicArray.remove(frames, f));    // remove it from list of frames
	f.setVisible(false);                          // hide it before we start disassembling it

	removeToolMenuItemsFromFrameMenuBar(f, t);
	f.setContentPane(new Container()); // avoid the tool getting disposed along with the frame
	f.dispose();
	// other housekeeping done in window listener to ensure
	// that it executes however the tool is closed
	t.setDeskView(null);
    }

    public Tool getSelectedTool() {
	//System.err.println("SDI: selected tool " + selectedTool);
	return selectedTool;
    }

    public void setSelectedTool(Tool t) {
	JFrame f = getFrameForTool(t);
	if (f.getState() == JFrame.ICONIFIED)
	    f.setState(JFrame.NORMAL);
	//f.toFront();
	f.setVisible(visible);
	// selectedTool set when the window is activated -- there appears to
	// be no way to query directly which window has focus
    }

    public int getStyle() {
	return Desktop.SDI_STYLE;
    }

    public JFrame[] getFrames() {
	return frames;
    }

    public Rectangle getBounds() {
	Rectangle bounds = null;
	// start loop at 0 to include console, start at 1 to ignore it
	for (int i = 1; i < frames.length; i++) {
	    Rectangle r = frames[i].getBounds();
	    bounds = (bounds == null ? r : bounds.union(r));
	}
	return (bounds == null ? getDefaultBounds() : bounds);
    }

    public boolean isToolOwnerForDialog(Tool tool, Container dialog) {
	if (dialog == null)
	    return false;

	JFrame f = (JFrame) (SwingUtilities.getAncestorOfClass(JFrame.class, tool));
	return (dialog.getParent() == f);
    }

    public Container createDialog(Tool tool, String uiKey, String title, 
				  JMenuBar menuBar, Container body,
				  Rectangle bounds) {
	JFrame f = (JFrame) (SwingUtilities.getAncestorOfClass(JFrame.class, tool));

	UIFactory uif = tool.uif;
	JDialog d = uif.createDialog(uiKey, f, title, body);
	if (menuBar != null)
	    d.setJMenuBar(menuBar);

	if (bounds == null) {
	    d.pack();
	    // for some reason the first call of pack seems to yield small results
	    // so we need to pack it again to get the real results.  Additional calls
	    // seem to have no effect, so after 2 calls we seem to have stable results.
	    d.pack();
	    d.setLocationRelativeTo(f);
	}
	else 
	    d.setBounds(bounds);

	return d;
    }

    protected void saveDesktop(Map m) {
	saveBounds(console, new PrefixMap(m, "dt.console"));
	saveTools(m);
    }

    protected void restoreDesktop(Map m) {
	restoreBounds(console, new PrefixMap(m, "dt.console"));
	restoreTools(m);
    }

    protected void saveTool(Map m, Tool t) {
	super.saveTool(m, t);
	saveBounds(getFrameForTool(t), new PrefixMap(m, "dt"));
    }

    protected Tool restoreTool(Map m, String name) throws Fault, ToolManager.Fault
    {
	Tool t = super.restoreTool(m, name);
	restoreBounds(getFrameForTool(t), new PrefixMap(m, "dt"));
	return t;
    }

    private void initFrames() {
	console = createFrame(listener, "sdi.console");
	console.setTitle(uif.getI18NString("dt.title.console"));
	console.addWindowListener(new WindowAdapter() {
	    public void windowClosing(WindowEvent e) {
		getDesktop().checkToolsAndExitIfOK(console);
	    }
	});
	JLabel logo = new JLabel(getDesktop().getLogo());
	logo.setName("sdi.console");
	logo.setFocusable(false);
	logo.setOpaque(true);
	console.setContentPane(logo);
	console.pack();
	frames = new JFrame[] { console };
    }

    private void doCascade() {
	final int offset = 30;
	// first find width of cascaded set
	int maxWidth = 0;
	int maxHeight = 0;
	// start at 0 to include console, start at 1 for just tools
	for (int i = 1; i < frames.length; i++) {
	    JFrame f = frames[i];
	    Dimension fSize = f.getSize();
	    maxWidth = Math.max(maxWidth, i * offset + fSize.width);
	    maxHeight = Math.max(maxHeight, i * offset + fSize.height);
	}
	//Dimension dSize = Toolkit.getDefaultToolkit().getScreenSize();  
	GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();

	int offsetX = (ge.getCenterPoint().x - maxWidth/2);
	int offsetY = (ge.getCenterPoint().y - maxHeight/2);

	// make sure we start on-screen
	if (offsetX <= 0)
	    offsetX = offset;

	if (offsetY <= 0)
	    offsetY = offset;

	// start at 0 to include console, start at 1 for just tools
	for (int i = 1; i < frames.length; i++) {
	    JFrame f = frames[i];
	    f.setLocation(offsetX + i * offset, offsetY + i * offset);
	    f.toFront();
	    Window[] ww = f.getOwnedWindows();
	    for (int wwi = 0; wwi < ww.length; wwi++)
		ww[wwi].toFront();
	}
    }

    private void doTile() {
	// start at 0 to include console, start at 1 for just tools
	int n = frames.length - 1;
	int cols = (int) Math.sqrt(n);
	int rows = (n + cols - 1) / cols;
	Dimension dSize = Toolkit.getDefaultToolkit().getScreenSize();
	Dimension tSize = new Dimension(dSize.width / cols, dSize.height / rows);
	for (int i = 0; i < n; i++) {
	    // start at 0 to include console, start at 1 for just tools
	    JFrame f = frames[i + 1];
	    int c = (i % cols);
	    int r = (i / cols);
	    f.setBounds(c * tSize.width, r * tSize.height, tSize.width, tSize.height);
	    //f.validate();
	}
    }

    private static JFrame getFrameForTool(Tool t) {
	return (JFrame) SwingUtilities.getAncestorOfClass(JFrame.class, t);
    }

    private static final int MENU_INSERT_POINT = 3; // before Windows, glue and Help

    private JFrame console;
    private JFrame[] frames;
    private boolean visible;
    private Tool selectedTool;
    private Listener listener = new Listener();

    private static final String TILE = "tile";
    private static final String CASCADE = "cascade";

    private class Listener 
	implements ActionListener, MenuListener, Tool.Observer 
    {
	// --------- ActionListener  ---------
	
	public void actionPerformed(ActionEvent e) {
	    String cmd = e.getActionCommand();
	    if (cmd.equals(CASCADE))
		doCascade();
	    else if (cmd.equals(TILE))
		doTile();
	    else {
		JMenuItem mi = (JMenuItem) (e.getSource());
		Object o = mi.getClientProperty(this);
		if (o instanceof Window)
		    ((Window) o).toFront();
		else if (o instanceof Tool)
		    setSelectedTool((Tool) o);
	    }
	}
	
	// ---------- WindowAdapter ----------
	/*
	public void windowClosed(WindowEvent e) {
	    JFrame src = (JFrame) (e.getSource());
	    removeToolMenuItemsFromFrameMenuBar(src);
	    }*/
	
	// --------- MenuListener ---------

	public void menuSelected(MenuEvent e) {
	    Tool[] tools = getTools();

	    JMenu m = (JMenu) (e.getSource());
	    m.removeAll();
	    JMenuItem tmi = uif.createMenuItem("dt.windows", TILE, this);
	    tmi.setEnabled(tools.length > 0);
	    m.add(tmi);
	    JMenuItem cmi = uif.createMenuItem("dt.windows", CASCADE, this);
	    cmi.setEnabled(tools.length > 0);
	    m.add(cmi);
	    m.addSeparator();

            /*
	    JMenu winOpenMenu = getWindowOpenMenu();
	    if (winOpenMenu.getItemCount() > 0) {
		m.add(getWindowOpenMenu());
		m.addSeparator();
	    }
            */

	    int n = 0;

	    // add entries for all current tools
	    for (int i = 0; i < tools.length; i++) {
		Tool tool = tools[i];
		addMenuItem(m, n++, tool.getTitle(), tool);
	    }

	    // add entries for any dialogs
	    for (int f = 0; f < frames.length; f++) {
		Window[] ownedWindows = frames[f].getOwnedWindows();
		for (int i = 0; i < ownedWindows.length; i++) {
		    Window w = ownedWindows[i];
		    if (w instanceof JDialog && w.isVisible()) 
			addMenuItem(m, n++, ((JDialog) w).getTitle(), w);
		}
	    }

	    // add the main console
	    addMenuItem(m, n++, console.getTitle(), console);
	}

	private void addMenuItem(JMenu m, int n, String s, Object o) {
	    JMenuItem mi = new JMenuItem(uif.getI18NString("dt.windows.toolX.mit", 
						 new Object[] { new Integer(n), s }));
	    if (n < 10) 
		mi.setMnemonic(Character.forDigit(n, 10));
	    mi.addActionListener(this);
	    mi.putClientProperty(this, o);
	    m.add(mi);
	}

	public void menuDeselected(MenuEvent e) {
	}

	public void menuCanceled(MenuEvent e) {
	}
	
	// ---------- Tool.Observer ----------
	
	public void shortTitleChanged(Tool src, String newValue) {
	}
	
	public void titleChanged(Tool src, String newValue) {
	    JFrame f = getFrameForTool(src);
	    f.setTitle(uif.getI18NString("dt.title.tool.txt", newValue));
	    //System.err.println("Tool title changed: " + newValue);
	}

	public void toolDisposed(Tool src) { }
    };
}
