/*
 * Created on Sep 13, 2004
 * Created by Olivier Chalouhi
 * Copyright (C) 2004, 2005, 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.views.stats;


import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.gudy.azureus2.core3.internat.MessageText;
import org.gudy.azureus2.core3.util.ByteFormatter;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.DisplayFormatters;
import org.gudy.azureus2.core3.util.TimeFormatter;
import org.gudy.azureus2.plugins.PluginInterface;
import org.gudy.azureus2.ui.swt.Messages;
import org.gudy.azureus2.ui.swt.Utils;
import org.gudy.azureus2.ui.swt.components.graphics.SpeedGraphic;
import org.gudy.azureus2.ui.swt.views.AbstractIView;

import com.aelitis.azureus.core.AzureusCoreFactory;
import com.aelitis.azureus.core.dht.DHT;
import com.aelitis.azureus.core.dht.DHTStorageAdapter;
import com.aelitis.azureus.core.dht.control.DHTControlActivity;
import com.aelitis.azureus.core.dht.control.DHTControlListener;
import com.aelitis.azureus.core.dht.control.DHTControlStats;
import com.aelitis.azureus.core.dht.db.DHTDBStats;
import com.aelitis.azureus.core.dht.nat.DHTNATPuncher;
import com.aelitis.azureus.core.dht.router.DHTRouterStats;
import com.aelitis.azureus.core.dht.transport.DHTTransport;
import com.aelitis.azureus.core.dht.transport.DHTTransportFullStats;
import com.aelitis.azureus.core.dht.transport.DHTTransportStats;
import com.aelitis.azureus.plugins.dht.DHTPlugin;

/**
 * 
 */
public class DHTView extends AbstractIView {
  
  public static final int DHT_TYPE_MAIN 	= DHT.NW_MAIN;
  public static final int DHT_TYPE_CVS  	= DHT.NW_CVS;
  public static final int DHT_TYPE_MAIN_V6 	= DHT.NW_MAIN_V6;

  DHT dht;
  
  Composite panel;
  
  String	yes_str;
  String	no_str;
  
  Label lblUpTime,lblNumberOfUsers;
  Label lblNodes,lblLeaves;
  Label lblContacts,lblReplacements,lblLive,lblUnknown,lblDying;
  Label lblSkew, lblRendezvous, lblReachable;
  Label lblKeys,lblValues,lblSize;
  Label lblLocal,lblDirect,lblIndirect;
  Label lblDivFreq,lblDivSize;
  
  Label lblReceivedPackets,lblReceivedBytes;
  Label lblSentPackets,lblSentBytes;
    
  
  Label lblPings[] = new Label[4];
  Label lblFindNodes[] = new Label[4];
  Label lblFindValues[] = new Label[4];
  Label lblStores[] = new Label[4];
  Label lblData[] = new Label[4];
    
  Canvas  in,out;  
  SpeedGraphic inGraph,outGraph;
  
  boolean activityChanged;
  DHTControlListener controlListener;
  Table activityTable;
  DHTControlActivity[] activities;
  
  private final int dht_type;
  

  public DHTView( int dht_type ) {
    this.dht_type = dht_type;
    init();
  }
  
  private void init() {
    try {
      PluginInterface dht_pi = AzureusCoreFactory.getSingleton().getPluginManager().getPluginInterfaceByClass( DHTPlugin.class );
        
      if ( dht_pi == null ){
      	   
    	  return;
      }
         
      DHT[] dhts = ((DHTPlugin)dht_pi.getPlugin()).getDHTs();
      
      for (int i=0;i<dhts.length;i++){
    	  if ( dhts[i].getTransport().getNetwork() == dht_type ){
    		  dht = dhts[i];
    		  break;
    	  }
      }
	  
      if ( dht == null ){
    	  
    	  return;
      }
      
      controlListener = new DHTControlListener() {
        public void activityChanged(DHTControlActivity activity,int type) {
          activityChanged = true;
        }                
      };
      dht.getControl().addListener(controlListener);
      
    } catch(Exception e) {
      Debug.printStackTrace( e );
    }
  }
  
  public void initialize(Composite composite) {
    panel = new Composite(composite,SWT.NULL);
    GridLayout layout = new GridLayout();
    layout.numColumns = 2;
    panel.setLayout(layout);
    
    yes_str = MessageText.getString( "Button.yes").replaceAll("&", "");
    no_str 	= MessageText.getString( "Button.no").replaceAll("&", "");
    
    initialiseGeneralGroup();
    initialiseDBGroup();
    
    initialiseTransportDetailsGroup();
    initialiseOperationDetailsGroup();
    
    initialiseActivityGroup();
  }
  
  private void initialiseGeneralGroup() {
    Group gGeneral = new Group(panel,SWT.NONE);
    Messages.setLanguageText(gGeneral, "DHTView.general.title" );
    
    GridData data = new GridData();
    data.verticalAlignment = SWT.BEGINNING;
    data.widthHint = 350;
    gGeneral.setLayoutData(data);
    
    GridLayout layout = new GridLayout();
    layout.numColumns = 6;
    gGeneral.setLayout(layout);
    
    	// row1 
    
    Label label = new Label(gGeneral,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.general.uptime");    
    
    lblUpTime = new Label(gGeneral,SWT.NONE);
    lblUpTime.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    
    label = new Label(gGeneral,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.general.users");    
    
    lblNumberOfUsers = new Label(gGeneral,SWT.NONE);
    lblNumberOfUsers.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    
    label = new Label(gGeneral,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.general.reachable");    

    lblReachable = new Label(gGeneral,SWT.NONE);
    lblReachable.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    
    
    	// row 2
    
    label = new Label(gGeneral,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.general.nodes");    
    
    lblNodes = new Label(gGeneral,SWT.NONE);
    lblNodes.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    
    label = new Label(gGeneral,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.general.leaves");    
    
    lblLeaves = new Label(gGeneral,SWT.NONE);
    lblLeaves.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    
    label = new Label(gGeneral,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.general.rendezvous");    

    lblRendezvous = new Label(gGeneral,SWT.NONE);
    lblRendezvous.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    
    	// row 3
    
    label = new Label(gGeneral,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.general.contacts");    
    
    lblContacts = new Label(gGeneral,SWT.NONE);
    lblContacts.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    
    label = new Label(gGeneral,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.general.replacements");    
    
    lblReplacements = new Label(gGeneral,SWT.NONE);
    lblReplacements.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    
    label = new Label(gGeneral,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.general.live");    
    
    lblLive= new Label(gGeneral,SWT.NONE);
    lblLive.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    
    	// row 4
    
    label = new Label(gGeneral,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.general.skew");    
    
    lblSkew= new Label(gGeneral,SWT.NONE);
    lblSkew.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));

    label = new Label(gGeneral,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.general.unknown");    
    
    lblUnknown = new Label(gGeneral,SWT.NONE);
    lblUnknown.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    
    label = new Label(gGeneral,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.general.dying");    
    
    lblDying = new Label(gGeneral,SWT.NONE);
    lblDying.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));    
  }
  
  private void initialiseDBGroup() {
    Group gDB = new Group(panel,SWT.NONE);
    Messages.setLanguageText(gDB,"DHTView.db.title");
    
    GridData data = new GridData(GridData.FILL_HORIZONTAL);
    data.verticalAlignment = SWT.FILL;
    gDB.setLayoutData(data);
    
    GridLayout layout = new GridLayout();
    layout.numColumns = 6;    
    layout.makeColumnsEqualWidth = true;
    gDB.setLayout(layout);
    
    Label label = new Label(gDB,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.db.keys");    
    
    lblKeys = new Label(gDB,SWT.NONE);
    lblKeys.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    
    label = new Label(gDB,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.db.values");    
    
    lblValues = new Label(gDB,SWT.NONE);
    lblValues.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    
    label = new Label(gDB,SWT.NONE);
    Messages.setLanguageText(label,"TableColumn.header.size");    
    
    lblSize = new Label(gDB,SWT.NONE);
    lblSize.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));    
    
    label = new Label(gDB,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.db.local");    
    
    lblLocal = new Label(gDB,SWT.NONE);
    lblLocal.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    
    label = new Label(gDB,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.db.direct");    
    
    lblDirect = new Label(gDB,SWT.NONE);
    lblDirect.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    
    label = new Label(gDB,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.db.indirect");    
    
    lblIndirect = new Label(gDB,SWT.NONE);
    lblIndirect.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false)); 
    
    
    label = new Label(gDB,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.db.divfreq");    
    
    lblDivFreq = new Label(gDB,SWT.NONE);
    lblDivFreq.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    
    label = new Label(gDB,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.db.divsize");    
    
    lblDivSize = new Label(gDB,SWT.NONE);
    lblDivSize.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
  }
  
  private void initialiseTransportDetailsGroup() {
    Group gTransport = new Group(panel,SWT.NONE);
    Messages.setLanguageText(gTransport,"DHTView.transport.title");
    
    GridData data = new GridData(GridData.FILL_VERTICAL);
    data.widthHint = 350;
    data.verticalSpan = 2;
    gTransport.setLayoutData(data);
    
    GridLayout layout = new GridLayout();
    layout.numColumns = 3;    
    layout.makeColumnsEqualWidth = true;
    gTransport.setLayout(layout);
    
    
    Label label = new Label(gTransport,SWT.NONE);
    
    label = new Label(gTransport,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.transport.packets");
    label.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    
    label = new Label(gTransport,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.transport.bytes");
    label.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    
    label = new Label(gTransport,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.transport.received");
    
    lblReceivedPackets = new Label(gTransport,SWT.NONE);
    lblReceivedPackets.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    
    lblReceivedBytes = new Label(gTransport,SWT.NONE);
    lblReceivedBytes.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    
    label = new Label(gTransport,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.transport.sent");
    
    lblSentPackets = new Label(gTransport,SWT.NONE);
    lblSentPackets.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    
    lblSentBytes = new Label(gTransport,SWT.NONE);
    lblSentBytes.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    
    label = new Label(gTransport,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.transport.in");
    data = new GridData();
    data.horizontalSpan = 3;
    label.setLayoutData(data);
    
    
    in = new Canvas(gTransport,SWT.NO_BACKGROUND);
    data = new GridData(GridData.FILL_BOTH);
    data.horizontalSpan = 3;
    in.setLayoutData(data);
    inGraph = SpeedGraphic.getInstance();
    inGraph.initialize(in);
    
    label = new Label(gTransport,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.transport.out");
    data = new GridData();
    data.horizontalSpan = 3;
    label.setLayoutData(data);
    
    out = new Canvas(gTransport,SWT.NO_BACKGROUND);
    data = new GridData(GridData.FILL_BOTH);
    data.horizontalSpan = 3;
    out.setLayoutData(data);
    outGraph = SpeedGraphic.getInstance();
    outGraph.initialize(out);
  }
  
  private void initialiseOperationDetailsGroup() {
    Group gOperations = new Group(panel,SWT.NONE);
    Messages.setLanguageText(gOperations,"DHTView.operations.title");
    gOperations.setLayoutData(new GridData(SWT.FILL,SWT.BEGINNING,true,false));
    
    GridLayout layout = new GridLayout();
    layout.numColumns = 5;
    layout.makeColumnsEqualWidth = true;
    gOperations.setLayout(layout);
    
    
    Label label = new Label(gOperations,SWT.NONE);
    
    label = new Label(gOperations,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.operations.sent");
    label.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    
    label = new Label(gOperations,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.operations.ok");
    label.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    
    label = new Label(gOperations,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.operations.failed");
    label.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    
    label = new Label(gOperations,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.operations.received");
    label.setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    
    
    label = new Label(gOperations,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.operations.ping");
    
    for(int i = 0 ; i < 4 ; i++) {
      lblPings[i] = new Label(gOperations,SWT.NONE);      
      lblPings[i].setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    }
    
    
    label = new Label(gOperations,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.operations.findNode");
    
    for(int i = 0 ; i < 4 ; i++) {
      lblFindNodes[i] = new Label(gOperations,SWT.NONE);      
      lblFindNodes[i].setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    }
    
    
    label = new Label(gOperations,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.operations.findValue");
    
    for(int i = 0 ; i < 4 ; i++) {
      lblFindValues[i] = new Label(gOperations,SWT.NONE);      
      lblFindValues[i].setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    }
    
    
    label = new Label(gOperations,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.operations.store");
    
    for(int i = 0 ; i < 4 ; i++) {
      lblStores[i] = new Label(gOperations,SWT.NONE);      
      lblStores[i].setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    }
    
    label = new Label(gOperations,SWT.NONE);
    Messages.setLanguageText(label,"DHTView.operations.data");
    
    for(int i = 0 ; i < 4 ; i++) {
      lblData[i] = new Label(gOperations,SWT.NONE);      
      lblData[i].setLayoutData(new GridData(SWT.FILL,SWT.TOP,true,false));
    }
  }
  
  private void initialiseActivityGroup() {
    Group gActivity = new Group(panel,SWT.NONE);
    Messages.setLanguageText(gActivity,"DHTView.activity.title");
    gActivity.setLayoutData(new GridData(SWT.FILL,SWT.FILL,true,true));
    gActivity.setLayout(new GridLayout());
    
    activityTable = new Table(gActivity,SWT.VIRTUAL | SWT.BORDER | SWT.FULL_SELECTION | SWT.SINGLE);
    activityTable.setLayoutData(new GridData(GridData.FILL_BOTH));
    
    final TableColumn colStatus =  new TableColumn(activityTable,SWT.LEFT);
    Messages.setLanguageText(colStatus,"DHTView.activity.status");
    colStatus.setWidth(80);
    
    final TableColumn colType =  new TableColumn(activityTable,SWT.LEFT);
    Messages.setLanguageText(colType,"DHTView.activity.type");
    colType.setWidth(80);
    
    final TableColumn colName =  new TableColumn(activityTable,SWT.LEFT);
    Messages.setLanguageText(colName,"DHTView.activity.target");
    colName.setWidth(80);
    
    final TableColumn colDetails =  new TableColumn(activityTable,SWT.LEFT);
    Messages.setLanguageText(colDetails,"DHTView.activity.details");
    colDetails.setWidth(300);
    colDetails.setResizable(false);
    
    
    activityTable.setHeaderVisible(true);
    Listener computeLastRowWidthListener = new Listener() {
    	// inUse flag to prevent a SWT stack overflow.  For some reason
    	// the setWidth call was triggering a resize.
    	boolean inUse = false;
      public void handleEvent(Event event) {
      	if (inUse) {
      		return;
      	}

      	inUse = true;
       	try {
          if(activityTable == null || activityTable.isDisposed()) return;
          int totalWidth = activityTable.getClientArea().width;
          int remainingWidth = totalWidth 
                                 - colStatus.getWidth()
                                 - colType.getWidth()
                                 - colName.getWidth();
          if(remainingWidth > 0)
            colDetails.setWidth(remainingWidth);

       	} finally {
      		inUse = false;
      	}
      }
    };
    activityTable.addListener(SWT.Resize, computeLastRowWidthListener);    
    colStatus.addListener(SWT.Resize,computeLastRowWidthListener);
    colType.addListener(SWT.Resize,computeLastRowWidthListener);
    colName.addListener(SWT.Resize,computeLastRowWidthListener);
    
    activityTable.addListener(SWT.SetData, new Listener() {
      public void handleEvent(Event event) {
        TableItem item = (TableItem) event.item;
        int index = activityTable.indexOf (item);
        item.setText (0,MessageText.getString("DHTView.activity.status." + activities[index].isQueued()));
        item.setText (1,MessageText.getString("DHTView.activity.type." + activities[index].getType()));
        item.setText (2,ByteFormatter.nicePrint(activities[index].getTarget()));
        item.setText (3,activities[index].getDescription());
      }
    });
    
  }
  

  public void delete() {
    Utils.disposeComposite(panel);
    if (dht != null) {
      dht.getControl().removeListener(controlListener);
    }
    outGraph.dispose();
    inGraph.dispose();
  }

  public String getFullTitle() {
	  if ( dht_type == DHT_TYPE_MAIN ){

		  return( "DHTView.title.full" );

	  }else if ( dht_type == DHT_TYPE_CVS ){

		  return( "DHTView.title.fullcvs" );
	  }else{

		  return( "DHTView.title.full_v6" );
	  }
  }
  
  public Composite getComposite() {
    return panel;
  }
  
  public void refresh() {    
    if(dht == null) { 
      init();
      return;
    }
    
    inGraph.refresh();
    outGraph.refresh();
    
    refreshGeneral();
    refreshDB();
    refreshTransportDetails();
    refreshOperationDetails();
    refreshActivity();
  }  
  
  private void refreshGeneral() {
    DHTControlStats controlStats = dht.getControl().getStats();
    DHTRouterStats routerStats = dht.getRouter().getStats();
    DHTTransport transport = dht.getTransport();
    DHTTransportStats transportStats = transport.getStats();
    lblUpTime.setText(TimeFormatter.format(controlStats.getRouterUptime() / 1000));
    lblNumberOfUsers.setText("" + controlStats.getEstimatedDHTSize());
    int percent = transportStats.getRouteablePercentage();
    lblReachable.setText((transport.isReachable()?yes_str:no_str) + (percent==-1?"":(" " + percent+"%")));
    
    DHTNATPuncher puncher = dht.getNATPuncher();
    
    String	puncher_str;
    
    if ( puncher == null ){
    	puncher_str = "";
    }else{
    	puncher_str = puncher.operational()?yes_str:no_str;
    }
    
    lblRendezvous.setText(transport.isReachable()?"":puncher_str);
    long[] stats = routerStats.getStats();
    lblNodes.setText("" + stats[DHTRouterStats.ST_NODES]);
    lblLeaves.setText("" + stats[DHTRouterStats.ST_LEAVES]);
    lblContacts.setText("" + stats[DHTRouterStats.ST_CONTACTS]);
    lblReplacements.setText("" + stats[DHTRouterStats.ST_REPLACEMENTS]);
    lblLive.setText("" + stats[DHTRouterStats.ST_CONTACTS_LIVE]);
    lblUnknown.setText("" + stats[DHTRouterStats.ST_CONTACTS_UNKNOWN]);
    lblDying.setText("" + stats[DHTRouterStats.ST_CONTACTS_DEAD]);
    
    long skew_average = transportStats.getSkewAverage();
    
    lblSkew.setText( skew_average==0?"":(skew_average<0?"-":"") + TimeFormatter.format100ths( Math.abs(skew_average )));
  }
  
  private int refreshIter = 0;
  
  private void refreshDB() {    
    if(refreshIter == 0) {
	  DHTDBStats    dbStats = dht.getDataBase().getStats();
      lblKeys.setText("" + dbStats.getKeyCount() + " (" + dbStats.getLocalKeyCount() + ")" );  
      int[] stats = dbStats.getValueDetails();
      lblValues.setText("" + stats[DHTDBStats.VD_VALUE_COUNT]);
      lblSize.setText(DisplayFormatters.formatByteCountToKiBEtc(dbStats.getSize()));
      lblDirect.setText(DisplayFormatters.formatByteCountToKiBEtc( stats[DHTDBStats.VD_DIRECT_SIZE]));
      lblIndirect.setText(DisplayFormatters.formatByteCountToKiBEtc( stats[DHTDBStats.VD_INDIRECT_SIZE]));
      lblLocal.setText(DisplayFormatters.formatByteCountToKiBEtc( stats[DHTDBStats.VD_LOCAL_SIZE]));
      
      DHTStorageAdapter sa = dht.getStorageAdapter();
      
      String rem_freq;
      String rem_size;
      
      if ( sa == null ){
    	  rem_freq = "?";
    	  rem_size = "?";
      }else{
    	  rem_freq = "" + sa.getRemoteFreqDivCount();
    	  rem_size = "" + sa.getRemoteSizeDivCount(); 
      }
      
      lblDivFreq.setText("" + stats[DHTDBStats.VD_DIV_FREQ] + " (" + rem_freq + ")");
      lblDivSize.setText("" + stats[DHTDBStats.VD_DIV_SIZE] + " (" + rem_size + ")");
    } else {
      refreshIter++;
      if(refreshIter == 100) refreshIter = 0;
    }

  }

  private void refreshTransportDetails() {
    DHTTransportStats   transportStats = dht.getTransport().getStats();
    lblReceivedBytes.setText(DisplayFormatters.formatByteCountToKiBEtc(transportStats.getBytesReceived()));
    lblSentBytes.setText(DisplayFormatters.formatByteCountToKiBEtc(transportStats.getBytesSent()));
    lblReceivedPackets.setText("" + transportStats.getPacketsReceived());
    lblSentPackets.setText("" + transportStats.getPacketsSent());
  }
  
  private void refreshOperationDetails() {   
    DHTTransportStats   transportStats = dht.getTransport().getStats();
    long[] pings = transportStats.getPings();
    for(int i = 0 ; i < 4 ; i++) {
      lblPings[i].setText("" + pings[i]);
    }
    
    long[] findNodes = transportStats.getFindNodes();
    for(int i = 0 ; i < 4 ; i++) {
      lblFindNodes[i].setText("" + findNodes[i]);
    }
    
    long[] findValues = transportStats.getFindValues();
    for(int i = 0 ; i < 4 ; i++) {
      lblFindValues[i].setText("" + findValues[i]);
    }
    
    long[] stores = transportStats.getStores();
    for(int i = 0 ; i < 4 ; i++) {
      lblStores[i].setText("" + stores[i]);
    }
    long[] data = transportStats.getData();
    for(int i = 0 ; i < 4 ; i++) {
      lblData[i].setText("" + data[i]);
    }
  }
  
  private void refreshActivity() {
    if(activityChanged) {
      activityChanged = false;
      activities = dht.getControl().getActivities();
      activityTable.setItemCount(activities.length);
      activityTable.clearAll();
      //Dunno if still needed?
      activityTable.redraw();  
    }    
  }
  
  public void periodicUpdate() {
    if(dht == null) return;
    
    DHTTransportFullStats fullStats = dht.getTransport().getLocalContact().getStats();
    inGraph.addIntValue((int)fullStats.getAverageBytesReceived());
    outGraph.addIntValue((int)fullStats.getAverageBytesSent());
  }
  
  public String getData() {
	  return( getFullTitle());
  }
}


