/*
 * Created on Mar 20, 2006 6:40:14 PM
 * Copyright (C) 2006 Aelitis, All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * This program 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 for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * AELITIS, SAS au capital de 46,603.30 euros
 * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
 */
package org.gudy.azureus2.ui.swt.mainwindow;

import java.text.NumberFormat;
import java.util.ArrayList;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;

import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.config.ParameterListener;
import org.gudy.azureus2.core3.config.impl.TransferSpeedValidator;
import org.gudy.azureus2.core3.global.GlobalManager;
import org.gudy.azureus2.core3.global.GlobalManagerStats;
import org.gudy.azureus2.core3.internat.MessageText;
import org.gudy.azureus2.core3.ipfilter.IpFilter;
import org.gudy.azureus2.core3.logging.LogAlert;
import org.gudy.azureus2.core3.stats.transfer.OverallStats;
import org.gudy.azureus2.core3.stats.transfer.StatsFactory;
import org.gudy.azureus2.core3.util.*;
import org.gudy.azureus2.plugins.PluginInterface;
import org.gudy.azureus2.plugins.PluginManager;
import org.gudy.azureus2.plugins.network.ConnectionManager;
import org.gudy.azureus2.plugins.ui.config.ConfigSection;
import org.gudy.azureus2.pluginsimpl.local.PluginInitializer;
import org.gudy.azureus2.ui.swt.*;
import org.gudy.azureus2.ui.swt.Alerts.AlertHistoryListener;
import org.gudy.azureus2.ui.swt.progress.*;
import org.gudy.azureus2.ui.swt.shells.CoreWaiterSWT;
import org.gudy.azureus2.ui.swt.shells.GCStringPrinter;
import org.gudy.azureus2.ui.swt.update.UpdateWindow;
import org.gudy.azureus2.ui.swt.views.stats.StatsView;

import com.aelitis.azureus.core.AzureusCore;
import com.aelitis.azureus.core.AzureusCoreFactory;
import com.aelitis.azureus.core.AzureusCoreRunningListener;
import com.aelitis.azureus.core.dht.DHT;
import com.aelitis.azureus.core.networkmanager.NetworkManager;
import com.aelitis.azureus.plugins.dht.DHTPlugin;
import com.aelitis.azureus.ui.UIFunctions;
import com.aelitis.azureus.ui.UIFunctionsManager;
import com.aelitis.azureus.ui.UIStatusTextClickListener;
import com.aelitis.azureus.ui.common.updater.UIUpdatable;
import com.aelitis.azureus.ui.swt.UIFunctionsManagerSWT;
import com.aelitis.azureus.ui.swt.imageloader.ImageLoader;

/**
 * Moved from MainWindow and GUIUpdater
 */
public class MainStatusBar
	implements UIUpdatable
{
	/**
	 * Warning status icon identifier
	 */
	private static final String STATUS_ICON_WARN = "sb_warning";
	
	private static final String ID = "MainStatusBar";

	private AEMonitor this_mon = new AEMonitor(ID);

	private UpdateWindow updateWindow;

	private Composite statusBar;

	private CLabel statusText;

	private String statusTextKey = "";

	private String statusImageKey = null;

	private Image statusImage = null;

	private AZProgressBar progressBar;

	private CLabelPadding ipBlocked;

	private CLabelPadding srStatus;

	private CLabelPadding natStatus;

	private CLabelPadding dhtStatus;

	private CLabelPadding statusDown;

	private CLabelPadding statusUp;

	private Composite plugin_label_composite;
	
	private ArrayList<Runnable> listRunAfterInit = new ArrayList<Runnable>();

	private Display display;

	// For Refresh..
	private long last_sr_ratio = -1;

	private int last_sr_status = -1;

	private int lastNATstatus = -1;

	private String lastNATimageID = null;

	private int lastDHTstatus = -1;

	private long lastDHTcount = -1;

	private NumberFormat numberFormat;

	private OverallStats overall_stats;

	private ConnectionManager connection_manager;

	private DHTPlugin dhtPlugin;

	private UIFunctions uiFunctions;

	private UIStatusTextClickListener clickListener;

	//	 final int borderFlag = (Constants.isOSX) ? SWT.SHADOW_NONE : SWT.SHADOW_IN;
	private static final int borderFlag = SWT.SHADOW_NONE;

	/**
	 * Just a flag to differentiate az3 from other versions; default status bar text is handled differently between versions.
	 * Specifically speaking the Vuze UI status text is just empty whereas the Classic UI status text has an icon
	 * and the application version number.
	 */
	private boolean isAZ3 = false;

	/**
	 * Just a reference to the static <code>ProgressReportingManager</code> to make the code look cleaner instead of
	 * using <code>ProgressReportingManager.getInstance().xxx()</code> everywhere.
	 */
	private ProgressReportingManager PRManager = ProgressReportingManager.getInstance();

	/**
	 * A <code>GridData</code> for the progress bar; used to dynamically provide .widthHint to the layout manager
	 */
	private GridData progressGridData = new GridData(SWT.RIGHT, SWT.CENTER,
			false, false);

	/**
	 * A clickable image label that brings up the Progress viewer 
	 */
	private CLabelPadding progressViewerImageLabel;

	private String lastProgressImageID = null;

	private boolean updateProgressBarDisplayQueued = false;

	protected IProgressReport latestReport = null;

	protected AEMonitor latestReport_mon = new AEMonitor("latestReport");

	private String lastSRimageID = null;

	private int last_dl_limit;

	private long last_rec_data = - 1;

	private long last_rec_prot;

	private long[] max_rec = { 0 };
	private long[] max_sent = { 0 };

	private Image imgRec;
	private Image imgSent;

	private Image	warningIcon;
	private Image	warningGreyIcon;
	private Image	infoIcon;
	
	private CLabelPadding statusWarnings;

	/**
	 * 
	 */
	public MainStatusBar() {
		numberFormat = NumberFormat.getInstance();
		// Proably need to wait for core to be running to make sure dht plugin is fully avail
		AzureusCoreFactory.addCoreRunningListener(new AzureusCoreRunningListener() {
			public void azureusCoreRunning(AzureusCore core) {
				PluginManager pm = core.getPluginManager();
				connection_manager = PluginInitializer.getDefaultInterface().getConnectionManager();
				PluginInterface dht_pi = pm.getPluginInterfaceByClass(DHTPlugin.class);
				if (dht_pi != null) {
					dhtPlugin = (DHTPlugin) dht_pi.getPlugin();
				}
			}
		});
	}

	/**
	 * 
	 * @return composite holding the statusbar
	 */
	public Composite initStatusBar(final Composite parent) {
		this.display = parent.getDisplay();
		this.uiFunctions = UIFunctionsManager.getUIFunctions();
		ImageLoader imageLoader = ImageLoader.getInstance();

		FormData formData;

		Color fgColor = parent.getForeground();

		statusBar = new Composite(parent, SWT.NONE);
		statusBar.setForeground(fgColor);
		isAZ3 = "az3".equalsIgnoreCase(COConfigurationManager.getStringParameter("ui"));
		
		statusBar.getShell().addListener(SWT.Deiconify, new Listener() {
			public void handleEvent(Event event) {
				Utils.execSWTThreadLater(0, new AERunnable() {
					public void runSupport() {
						if (!statusBar.isDisposed()) {
							statusBar.layout();
						}
					}
				});
			}
		});
		
		GridLayout layout_status = new GridLayout();
		layout_status.numColumns = 20;
		layout_status.horizontalSpacing = 0;
		layout_status.verticalSpacing = 0;
		layout_status.marginHeight = 0;
		if (Constants.isOSX) {
			// OSX has a resize widget on the bottom right.  It's about 15px wide.
			try {
				layout_status.marginRight = 15;
			} catch (NoSuchFieldError e) {
				// Pre SWT 3.1 
				layout_status.marginWidth = 15;
			}
		} else {
			layout_status.marginWidth = 0;
		}
		statusBar.setLayout(layout_status);

		//Either the Status Text
		statusText = new CLabel(statusBar, borderFlag);
		statusText.setForeground(fgColor);
		statusText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL
				| GridData.VERTICAL_ALIGN_FILL));

		addStatusBarMenu(statusText);

		GC gc = new GC(statusText);
		// add 6, because CLabel forces a 3 pixel indent
		int height = Math.max(16, gc.getFontMetrics().getHeight()) + 6;
		gc.dispose();

		formData = new FormData();
		formData.height = height;
		formData.bottom = new FormAttachment(100, 0); // 2 params for Pre SWT 3.0
		formData.left = new FormAttachment(0, 0); // 2 params for Pre SWT 3.0
		formData.right = new FormAttachment(100, 0); // 2 params for Pre SWT 3.0
		statusBar.setLayoutData(formData);

		Listener listener = new Listener() {
			public void handleEvent(Event e) {
				if (clickListener == null) {
					if (updateWindow != null) {
						updateWindow.show();
					}
				} else {
					clickListener.UIStatusTextClicked();
				}
			}
		};

		statusText.addListener(SWT.MouseUp, listener);
		statusText.addListener(SWT.MouseDoubleClick, listener);

		// final int progressFlag = (Constants.isOSX) ? SWT.INDETERMINATE	: SWT.HORIZONTAL;
		// KN: Don't know why OSX is treated differently but this check was already here from the previous code
		if (true == Constants.isOSX) {
			progressBar = new AZProgressBar(statusBar, true);
		} else {
			progressBar = new AZProgressBar(statusBar, false);
		}

		progressBar.setVisible(false);
		progressGridData = new GridData(SWT.CENTER, SWT.CENTER, false, false);
		progressGridData.widthHint = 5;
		progressBar.setLayoutData(progressGridData);

		
		/*
		 * Progress reporting window image label
		 */

		progressViewerImageLabel = new CLabelPadding(statusBar, SWT.NONE);
		// image set below after adding listener
		progressViewerImageLabel.setToolTipText(MessageText.getString("Progress.reporting.statusbar.button.tooltip"));
		progressViewerImageLabel.addMouseListener(new MouseAdapter() {
			public void mouseDown(MouseEvent e) {
				/*
				 * Opens the progress viewer if any of the reporters in the array is NOT already opened
				 * KN: TODO -- This is only a partial solution to minimize the occurrence of the main progress window
				 * opening more than once.  The one remaining case where multiple windows will still open is
				 * when you have one opened already... then run another process such as a torrent file download...
				 * at this point this new process is not in the already opened window so the check would
				 * allow the second window to open.
				 */
				IProgressReporter[] reporters = PRManager.getReportersArray(false);
				if (reporters.length == 0) {
					/*
					 * If there's nothing to see then open the window; the default widow will say there's nothing to see
					 * KN: calling isShowingEmpty return true is there is already a window opened showing the empty panel
					 */
					if (false == ProgressReporterWindow.isShowingEmpty()) {
						ProgressReporterWindow.open(reporters,
								ProgressReporterWindow.SHOW_TOOLBAR);
					}
				} else {

					for (int i = 0; i < reporters.length; i++) {
						if (false == ProgressReporterWindow.isOpened(reporters[i])) {
							ProgressReporterWindow.open(reporters,
									ProgressReporterWindow.SHOW_TOOLBAR);
							break;
						}
					}
				}
			}
		});
		progressViewerImageLabel.addDisposeListener(new DisposeListener() {
			public void widgetDisposed(DisposeEvent e) {
				ImageLoader imageLoader = ImageLoader.getInstance();
				imageLoader.releaseImage("progress_error");
				imageLoader.releaseImage("progress_info");
				imageLoader.releaseImage("progress_viewer");
			}
		});

		this.plugin_label_composite = new Composite(statusBar, SWT.NONE);
		this.plugin_label_composite.setForeground(fgColor);
		GridLayout gridLayout = new GridLayout();
		gridLayout.horizontalSpacing = 0;
		gridLayout.verticalSpacing = 0;
		gridLayout.marginHeight = 0;
		gridLayout.marginBottom = 0;
		gridLayout.marginTop = 0;
		gridLayout.marginLeft = 0;
		gridLayout.marginRight = 0;
		gridLayout.numColumns = 20; // Something nice and big. :)

		GridData gridData = new GridData(GridData.FILL_VERTICAL);
		gridData.heightHint = height;
		gridData.minimumHeight = height;
		plugin_label_composite.setLayout(gridLayout);
		plugin_label_composite.setLayoutData(gridData);

		srStatus = new CLabelPadding(statusBar, borderFlag);
		srStatus.setText(MessageText.getString("SpeedView.stats.ratio"));

		COConfigurationManager.addAndFireParameterListener("Status Area Show SR",
				new ParameterListener() {
					public void parameterChanged(String parameterName) {
						srStatus.setVisible(COConfigurationManager.getBooleanParameter(parameterName));
						statusBar.layout();
					}
				});

		natStatus = new CLabelPadding(statusBar, borderFlag);
		natStatus.setText("");

		COConfigurationManager.addAndFireParameterListener("Status Area Show NAT",
				new ParameterListener() {
					public void parameterChanged(String parameterName) {
						natStatus.setVisible(COConfigurationManager.getBooleanParameter(parameterName));
						statusBar.layout();
					}
				});

		dhtStatus = new CLabelPadding(statusBar, borderFlag);
		dhtStatus.setText("");
		dhtStatus.setToolTipText(MessageText.getString("MainWindow.dht.status.tooltip"));

		COConfigurationManager.addAndFireParameterListener("Status Area Show DDB",
				new ParameterListener() {
					public void parameterChanged(String parameterName) {
						dhtStatus.setVisible(COConfigurationManager.getBooleanParameter(parameterName));
						statusBar.layout();
					}
				});
		
			// ip filters
		
		ipBlocked = new CLabelPadding(statusBar, borderFlag);
		ipBlocked.setText("{} IPs:"); //$NON-NLS-1$
		Messages.setLanguageText(ipBlocked, "MainWindow.IPs.tooltip");
		ipBlocked.addListener(SWT.MouseDoubleClick, new ListenerNeedingCoreRunning() {
			public void handleEvent(AzureusCore core, Event event) {
				BlockedIpsWindow.showBlockedIps(core, parent.getShell());
			}
		});

		final Menu menuIPFilter = new Menu(statusBar.getShell(), SWT.POP_UP);
		ipBlocked.setMenu( menuIPFilter );
		
		menuIPFilter.addListener(
			SWT.Show, 
			new Listener()
			{
				public void
				handleEvent(Event e) 
				{
					MenuItem[] oldItems = menuIPFilter.getItems();
					
					for(int i = 0; i < oldItems.length; i++){
						
						oldItems[i].dispose();
					}

					if ( !AzureusCoreFactory.isCoreRunning()){
						
						return;
					}
					
					AzureusCore azureusCore = AzureusCoreFactory.getSingleton();

					final IpFilter ip_filter = azureusCore.getIpFilterManager().getIPFilter();
					
					final MenuItem ipfEnable = new MenuItem(menuIPFilter, SWT.CHECK);
					
					ipfEnable.setSelection( ip_filter.isEnabled());
					
					Messages.setLanguageText(ipfEnable, "MyTorrentsView.menu.ipf_enable");
					
					ipfEnable.addSelectionListener(
						new SelectionAdapter() 
						{
							public void 
							widgetSelected(
								SelectionEvent e) 
							{
								ip_filter.setEnabled( ipfEnable.getSelection());
							}
						});

					final MenuItem ipfOptions = new MenuItem(menuIPFilter, SWT.PUSH);
										
					Messages.setLanguageText(ipfOptions, "ipfilter.options");
					
					ipfOptions.addSelectionListener(
						new SelectionAdapter() 
						{
							public void 
							widgetSelected(
								SelectionEvent e) 
							{
								UIFunctions uif = UIFunctionsManager.getUIFunctions();

								if (uif != null) {

									uif.openView(UIFunctions.VIEW_CONFIG, "ipfilter");
								}
							}
						});
				}
			});
				
		COConfigurationManager.addAndFireParameterListener("Status Area Show IPF",
				new ParameterListener() {
					public void parameterChanged(String parameterName) {
						ipBlocked.setVisible(COConfigurationManager.getBooleanParameter(parameterName));
						statusBar.layout();
					}
				});

			// down speed
		
		
		statusDown = new CLabelPadding(statusBar, borderFlag);
		statusDown.setImage(imageLoader.getImage("down"));
		//statusDown.setText(/*MessageText.getString("ConfigView.download.abbreviated") +*/"n/a");
		Messages.setLanguageText(statusDown,
				"MainWindow.status.updowndetails.tooltip");

		Listener lStats = new Listener() {
			public void handleEvent(Event e) {
				uiFunctions.getMDI().loadEntryByID(StatsView.VIEW_ID, true, false, "transfers");
			}
		};

		statusUp = new CLabelPadding(statusBar, borderFlag);
		statusUp.setImage(imageLoader.getImage("up"));
		//statusUp.setText(/*MessageText.getString("ConfigView.upload.abbreviated") +*/"n/a");
		Messages.setLanguageText(statusUp,
				"MainWindow.status.updowndetails.tooltip");

		statusDown.addListener(SWT.MouseDoubleClick, lStats);
		statusUp.addListener(SWT.MouseDoubleClick, lStats);

		Listener lDHT = new Listener() {
			public void handleEvent(Event e) {
				uiFunctions.getMDI().loadEntryByID(StatsView.VIEW_ID, true, false, "dht");
			}
		};

		dhtStatus.addListener(SWT.MouseDoubleClick, lDHT);

		Listener lSR = new Listener() {
			public void handleEvent(Event e) {

				uiFunctions.getMDI().loadEntryByID(StatsView.VIEW_ID, true, false, "activity");

				OverallStats stats = StatsFactory.getStats();
				
				if (stats == null) {
					return;
				}

				long ratio = (1000 * stats.getUploadedBytes() / (stats.getDownloadedBytes() + 1));

				if (ratio < 900) {

					//Utils.launch(Constants.AZUREUS_WIKI + "Share_Ratio");
				}
			}
		};

		srStatus.addListener(SWT.MouseDoubleClick, lSR);

		Listener lNAT = new ListenerNeedingCoreRunning() {
			public void handleEvent(AzureusCore core, Event e) {
				uiFunctions.openView(UIFunctions.VIEW_CONFIG,
						ConfigSection.SECTION_CONNECTION);

				if (PluginInitializer.getDefaultInterface().getConnectionManager().getNATStatus() != ConnectionManager.NAT_OK) {
					Utils.launch(Constants.AZUREUS_WIKI + "NAT_problem");
				}
			}
		};

		natStatus.addListener(SWT.MouseDoubleClick, lNAT);

		boolean bSpeedMenu = COConfigurationManager.getBooleanParameter("GUI_SWT_bOldSpeedMenu");

		if (bSpeedMenu) {
			// Status Bar Menu construction
			final Menu menuUpSpeed = new Menu(statusBar.getShell(), SWT.POP_UP);
			menuUpSpeed.addListener(SWT.Show, new Listener() {
				public void handleEvent(Event e) {
					if (!AzureusCoreFactory.isCoreRunning()) {
						return;
					}
					AzureusCore core = AzureusCoreFactory.getSingleton();
					GlobalManager globalManager = core.getGlobalManager();
					
					SelectableSpeedMenu.generateMenuItems(menuUpSpeed, core,
							globalManager, true);
				}
			});
			statusUp.setMenu(menuUpSpeed);
		} else {

			statusUp.addMouseListener(new MouseAdapter() {
				public void mouseDown(MouseEvent e) {
					if (!(e.button == 3 || (e.button == 1 && e.stateMask == SWT.CONTROL))) {
						return;
					}
					Event event = new Event();
					event.type = SWT.MouseUp;
					event.widget = e.widget;
					event.stateMask = e.stateMask;
					event.button = e.button;
					e.widget.getDisplay().post(event);

					CoreWaiterSWT.waitForCoreRunning(new AzureusCoreRunningListener() {
						public void azureusCoreRunning(AzureusCore core) {
							SelectableSpeedMenu.invokeSlider(core, true);
						}
					});
				}
			});
		}

		if (bSpeedMenu) {
			final Menu menuDownSpeed = new Menu(statusBar.getShell(), SWT.POP_UP);
			menuDownSpeed.addListener(SWT.Show, new Listener() {
				public void handleEvent(Event e) {
					if (!AzureusCoreFactory.isCoreRunning()) {
						return;
					}
					AzureusCore core = AzureusCoreFactory.getSingleton();
					GlobalManager globalManager = core.getGlobalManager();

					SelectableSpeedMenu.generateMenuItems(menuDownSpeed, core,
							globalManager, false);
				}
			});
			statusDown.setMenu(menuDownSpeed);
		} else {
			statusDown.addMouseListener(new MouseAdapter() {
				public void mouseDown(MouseEvent e) {
					if (!(e.button == 3 || (e.button == 1 && e.stateMask == SWT.CONTROL))) {
						return;
					}
					Event event = new Event();
					event.type = SWT.MouseUp;
					event.widget = e.widget;
					event.stateMask = e.stateMask;
					event.button = e.button;
					e.widget.getDisplay().post(event);

					CoreWaiterSWT.waitForCoreRunning(new AzureusCoreRunningListener() {
						public void azureusCoreRunning(AzureusCore core) {
							SelectableSpeedMenu.invokeSlider(core, false);
						}
					});
				}
			});
		}

		statusWarnings = new CLabelPadding(statusBar, borderFlag);
		warningIcon 	= imageLoader.getImage("image.sidebar.vitality.alert");
		warningGreyIcon = imageLoader.getImage("image.sidebar.vitality.alert-gray");
		infoIcon 		= imageLoader.getImage("image.sidebar.vitality.info");
		updateStatusWarnings( null, false );
		Messages.setLanguageText(statusWarnings,
				"MainWindow.status.warning.tooltip");
		Alerts.addMessageHistoryListener(new AlertHistoryListener() {
			public void alertHistoryAdded(LogAlert alert) {
				updateStatusWarnings( alert, true );
			}
			public void alertHistoryRemoved(LogAlert alert) {
				updateStatusWarnings( alert, false );
			}
		});
		statusWarnings.addMouseListener(new MouseListener() {
			public void mouseUp(MouseEvent e) {
				if (SystemWarningWindow.numWarningWindowsOpen > 0) {
					return;
				}
				ArrayList<LogAlert> alerts = Alerts.getUnviewedLogAlerts();
				if (alerts.size() == 0) {
					return;
				}

				Shell shell = statusWarnings.getShell();
				Rectangle bounds = statusWarnings.getClientArea();
				Point ptBottomRight = statusWarnings.toDisplay(bounds.x + bounds.width, bounds.y);
				new SystemWarningWindow(alerts.get(0), ptBottomRight, shell, 0);
			}
			
			public void mouseDown(MouseEvent e) {
			}
			
			public void mouseDoubleClick(MouseEvent e) {
			}
		});
		
		COConfigurationManager.addAndFireParameterListener("status.rategraphs",
				new ParameterListener() {
			public void parameterChanged(String parameterName) {
				Utils.execSWTThread(new AERunnable() {
					public void runSupport() {
						boolean doRateGraphs = COConfigurationManager.getBooleanParameter("status.rategraphs");
						if (doRateGraphs) {
							if (imgRec == null || imgRec.isDisposed()) {
  							imgRec = new Image(display, 100, 20);
  							GC gc = new GC(imgRec);
  							gc.setBackground(statusDown.getBackground());
  							gc.fillRectangle(0, 0, 100, 20);
  							gc.dispose();
  							statusDown.setBackgroundImage(imgRec);
							}
							
							if (imgSent == null || imgSent.isDisposed()) {
  							imgSent = new Image(display, 100, 20);
  							GC gc = new GC(imgSent);
  							gc.setBackground(statusUp.getBackground());
  							gc.fillRectangle(0, 0, 100, 20);
  							gc.dispose();
  							statusUp.setBackgroundImage(imgSent);
							}
						} else {
							statusUp.setBackgroundImage(null);
							statusDown.setBackgroundImage(null);
							Utils.disposeSWTObjects(new Object[] { imgRec, imgSent });
							imgRec = imgSent = null;
						}
					}
				});
			}
		});
		
		/////////
		
		PRManager.addListener(new ProgressListener());
		setProgressImage();
		
		uiFunctions.getUIUpdater().addUpdater(this);
		
		ArrayList<Runnable> list;
		this_mon.enter();
		try {
			list = listRunAfterInit;
			listRunAfterInit = null;
		} finally {
			this_mon.exit();
		}
		for (Runnable runnable : list) {
			try {
				runnable.run();
			} catch (Exception e) {
				Debug.out(e);
			}
		}
		
		statusBar.layout(true);

		return statusBar;
	}

	private TimerEventPeriodic		alert_flasher_event;
	private long					alert_flasher_event_start_time;
	private boolean					alert_flash_activate;
	
	
	protected void updateStatusWarnings( final LogAlert current_alert, final boolean current_added ) {
		Utils.execSWTThread(new AERunnable() {
			public void runSupport() {
				if (statusWarnings == null || statusWarnings.isDisposed()) {
					return;
				}
				
				ArrayList<LogAlert> alerts = Alerts.getUnviewedLogAlerts();
				int count = alerts.size();
				
				Image icon = infoIcon;
				
				for ( LogAlert alert: alerts ){
					int type = alert.getType();
					
					if ( type == LogAlert.LT_ERROR || type == LogAlert.LT_WARNING ){
						
						icon = warningIcon;
						
						break;
					}
				}
				
				if ( statusWarnings.getImage() != icon ){
					statusWarnings.setImage( icon );
				}
				
				statusWarnings.setVisible(count > 0);
				statusWarnings.setText("" + count);
				statusWarnings.layoutNow();
				
				if ( current_added ){
					
					alert_flash_activate = true;
					
					if ( current_alert.getType() != LogAlert.LT_INFORMATION ){
											
						alert_flasher_event_start_time = SystemTime.getMonotonousTime();

						if ( alert_flasher_event == null ){
																																	
							alert_flasher_event = 
								SimpleTimer.addPeriodicEvent(
									"MSB:alertFlasher",
									500,
									new TimerEventPerformer()
									{
										private long	last_tick_time = -1;
										
										public void 
										perform(
											TimerEvent event )
										{
											Utils.execSWTThread(
												new AERunnable() 
												{
													public void 
													runSupport() 
													{
														long now = SystemTime.getMonotonousTime();
														
															// during init timing can go a bit askew, try
															// and prevent too-quick transitions
														
														if ( 	last_tick_time != -1 &&
																now - last_tick_time < 400 ){
															
															return;
														}
														
														last_tick_time = now;
														
															// all logic is single threaded via SWT thread...
														
														if (	statusWarnings == null || 
																statusWarnings.isDisposed() ||
																alert_flasher_event == null ||
																!alert_flash_activate ){
																	
															if ( alert_flasher_event != null ){
																
																alert_flasher_event.cancel();
																
																alert_flasher_event = null;
															}
															
															return;
														}
														
														Image current_icon = statusWarnings.getImage();
														
														if ( 	now > alert_flasher_event_start_time + 15*1000 &&
																current_icon == warningIcon ){
															
															alert_flasher_event.cancel();
															
															alert_flasher_event = null;
															
															return;
														}
														
														Image target_icon = current_icon == warningIcon?warningGreyIcon:warningIcon;
														
														statusWarnings.setImage( target_icon );
													}
												});
										}									
									});
						}
					}
				}else{
					
					alert_flash_activate = false;
				}
			}
		});
	}

	private void addFeedBack() {
		AzureusCoreFactory.addCoreRunningListener(new AzureusCoreRunningListener() {
			public void azureusCoreRunning(AzureusCore core) {
				Utils.execSWTThread(new AERunnable() {
					public void runSupport() {
						_addFeedBack();
					}
				});
			}
		});
	}

	private void _addFeedBack() {
		/*
		 * Feedback
		 * 
		 */

		// only show after restart after 15 mins uptime
		OverallStats stats = StatsFactory.getStats();

		long secs_uptime = stats.getTotalUpTime();

		long last_uptime = COConfigurationManager.getLongParameter(
				"statusbar.feedback.uptime", 0);

		if (last_uptime == 0) {

			COConfigurationManager.setParameter("statusbar.feedback.uptime",
					secs_uptime);

		} else if (secs_uptime - last_uptime > 15 * 60) {

			createStatusEntry(new CLabelUpdater() {
				public boolean update(CLabelPadding label) {
					return( false );
				}

				public void created(CLabelPadding feedback) {
					feedback.setText(MessageText.getString("statusbar.feedback"));
					
					Listener feedback_listener = new Listener() {
						public void handleEvent(Event e) {
							
							String url = "feedback.start?" + Utils.getWidgetBGColorURLParam()
							+ "&fromWeb=false&os.name=" + UrlUtils.encode(Constants.OSName)
							+ "&os.version="
							+ UrlUtils.encode(System.getProperty("os.version"))
							+ "&java.version=" + UrlUtils.encode(Constants.JAVA_VERSION);
							
							// Utils.launch( url );
							
							UIFunctionsManagerSWT.getUIFunctionsSWT().viewURL(url, null, 600,
									520, true, false);
						}
					};
					
					feedback.setToolTipText(MessageText.getString("statusbar.feedback.tooltip"));
					feedback.setCursor(display.getSystemCursor(SWT.CURSOR_HAND));
					feedback.setForeground(Colors.blue);
					feedback.addListener(SWT.MouseUp, feedback_listener);
					feedback.addListener(SWT.MouseDoubleClick, feedback_listener);
					
					feedback.setVisible(true);
				}
			});
			
		}
	}

	/**
	 * @param statusBar2
	 *
	 * @since 4.0.0.1
	 */
	private void addStatusBarMenu(Composite cSB) {
		if (!Constants.isCVSVersion()) {
			return;
		}
		Menu menu = new Menu(cSB);
		cSB.setMenu(menu);

		MenuItem itemShow = new MenuItem(menu, SWT.CASCADE);
		itemShow.setText("Show");
		Menu menuShow = new Menu(itemShow);
		itemShow.setMenu(menuShow);

		final String[] statusAreaLangs = {
			"ConfigView.section.style.status.show_sr",
			"ConfigView.section.style.status.show_nat",
			"ConfigView.section.style.status.show_ddb",
			"ConfigView.section.style.status.show_ipf",
		};
		final String[] statusAreaConfig = {
			"Status Area Show SR",
			"Status Area Show NAT",
			"Status Area Show DDB",
			"Status Area Show IPF",
		};

		for (int i = 0; i < statusAreaConfig.length; i++) {
			final String configID = statusAreaConfig[i];
			String langID = statusAreaLangs[i];

			final MenuItem item = new MenuItem(menuShow, SWT.CHECK);
			Messages.setLanguageText(item, langID);
			item.addListener(SWT.Selection, new Listener() {
				public void handleEvent(Event event) {
					COConfigurationManager.setParameter(configID,
							!COConfigurationManager.getBooleanParameter(configID));
				}
			});
			menuShow.addListener(SWT.Show, new Listener() {
				public void handleEvent(Event event) {
					item.setSelection(COConfigurationManager.getBooleanParameter(configID));
				}
			});
		}
		
	}

	/**
	 * 
	 * @param keyedSentence
	 */
	public void setStatusText(String keyedSentence) {
		this.statusTextKey = keyedSentence == null ? "" : keyedSentence;
		setStatusImageKey(null);
		this.clickListener = null;
		if (statusTextKey.length() == 0) { // reset
			resetStatus();
		}

		updateStatusText();
	}
	
	private void setStatusImageKey(String newStatusImageKey) {
		if (("" + statusImageKey).equals("" + newStatusImageKey)) {
			return;
		}
		ImageLoader imageLoader = ImageLoader.getInstance();
		if (statusImageKey != null) {
			imageLoader.releaseImage(statusImageKey);
		}
		statusImageKey = newStatusImageKey;
		if (statusImageKey != null) {
			statusImage = imageLoader.getImage(statusImageKey);
		} else {
			statusImage = null;
		}
	}

	private void resetStatus() {
		if (Constants.isCVSVersion()) {
			statusTextKey = "MainWindow.status.unofficialversion ("
					+ Constants.AZUREUS_VERSION + ")";
			setStatusImageKey(STATUS_ICON_WARN);
		} else if (!Constants.isOSX && COConfigurationManager.getStringParameter("ui").equals("az2")) { //don't show official version numbers for OSX L&F
			statusTextKey = Constants.APP_NAME + " " + Constants.AZUREUS_VERSION;
			setStatusImageKey(null);
		}

	}

	/**
	 * @param statustype
	 * @param string
	 * @param l
	 */
	public void setStatusText(int statustype, String string,
			UIStatusTextClickListener l) {
		this.statusTextKey = string == null ? "" : string;

		if (statusTextKey.length() == 0) { // reset
			resetStatus();
		}

		this.clickListener = l;
		if (statustype == UIFunctions.STATUSICON_WARNING) {
			setStatusImageKey(STATUS_ICON_WARN);
		}
		if (statustype == UIFunctions.STATUSICON_WARNING) {
			setStatusImageKey(STATUS_ICON_WARN);
		} else {
			setStatusImageKey(null);
		}

		updateStatusText();
	}

	/**
	 * 
	 *
	 */
	public void updateStatusText() {
		if (display == null || display.isDisposed())
			return;
		final String text;
		if (updateWindow != null) {
			text = "MainWindow.updateavail";
		} else {
			text = this.statusTextKey;
		}
		Utils.execSWTThread(new AERunnable() {
			public void runSupport() {
				if (statusText != null && !statusText.isDisposed()) {
					statusText.setText(MessageText.getStringForSentence(text));
					statusText.setImage(statusImage);
				}
			}
		});
	}

	/**
	 * 
	 *
	 */
	public void refreshStatusText() {
		if (statusText != null && !statusText.isDisposed())
			statusText.update();
	}

	/**
	 * 
	 * @param updateWindow
	 */
	public void setUpdateNeeded(UpdateWindow updateWindow) {
		this.updateWindow = updateWindow;
		if (updateWindow != null) {
			statusText.setCursor(display.getSystemCursor(SWT.CURSOR_HAND));
			statusText.setForeground(Colors.colorWarning);
			updateStatusText();
		} else {
			statusText.setCursor(null);
			statusText.setForeground(null);
			updateStatusText();
		}
	}

	// @see com.aelitis.azureus.ui.common.updater.UIUpdatable#updateUI()
	public void updateUI() {
		if (statusBar.isDisposed()) {
			uiFunctions.getUIUpdater().removeUpdater(this);
			return;
		}

		// Plugins.
		Control[] plugin_elements = this.plugin_label_composite.getChildren();
		for (int i = 0; i < plugin_elements.length; i++) {
			if (plugin_elements[i] instanceof UpdateableCLabel) {
				((UpdateableCLabel) plugin_elements[i]).checkForRefresh();
			}
		}

		if (ipBlocked.isVisible()) {
			updateIPBlocked();
		}
		
		if (srStatus.isVisible()) {
			updateShareRatioStatus();
		}


		if (natStatus.isVisible()) {
			updateNatStatus();
		}
		
		if (dhtStatus.isVisible()) {
			updateDHTStatus();
		}


		// UL/DL Status Sections
		if (AzureusCoreFactory.isCoreRunning()) {
			AzureusCore core = AzureusCoreFactory.getSingleton();
			GlobalManager gm = core.getGlobalManager();
			GlobalManagerStats stats = gm.getStats();

			int dl_limit = NetworkManager.getMaxDownloadRateBPS() / 1024;
			long rec_data = stats.getDataReceiveRate();
			long rec_prot = stats.getProtocolReceiveRate();
			
			if (last_dl_limit != dl_limit || last_rec_data != rec_data || last_rec_prot != rec_prot) {
				last_dl_limit = dl_limit;
				last_rec_data = rec_data;
				last_rec_prot = rec_prot;

				statusDown.setText((dl_limit == 0 ? "" : "[" + dl_limit + "K] ")
						+ DisplayFormatters.formatDataProtByteCountToKiBEtcPerSec(rec_data, rec_prot));
			}
			
			boolean auto_up = TransferSpeedValidator.isAutoSpeedActive(gm)
					&& TransferSpeedValidator.isAutoUploadAvailable(core);

			int ul_limit_norm = NetworkManager.getMaxUploadRateBPSNormal() / 1024;

			String seeding_only;
			if (NetworkManager.isSeedingOnlyUploadRate()) {
				int ul_limit_seed = NetworkManager.getMaxUploadRateBPSSeedingOnly() / 1024;
				if (ul_limit_seed == 0) {
					seeding_only = "+" + Constants.INFINITY_STRING + "K";
				} else {
					int diff = ul_limit_seed - ul_limit_norm;
					seeding_only = (diff >= 0 ? "+" : "") + diff + "K";
				}
			} else {
				seeding_only = "";
			}

			int sent_data = stats.getDataSendRate();
			if (imgRec != null && !imgRec.isDisposed()) {
				updateGraph(statusDown, imgRec, rec_data, max_rec);
				updateGraph(statusUp, imgSent, sent_data, max_sent);
			}


			statusUp.setText((ul_limit_norm == 0 ? "" : "[" + ul_limit_norm + "K"
					+ seeding_only + "]")
					+ (auto_up ? "* " : " ")
					+ DisplayFormatters.formatDataProtByteCountToKiBEtcPerSec(
							sent_data, stats.getProtocolSendRate()));
		}
	}

	private void updateGraph(CLabelPadding label, Image img,
			long newVal, long[] max) {
		GC gc = new GC(img);
		try {
  		long val = newVal;
  		Rectangle bounds = img.getBounds();
  		final int padding = 2;
  		int x = bounds.width - padding - padding;
  		if (val > max[0]) {
  			int y = 20 - (int) (max[0] * 20 / val);
  			gc.setBackground(label.getBackground());
  			gc.fillRectangle(padding, 0, x, y);
  			// gc.drawImage(imgRec, 1, 0, x, 20, 0, y, x, 20 - y);
  			gc.copyArea(padding + 1, 0, x, 20, padding, y);
  			max[0] = val;
  		} else {
  			gc.copyArea(padding + 1, 0, x, 20, padding, 0);
  			// gc.drawImage(imgRec, 1, 0, x, 20, 0, 0, x, 20);
  		}
  		gc.setForeground(label.getBackground());
  		int breakPoint = 20 - (max[0] == 0 ? 0
  				: (int) (val * 20 / max[0]));
  		gc.drawLine(x, 0, x, breakPoint);
  		gc.setForeground(Colors.blues[5]);
  		gc.drawLine(x, breakPoint, x, 20);
		} finally {
  		gc.dispose();
		}
		label.redraw();
	}

	/**
	 * 
	 *
	 * @since 3.1.1.1
	 */
	private void updateDHTStatus() {
		if (dhtPlugin == null) {
			return;
		}
		// DHT Status Section
		int dht_status = dhtPlugin.getStatus();
		long dht_count = -1;
		//boolean	reachable = false;
		if (dht_status == DHTPlugin.STATUS_RUNNING) {
			DHT[] dhts = dhtPlugin.getDHTs();

			//reachable = dhts.length > 0 && dhts[0].getTransport().isReachable();

			//if ( reachable ){
			dht_count = dhts[0].getControl().getStats().getEstimatedDHTSize();
			//}
		}

		if (lastDHTstatus != dht_status || lastDHTcount != dht_count) {
			boolean hasImage = dhtStatus.getImage() != null;
			boolean needImage = true;
			switch (dht_status) {
				case DHTPlugin.STATUS_RUNNING:

					dhtStatus.setToolTipText(MessageText.getString("MainWindow.dht.status.tooltip"));
					dhtStatus.setText(MessageText.getString("MainWindow.dht.status.users").replaceAll(
							"%1", numberFormat.format(dht_count)));

					/*
					if ( reachable ){
						dhtStatus.setImage(ImageRepository.getImage("greenled"));
						dhtStatus.setToolTipText(MessageText
								.getString("MainWindow.dht.status.tooltip"));
						dhtStatus.setText(MessageText.getString("MainWindow.dht.status.users").replaceAll("%1", numberFormat.format(dht_count)));
					} else {
						dhtStatus.setImage(ImageRepository.getImage("yellowled"));
						dhtStatus.setToolTipText(MessageText
								.getString("MainWindow.dht.status.unreachabletooltip"));
						dhtStatus.setText(MessageText
								.getString("MainWindow.dht.status.unreachable"));
					}
					*/
					break;

				case DHTPlugin.STATUS_DISABLED:
					//dhtStatus.setImage(ImageRepository.getImage("grayled"));
					dhtStatus.setText(MessageText.getString("MainWindow.dht.status.disabled"));
					break;

				case DHTPlugin.STATUS_INITALISING:
					//dhtStatus.setImage(ImageRepository.getImage("yellowled"));
					dhtStatus.setText(MessageText.getString("MainWindow.dht.status.initializing"));
					break;

				case DHTPlugin.STATUS_FAILED:
					//dhtStatus.setImage(ImageRepository.getImage("redled"));
					dhtStatus.setText(MessageText.getString("MainWindow.dht.status.failed"));
					break;

				default:
					needImage = false;
					break;
			}

			if (hasImage != needImage) {
				ImageLoader imageLoader = ImageLoader.getInstance();
				if (needImage) {
					Image img = imageLoader.getImage("sb_count");
					dhtStatus.setImage(img);
				} else {
					imageLoader.releaseImage("sb_count");
					dhtStatus.setImage(null);
				}
			}
			lastDHTstatus = dht_status;
			lastDHTcount = dht_count;
		}
	}

	/**
	 * 
	 *
	 * @since 3.1.1.1
	 */
	private void updateNatStatus() {
		// NAT status Section
		if (connection_manager == null) {
			return;
		}

		int nat_status = connection_manager.getNATStatus();

		if (lastNATstatus != nat_status) {
			String imgID;
			String tooltipID;
			String statusID;

			switch (nat_status) {
				case ConnectionManager.NAT_UNKNOWN:
					imgID = "grayled";
					tooltipID = "MainWindow.nat.status.tooltip.unknown";
					statusID = "MainWindow.nat.status.unknown";
					break;

				case ConnectionManager.NAT_OK:
					imgID = "greenled";
					tooltipID = "MainWindow.nat.status.tooltip.ok";
					statusID = "MainWindow.nat.status.ok";
					break;

				case ConnectionManager.NAT_PROBABLY_OK:
					imgID = "yellowled";
					tooltipID = "MainWindow.nat.status.tooltip.probok";
					statusID = "MainWindow.nat.status.probok";
					break;

				default:
					imgID = "redled";
					tooltipID = "MainWindow.nat.status.tooltip.bad";
					statusID = "MainWindow.nat.status.bad";
					break;
			}
			
			if (!imgID.equals(lastNATimageID)) {
				ImageLoader imageLoader = ImageLoader.getInstance();
				natStatus.setImage(imageLoader.getImage(imgID));

				if (lastNATimageID != null) {
					imageLoader.releaseImage(lastNATimageID);
				}
				lastNATimageID = imgID;
			}

			natStatus.setToolTipText(MessageText.getString(tooltipID));
			natStatus.setText(MessageText.getString(statusID));
			lastNATstatus = nat_status;
		}
	}

	/**
	 * 
	 *
	 * @since 3.1.1.1
	 */
	private void updateShareRatioStatus() {
		// SR status section
		
		if (overall_stats == null) {
			overall_stats = StatsFactory.getStats();
			
			if (overall_stats == null) {
				return;
			}
		}

		long ratio = (1000 * overall_stats.getUploadedBytes() / (overall_stats.getDownloadedBytes() + 1));

		int sr_status;

		if (ratio < 500) {

			sr_status = 0;

		} else if (ratio < 900) {

			sr_status = 1;

		} else {

			sr_status = 2;
		}

		if (sr_status != last_sr_status) {

			String imgID;

			switch (sr_status) {
				case 2:
					imgID = "greenled";
					break;

				case 1:
					imgID = "yellowled";
					break;

				default:
					imgID = "redled";
					break;
			}

			if (!imgID.equals(lastSRimageID)) {
				ImageLoader imageLoader = ImageLoader.getInstance();
				srStatus.setImage(imageLoader.getImage(imgID));
				if (lastSRimageID != null) {
					imageLoader.releaseImage(lastSRimageID);
				}
				lastSRimageID  = imgID;
			}
			
			last_sr_status = sr_status;
		}

		if (ratio != last_sr_ratio) {

			String tooltipID;

			switch (sr_status) {
				case 2:
					tooltipID = "MainWindow.sr.status.tooltip.ok";
					break;

				case 1:
					tooltipID = "MainWindow.sr.status.tooltip.poor";
					break;

				default:
					tooltipID = "MainWindow.sr.status.tooltip.bad";
					break;
			}

			String ratio_str = "";

			String partial = "" + ratio % 1000;

			while (partial.length() < 3) {

				partial = "0" + partial;
			}

			ratio_str = (ratio / 1000) + "." + partial;

			srStatus.setToolTipText(MessageText.getString(tooltipID, new String[] {
				ratio_str
			}));

			last_sr_ratio = ratio;
		}
	}

	/**
	 * 
	 *
	 * @since 3.1.1.1
	 */
	private void updateIPBlocked() {
		if (!AzureusCoreFactory.isCoreRunning()) {
			return;
		}
		
		AzureusCore azureusCore = AzureusCoreFactory.getSingleton();

		// IP Filter Status Section
		IpFilter ip_filter = azureusCore.getIpFilterManager().getIPFilter();

		ipBlocked.setForeground( display.getSystemColor(ip_filter.isEnabled()? SWT.COLOR_WIDGET_FOREGROUND : SWT.COLOR_WIDGET_NORMAL_SHADOW));
		
		ipBlocked.setText("IPs: "
				+ numberFormat.format(ip_filter.getNbRanges())
				+ " - "
				+ numberFormat.format(ip_filter.getNbIpsBlockedAndLoggable())
				+ "/"
				+ numberFormat.format(ip_filter.getNbBannedIps())
				+ "/"
				+ numberFormat.format(azureusCore.getIpFilterManager().getBadIps().getNbBadIps()));
		
		ipBlocked.setToolTipText(MessageText.getString("MainWindow.IPs.tooltip",
				new String[] {
					ip_filter.isEnabled()?
					DisplayFormatters.formatDateShort(ip_filter.getLastUpdateTime()):MessageText.getString( "ipfilter.disabled" )
				}));
	}

	/**
	 * @param string
	 */
	public void setDebugInfo(String string) {
		if (statusText != null && !statusText.isDisposed())
			statusText.setToolTipText(string);
	}
	
	public boolean isMouseOver() {
		if (statusText == null || statusText.isDisposed()) {
			return false;
		}
		return statusText.getDisplay().getCursorControl() == statusText;
	}

	public static interface CLabelUpdater
	{
		public void created(CLabelPadding label);
		public boolean update(CLabelPadding label);
	}

	/**
	 * CLabel that shrinks to fit text after a specific period of time.
	 * Makes textual changes less jumpy
	 * 
	 * @author TuxPaper
	 * @created Mar 21, 2006
	 *
	 */
	public class CLabelPadding
		extends Canvas implements PaintListener
	{
		private int lastWidth = 0;

		private long widthSetOn = 0;

		private static final int KEEPWIDTHFOR_MS = 30 * 1000;
		
		String text = "";

		private Image image;

		private Image bgImage;

		/**
		 * Default Constructor
		 * 
		 * @param parent
		 * @param style
		 */
		public CLabelPadding(Composite parent, int style) {
			super(parent, style | SWT.DOUBLE_BUFFERED);

			GridData gridData = new GridData(GridData.HORIZONTAL_ALIGN_CENTER
					| GridData.VERTICAL_ALIGN_FILL);
			setLayoutData(gridData);
			setForeground(parent.getForeground());
			
			addPaintListener(this);
		}

		public void paintControl(PaintEvent e) {
			Point size = getSize();
			e.gc.setAdvanced(true);
			if (bgImage != null && !bgImage.isDisposed()) {
				Rectangle bounds = bgImage.getBounds();
				if (display.getCursorControl() != this) {
					e.gc.setAlpha(100);
				}
				e.gc.drawImage(bgImage, 0, 0, bounds.width, bounds.height, 0, 2,
						size.x, size.y - 4);
				e.gc.setAlpha(255);
			}
			Rectangle clientArea = getClientArea();
			

			Image image = getImage();
			Rectangle imageBounds = null;
			if (image != null && !image.isDisposed()) {
				imageBounds = image.getBounds();
			}
			GCStringPrinter sp = new GCStringPrinter(e.gc, getText(), clientArea,
					true, true, SWT.CENTER);
			sp.calculateMetrics();
			Point textSize = sp.getCalculatedSize();

			if (imageBounds != null) {
				int pad = 2;
				int ofs = imageBounds.width + imageBounds.x;
				int xStartImage = (clientArea.width - textSize.x - ofs - pad) / 2;
				e.gc.drawImage(image, xStartImage,
						(clientArea.height / 2) - (imageBounds.height / 2));
				clientArea.x += xStartImage + ofs + pad;
				clientArea.width -= xStartImage + ofs + pad;
			} else {
				int ofs = (clientArea.width / 2) - (textSize.x / 2);
				clientArea.x += ofs;
				clientArea.width -= ofs;
			}
			sp.printString(e.gc, clientArea, SWT.LEFT);

			int x = clientArea.x + clientArea.width - 1;
			e.gc.setAlpha(20);
			e.gc.drawLine(x, 3, x, clientArea.height - 3);
		}

		/* (non-Javadoc)
		 * @see org.eclipse.swt.custom.CLabel#computeSize(int, int, boolean)
		 */
		public Point computeSize(int wHint, int hHint) {
			return computeSize(wHint, hHint, true);
		}

		public Point computeSize(int wHint, int hHint, boolean changed) {
			try {
				Point pt = computeSize(wHint, hHint, changed, false);

				return pt;
			} catch (Throwable t) {
				Debug.out("Error while computing size for CLabel with text:"
						+ getText() + "; " + t.toString());
				return new Point(0, 0);
			}
		}

		// @see org.eclipse.swt.widgets.Control#computeSize(int, int)
		public Point computeSize(int wHint, int hHint, boolean changed, boolean realWidth) {
			if (!isVisible()) {
				return (new Point(0, 0));
			}

			if (wHint != SWT.DEFAULT && hHint != SWT.DEFAULT) {
				return new Point(wHint, hHint);
			}
			Point pt = new Point(wHint, hHint);

			Point lastSize = new Point(0, 0);

			Image image = getImage();
			if (image != null && !image.isDisposed()) {
				Rectangle bounds = image.getBounds();
				int ofs = bounds.width + bounds.x + 5;
				lastSize.x += ofs;
				lastSize.y = bounds.height;
			}

			GC gc = new GC(this);
			GCStringPrinter sp = new GCStringPrinter(gc, getText(), new Rectangle(0,
					0, 10000, 20), true, true, SWT.LEFT);
			sp.calculateMetrics();
			Point lastTextSize = sp.getCalculatedSize();
			gc.dispose();

			lastSize.x += lastTextSize.x + 10;
			lastSize.y = Math.max(lastSize.y, lastTextSize.y);

			if (wHint == SWT.DEFAULT) {
				pt.x = lastSize.x;
			}
			if (hHint == SWT.DEFAULT) {
				pt.y = lastSize.y;
			}

			if (!realWidth) {
	  			long now = System.currentTimeMillis();
	  			if (lastWidth > pt.x && now - widthSetOn < KEEPWIDTHFOR_MS) {
	  				pt.x = lastWidth;
	  			} else {
	  				if (lastWidth != pt.x) {
	  					lastWidth = pt.x;
	  				}
	  				widthSetOn = now;
	  			}
			}

			return pt;
		}
		

		public void setImage(Image image) {
			this.image = image;
			
			redraw();
		}
		
		public Image getImage() {
			return image;
		}
		
		public void setBackgroundImage(Image image) {
			bgImage = image;
			
			redraw();
		}
		
		public Image getBackgroundImage() {
			return bgImage;
		}
		
		public String getText() {
			return text;
		}
		
		public void setText(String text) {
			if (text == null) {
				text = "";
			}
			if (text.equals(getText())) {
				return;
			}
			this.text = text;
			int oldWidth = lastWidth;
			Point pt = computeSize(SWT.DEFAULT, SWT.DEFAULT, true, true);
			if (pt.x > oldWidth && text.length() > 0) {
				statusBar.layout();
			} else if (pt.x < oldWidth) {
				Utils.execSWTThreadLater(KEEPWIDTHFOR_MS, new AERunnable() {
					public void runSupport() {
						if (statusBar == null || statusBar.isDisposed()) {
							return;
						}
						statusBar.layout();
					}
				});
			}
			redraw();
		}
		
		public void
		reset()
		{
			widthSetOn 	= 0;
			lastWidth	= 0;
		}
		
		public void layoutNow() {
			widthSetOn = 0;
			statusBar.layout();
		}

	}

	private class UpdateableCLabel
		extends CLabelPadding
	{

		private CLabelUpdater updater;

		public UpdateableCLabel(Composite parent, int style, CLabelUpdater updater) {
			super(parent, style);
			this.updater = updater;
		}

		private void checkForRefresh() {
			if ( updater.update(this)){
				layoutPluginComposite();
			}
		}
	}

	public void createStatusEntry(final CLabelUpdater updater) {
		AERunnable r = new AERunnable() {
			public void runSupport() {
				UpdateableCLabel result = new UpdateableCLabel(plugin_label_composite, borderFlag,
						updater);
				result.setLayoutData(new GridData(GridData.FILL_BOTH));
				layoutPluginComposite();
				updater.created(result);
			}
		};
		this_mon.enter();
		try {
			if (listRunAfterInit != null) {
				listRunAfterInit.add(r);
				return;
			}
		} finally {
			this_mon.exit();
		}

		Utils.execSWTThread(r);
	}

	private void
	layoutPluginComposite()
	{
		Control[] plugin_elements = this.plugin_label_composite.getChildren();
		for (int i = 0; i < plugin_elements.length; i++) {
			if (plugin_elements[i] instanceof UpdateableCLabel) {
				((UpdateableCLabel) plugin_elements[i]).reset();
			}
		}
		statusBar.layout();
	}
	// =============================================================
	// Below code are ProgressBar/Status text specific
	// =============================================================	
	/**
	 * Show or hide the Progress Bar
	 * @param state
	 */
	private void showProgressBar(boolean state) {
		/*
		 * We show/hide the progress bar simply by setting the .widthHint and letting the statusBar handle the layout
		 */
		if (true == state && false == progressBar.isVisible()) {
			progressGridData.widthHint = 100;
			progressBar.setVisible(true);
			statusBar.layout();
		} else if (false == state && true == progressBar.isVisible()) {
			progressBar.setVisible(false);
			progressGridData.widthHint = 0;
			statusBar.layout();
		}
	}

	/**
	 * Updates the display of the ProgressBar and/or the status text
	 * @param pReport the <code>ProgressReport</code> containing the information
	 * to display; can be <code>null</code> in which case the status text and progress bar will be reset to default states
	 */
	private void updateProgressBarDisplay(IProgressReport pReport) {
		latestReport_mon.enter();
		try {
			latestReport = pReport;
		} finally {
			latestReport_mon.exit();
		}
		if (null == progressBar || progressBar.isDisposed()
				|| updateProgressBarDisplayQueued) {
			return;
		}
		updateProgressBarDisplayQueued = true;

		Utils.execSWTThread(new AERunnable() {
			public void runSupport() {
				latestReport_mon.enter();
				try {
					updateProgressBarDisplayQueued = false;

					if ((null == progressBar || true == progressBar.isDisposed())) {
						return;
					}

					if (null != latestReport) {
						/*
						 * Pass the values through to the progressbar
						 */
						progressBar.setMinimum(latestReport.getMinimum());
						progressBar.setMaximum(latestReport.getMaximum());
						progressBar.setIndeterminate(latestReport.isIndeterminate());
						progressBar.setPercentage(latestReport.getPercentage());
						showProgressBar(true);

						/*
						 * Update status text
						 */
						if (true == isAZ3) {
							statusText.setText(latestReport.getName());
						} else {
							setStatusText(latestReport.getName());
						}
					}

					else {
						/*
						 * Since the pReport is null then reset progress display appropriately
						 */
						showProgressBar(false);

						if (true == isAZ3) {
							statusText.setText("");
						} else {
							setStatusText(null);
						}
					}
				} finally {
					latestReport_mon.exit();
				}

			}

		}, true);

	}

	private void setProgressImage() {
		String imageID;

		if (PRManager.getReporterCount(ProgressReportingManager.COUNT_ERROR) > 0) {
			imageID = "progress_error";
		} else if (PRManager.getReporterCount(ProgressReportingManager.COUNT_ALL) > 0) {
			imageID = "progress_info";
		} else {
			imageID = "progress_viewer";
		}
		
		if (!imageID.equals(lastProgressImageID)) {
			final String fImageID = imageID;
			Utils.execSWTThread(new AERunnable() {
				public void runSupport() {
					if (progressViewerImageLabel.isDisposed()) {
						return;
					}
					ImageLoader imageLoader = ImageLoader.getInstance();
					progressViewerImageLabel.setImage(imageLoader.getImage(fImageID));
					if (lastProgressImageID != null) {
						imageLoader.releaseImage(lastProgressImageID);
					}
					lastProgressImageID  = fImageID;
				}
			});
		}
	}

	/**
	 * A listener that listens to any changes notified from the <code>ProgressReportingManager</code> and
	 * accordingly update the progress bar and/or the status text area.
	 * @author knguyen
	 *
	 */
	private class ProgressListener
		implements IProgressReportingListener
	{

		public int reporting(int eventType, IProgressReporter reporter) {

			/*
			 * Show the appropriate image based on the content of the reporting manager
			 */
			setProgressImage();

			if (null == reporter) {
				return RETVAL_OK;
			}

			if (MANAGER_EVENT_REMOVED == eventType) {
				updateFromPrevious();
			} else if (MANAGER_EVENT_ADDED == eventType
					|| MANAGER_EVENT_UPDATED == eventType) {
				/*
				 * Get a ProgressReport to ensure all data is consistent
				 */
				IProgressReport pReport = reporter.getProgressReport();

				/*
				 * Pops up the ProgressReportingWindow to show this report if it is an error report;
				 * this is to help catch the users attention
				 */
				if (true == pReport.isInErrorState()) {
					
					if(true == "reporterType_updater".equals(pReport.getReporterType())){
						/*
						 * Suppressing the pop-up for update-related errors
						 */
						return RETVAL_OK;
					}
					
					final IProgressReporter final_reporter = reporter;
					

					/*
					 * The new window is opened only if there is not one already showing the same reporter
					 */
					if (false == ProgressReporterWindow.isOpened(final_reporter)) {
						Utils.execSWTThread(new AERunnable() {
							public void runSupport() {
								if ( !ProgressReporterWindow.isOpened(final_reporter)){
									ProgressReporterWindow.open(final_reporter,
											ProgressReporterWindow.NONE);
								}
							}
						}, true);
					}
				}

				/*
				 * If this reporter is not active then get the previous reporter that is still active and display info from that
				 */
				if (false == pReport.isActive()) {
					updateFromPrevious();
				} else {
					update(pReport);
				}
			}

			return RETVAL_OK;
		}

		private void update(final IProgressReport pReport) {

			if (null == pReport) {
				updateProgressBarDisplay(null);
				return;
			}

			/*
			 * If there is at least 2 reporters still active then show the progress bar as indeterminate
			 * and display the text from the current reporter
			 */
			if (true == PRManager.hasMultipleActive()) {
				Utils.execSWTThread(new AERunnable() {
					public void runSupport() {
						setStatusText(pReport.getName());
						progressBar.setIndeterminate(true);
						showProgressBar(true);
					}
				}, true);
			} else {
				updateProgressBarDisplay(pReport);
			}
		}

		private void updateFromPrevious() {
			/*
			 * Get the previous reporter that is still active
			 */
			IProgressReporter previousReporter = PRManager.getNextActiveReporter();

			/*
			 * If null then we reset the status text and the progress bar
			 */
			if (null != previousReporter) {
				update(previousReporter.getProgressReport());
			} else {
				update(null);
			}
		}
	}

	public Rectangle getBounds() {
		if (null != statusBar) {
			return statusBar.getBounds();
		}
		return null;
	}

	// @see com.aelitis.azureus.ui.common.updater.UIUpdatable#getUpdateUIName()
	public String getUpdateUIName() {
		return ID;
	}
}