package net.sourceforge.squirrel_sql.client.gui;
/*
 * Copyright (C) 2001-2003 Colin Bell
 * [email protected]
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.net.URL;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

import javax.management.MXBean;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.Timer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import net.sourceforge.squirrel_sql.client.IApplication;
import net.sourceforge.squirrel_sql.client.gui.builders.UIFactory;
import net.sourceforge.squirrel_sql.client.plugin.PluginInfo;
import net.sourceforge.squirrel_sql.client.resources.SquirrelResources;
import net.sourceforge.squirrel_sql.fw.datasetviewer.DataSetException;
import net.sourceforge.squirrel_sql.fw.datasetviewer.DataSetViewerTablePanel;
import net.sourceforge.squirrel_sql.fw.datasetviewer.HashtableDataSet;
import net.sourceforge.squirrel_sql.fw.gui.GUIUtils;
import net.sourceforge.squirrel_sql.fw.gui.PropertyPanel;
import net.sourceforge.squirrel_sql.fw.util.StringManager;
import net.sourceforge.squirrel_sql.fw.util.StringManagerFactory;
import net.sourceforge.squirrel_sql.fw.util.StringUtilities;
import net.sourceforge.squirrel_sql.fw.util.Utilities;
import net.sourceforge.squirrel_sql.fw.util.log.ILogger;
import net.sourceforge.squirrel_sql.fw.util.log.LoggerController;

import com.jgoodies.forms.builder.ButtonBarBuilder;
/**
 * About box dialog.
 *
 * @author <A HREF="mailto:[email protected]">Colin Bell</A>
 */
public class AboutBoxDialog extends JDialog
{
    private static final long serialVersionUID = 1L;

    /** Logger for this class. */
	private final static ILogger s_log =
		LoggerController.createLogger(AboutBoxDialog.class);

	/** Internationalized strings for this class. */
	private static final StringManager s_stringMgr =
		StringManagerFactory.getStringManager(AboutBoxDialog.class);

	/** Singleton instance of this class. */
	private static AboutBoxDialog s_instance;

	/** The tabbed panel. */
	private JTabbedPane _tabPnl;

	/** System panel. */
	private SystemPanel _systemPnl;
	
	private ThreadPanel _threadPnl;

	/** Close button for dialog. */
	private final JButton _closeBtn = new JButton(s_stringMgr.getString("AboutBoxDialog.close"));


	private AboutBoxDialog(IApplication app)
	{
		super(app.getMainFrame(), s_stringMgr.getString("AboutBoxDialog.about"), true);
		setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);
		createGUI(app);
	}

	/**
	 * Show the About Box.
	 *
	 * @param	app		Application API.
	 *
	 * @throws	IllegalArgumentException
	 * 			Thrown if a <TT>null</TT> <TT>IApplication</TT> object passed.
	 */
	public static synchronized void showAboutBox(IApplication app)
		throws IllegalArgumentException
	{
		if (app == null)
		{
			throw new IllegalArgumentException("Null IApplication passed");
		}
		if (s_instance == null)
		{
			s_instance = new AboutBoxDialog(app);
		}
		s_instance.setVisible(true);
	}

	private void createGUI(IApplication app)
	{
		final JPanel contentPane = new JPanel(new BorderLayout());
		setContentPane(contentPane);
		contentPane.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));

		final boolean isDebug = s_log.isDebugEnabled();
		long start = 0;

		_tabPnl = UIFactory.getInstance().createTabbedPane();

		if (isDebug)
		{
			start = System.currentTimeMillis();
		}
		_tabPnl.add(s_stringMgr.getString("AboutBoxDialog.about"), new AboutPanel(app));
		if (isDebug)
		{
            // i18n[AboutBoxDialog.aboutpanelcreatetime=AboutPanel created in ]
			s_log.debug(s_stringMgr.getString("AboutBoxDialog.aboutpanelcreatetime")
					+ (System.currentTimeMillis() - start)
					+ "ms");
		}

		if (isDebug)
		{
			start = System.currentTimeMillis();
		}
		_tabPnl.add(s_stringMgr.getString("AboutBoxDialog.credits"), new CreditsPanel(app)); // i18n
		if (isDebug)
		{
            // i18n[AboutBoxDialog.creditspanelcreatetime=CreditsPanel created in ]
			s_log.debug(s_stringMgr.getString("AboutBoxDialog.creditspanelcreatetime")
					+ (System.currentTimeMillis() - start)
					+ "ms");
		}

		if (isDebug)
		{
			start = System.currentTimeMillis();
		}
		_systemPnl = new SystemPanel();
		_tabPnl.add(s_stringMgr.getString("AboutBoxDialog.system"), _systemPnl);
		if (isDebug)
		{
            // i18n[AboutBoxDialog.systempanelcreatetime=SystemPanel created in ]
			s_log.debug(s_stringMgr.getString("AboutBoxDialog.systempanelcreatetime")
					+ (System.currentTimeMillis() - start)
					+ "ms");
		}
		
		_threadPnl = new ThreadPanel();
		_tabPnl.add(s_stringMgr.getString("AboutBoxDialog.threads"), _threadPnl);

		_tabPnl.addChangeListener(new ChangeListener()
		{
			public void stateChanged(ChangeEvent evt)
			{
				String title = _tabPnl.getTitleAt(_tabPnl.getSelectedIndex());
				if (title.equals(s_stringMgr.getString("AboutBoxDialog.system")))
				{
					_systemPnl._memoryPnl.startTimer();
				}
				else
				{
					_systemPnl._memoryPnl.stopTimer();
				}
			}
		});

		contentPane.add(_tabPnl, BorderLayout.CENTER);

		// Ok button at bottom of dialog.
//		JPanel btnsPnl = new JPanel();
//		JButton okBtn = new JButton("OK");

//		_closeBtn.addActionListener(new ActionListener()
//		{
//			public void actionPerformed(ActionEvent evt)
//			{
//				setVisible(false);
//			}
//		});
//		btnsPnl.add(okBtn);
//		contentPane.add(btnsPnl, BorderLayout.SOUTH);
		contentPane.add(createButtonBar(), BorderLayout.SOUTH);

		getRootPane().setDefaultButton(_closeBtn);

		addWindowListener(new WindowAdapter()
		{
			public void windowActivated(WindowEvent evt)
			{
				String title = _tabPnl.getTitleAt(_tabPnl.getSelectedIndex());
				if (title.equals(s_stringMgr.getString("AboutBoxDialog.system")))
				{
					_systemPnl._memoryPnl.startTimer();
				}
			}
			public void windowDeactivated(WindowEvent evt)
			{
				String title = _tabPnl.getTitleAt(_tabPnl.getSelectedIndex());
				if (title.equals(s_stringMgr.getString("AboutBoxDialog.system")))
				{
					_systemPnl._memoryPnl.stopTimer();
				}
			}
		});

		pack();
		GUIUtils.centerWithinParent(this);
		setResizable(true);
	}

	private JPanel createButtonBar()
	{
		_closeBtn.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent evt)
			{
				setVisible(false);
			}
		});

		final ButtonBarBuilder builder = new ButtonBarBuilder();
		builder.addGlue();
//		builder.addGridded(new JButton("Alter"));
//		builder.addRelatedGap();
		builder.addGridded(_closeBtn);

		return builder.getPanel();
	}

	private static final class CreditsPanel extends JScrollPane
	{
        private static final long serialVersionUID = 1L;

        CreditsPanel(IApplication app)
		{
			super();

			setBorder(BorderFactory.createEmptyBorder());

			final JEditorPane credits = new JEditorPane();
			credits.setEditable(false);
			credits.setContentType("text/html");

			// Required with the first beta of JDK1.4.1 to stop
			// this scrollpane from being too tall.
			credits.setPreferredSize(new Dimension(200, 200));

			String creditsHtml = readCreditsHtml(app);

			StringBuffer pluginHtml = new StringBuffer();
			// Get list of all plugin developers names. Allow for multiple
			// developers for a plugin in the form "John Smith, James Brown".
			PluginInfo[] pi = app.getPluginManager().getPluginInformation();
			for (int i = 0; i < pi.length; ++i)
			{
				pluginHtml.append("<br><b>").append(pi[i].getDescriptiveName()).append(":</b>");

				String authors = pi[i].getAuthor();
				StringTokenizer strok = new StringTokenizer(authors, ",");
				while (strok.hasMoreTokens())
				{
					pluginHtml.append("<br>").append(strok.nextToken().trim());
				}
				String contribs = pi[i].getContributors();
				strok = new StringTokenizer(contribs, ",");
				while (strok.hasMoreTokens())
				{
					pluginHtml.append("<br>").append(strok.nextToken().trim());
				}


				pluginHtml.append("<br>");
			}

			creditsHtml = creditsHtml.replaceAll("@@replace", pluginHtml.toString());
			credits.setText(creditsHtml);

			setViewportView(credits);
			credits.setCaretPosition(0);
		}

		private String readCreditsHtml(IApplication app)
		{
			final URL url = app.getResources().getCreditsURL();
			StringBuffer buf = new StringBuffer(2048);

			if (url != null)
			{
				try
				{
					BufferedReader rdr = new BufferedReader(new InputStreamReader(url.openStream()));
					try
					{
						String line = null;
						while ((line = rdr.readLine()) != null)
						{
							String internationalizedLine = 
								Utilities.replaceI18NSpanLine(line, s_stringMgr);
							buf.append(internationalizedLine);
						}
					}
					finally
					{
						rdr.close();
					}
				}
				catch (IOException ex)
				{
						  // i18n[AboutBoxDialog.error.creditsfile=Error reading credits file]
						  String errorMsg =
								s_stringMgr.getString("AboutBoxDialog.error.creditsfile");
					s_log.error(errorMsg, ex);
					buf.append(errorMsg + ": " + ex.toString());
				}
			}
			else
			{
					 // i18n[AboutBoxDialog.error.creditsfileurl=Couldn't retrieve Credits File URL]
					 String errorMsg =
						  s_stringMgr.getString("AboutBoxDialog.error.creditsfileurl");
				s_log.error(errorMsg);
				buf.append(errorMsg);
			}
			return buf.toString();
		}
				
	}

	private static final class AboutPanel extends JPanel
	{
        private static final long serialVersionUID = 1L;

        AboutPanel(IApplication app)
		{
			super();
			final SquirrelResources rsrc = app.getResources();
			setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
			setLayout(new BorderLayout());
			setBackground(new Color(SquirrelResources.S_SPLASH_IMAGE_BACKGROUND));
			Icon icon = rsrc.getIcon(SquirrelResources.IImageNames.SPLASH_SCREEN);
			add(BorderLayout.CENTER, new JLabel(icon));
            
            VersionPane versionPane = new VersionPane(true);
            versionPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
            add(BorderLayout.SOUTH, versionPane);
		}
	}

	private static final class SystemPanel extends JPanel
	{
        private static final long serialVersionUID = 1L;
        private MemoryPanel _memoryPnl;

		SystemPanel()
		{
			super();
			setLayout(new BorderLayout());
			DataSetViewerTablePanel propsPnl = new DataSetViewerTablePanel();
			propsPnl.init(null);
			try
			{
				propsPnl.show(new HashtableDataSet(System.getProperties()));
			}
			catch (DataSetException ex)
			{
                // i18n[AboutBoxDialog.error.systemprops=Error occured displaying System Properties]
				s_log.error(s_stringMgr.getString("AboutBoxDialog.error.systemprops"), ex);
			}

			_memoryPnl = new MemoryPanel();
			add(new JScrollPane(propsPnl.getComponent()), BorderLayout.CENTER);
			add(_memoryPnl, BorderLayout.SOUTH);

			//setPreferredSize(new Dimension(400, 400));
		}
	}

	
	
	private static class MemoryPanel
		extends PropertyPanel
		implements ActionListener
	{
        private static final long serialVersionUID = 1L;
        private final JLabel _totalMemoryLbl = new JLabel();
		private final JLabel _usedMemoryLbl = new JLabel();
		private final JLabel _freeMemoryLbl = new JLabel();
		private Timer _timer;

		MemoryPanel()
		{
			super();
			add(new JLabel(s_stringMgr.getString("AboutBoxDialog.heapsize")), _totalMemoryLbl);
			add(new JLabel(s_stringMgr.getString("AboutBoxDialog.usedheap")), _usedMemoryLbl);
			add(new JLabel(s_stringMgr.getString("AboutBoxDialog.freeheap")), _freeMemoryLbl);

			JButton gcBtn = new JButton(s_stringMgr.getString("AboutBoxDialog.gc"));
			gcBtn.addActionListener(new ActionListener()
			{
				public void actionPerformed(ActionEvent evt)
				{
					Utilities.garbageCollect();
				}
			});
			add(gcBtn, new JLabel(""));
		}

		public void removeNotify()
		{
			super.removeNotify();
			stopTimer();
		}

		/**
		 * Update component with the current memory status.
		 *
		 * @param	evt		The current event.
		 */
		public void actionPerformed(ActionEvent evt)
		{
			updateMemoryStatus();
		}

		synchronized void startTimer()
		{
			if (_timer == null)
			{
                // i18n[AboutBoxDialog.info.startmemtime=Starting memory timer (AboutBox)]
				s_log.debug(s_stringMgr.getString("AboutBoxDialog.info.startmemtime"));
				//_thread = new Thread(new MemoryTimer());
				//_thread.start();
				updateMemoryStatus();
				_timer = new Timer(2000, this);
				_timer.start();
			}
		}

		synchronized void stopTimer()
		{
			if (_timer != null)
			{
                // i18n[AboutBoxDialog.info.endmemtimer=Ending memory timer (AboutBox)]
				s_log.debug(s_stringMgr.getString("AboutBoxDialog.info.endmemtimer"));
				_timer.stop();
				_timer = null;
			}
		}

		private void updateMemoryStatus()
		{
			Runtime rt = Runtime.getRuntime();
			final long totalMemory = rt.totalMemory();
			final long freeMemory = rt.freeMemory();
			final long usedMemory = totalMemory - freeMemory;
			_totalMemoryLbl.setText(Utilities.formatSize(totalMemory, 1));
			_usedMemoryLbl.setText(Utilities.formatSize(usedMemory, 1));
			_freeMemoryLbl.setText(Utilities.formatSize(freeMemory, 1));
		}
	}
	
	private static final class ThreadPanel extends JPanel
	{
        private static final long serialVersionUID = 1L;
        
        private JTextArea content;

        ThreadPanel()
		{
			super();
			setLayout(new BorderLayout());
			
			content = new JTextArea(5,20);
			content.setEditable(false);
			content.setLineWrap(false);
			doThreadDump();
			add(new JScrollPane(content), BorderLayout.CENTER);
			add(createButtons(), BorderLayout.SOUTH);
		}

		/**
		 * @return
		 */
		private JPanel createButtons() {
			JPanel buttonPanel = new JPanel(new BorderLayout());
			JButton refreshButton = new JButton(s_stringMgr.getString("ThreadPanel.refresh"));
			buttonPanel.add(refreshButton, BorderLayout.WEST);
			
			refreshButton.addActionListener(new ActionListener() {
				
				@Override
				public void actionPerformed(ActionEvent e) {
					doThreadDump();
				}
			});
			
			return buttonPanel;
		}

		private void doThreadDump() {
			StringBuilder sb = new StringBuilder(1000);

			ThreadInfo[] threadInfos = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true);
			for (ThreadInfo threadInfo : threadInfos) {
				sb.append(threadInfo.toString());
				sb.append(StringUtilities.getEolStr());
			}

			content.setText(sb.toString());
		}
	}
}