/****************************************************************************
**  SCALASCA    http://www.scalasca.org/                                   **
*****************************************************************************
**  Copyright (c) 1998-2012                                                **
**  Forschungszentrum Juelich GmbH, Juelich Supercomputing Centre          **
**                                                                         **
**  See the file COPYRIGHT in the package base directory for details       **
****************************************************************************/


#include "TreeWidget.h"
#include "Tetragon.h"
#include "ScrollArea.h"
#include "SystemTopologyWidget.h"
#include "PrecisionWidget.h"
#include "ValueWidget.h"

#include "Cartesian.h"
#include "Sysres.h"
#include "Thread.h"
#include "Process.h"
#include "Node.h"

#include <QScrollBar>
#include <QSettings>
#include <QMessageBox>

#include <cassert>
#include <qdebug.h>

/*************** constructor / destructor *************************/

SystemTopologyWidget::SystemTopologyWidget(QWidget* widget, unsigned topologyId) : QWidget(widget){
   
   setMinimumSize(50,100);

   offscreen = NULL;
   //to handle keyboard input
   setFocusPolicy(Qt::StrongFocus);
   setMouseTracking(false);
   
   //the current dimensions
   dim[0] = 0;
   dim[1] = 0;
   dim[2] = 0;
   //the current order of the dimension
   dimToOrigDim[0] = 0;
   dimToOrigDim[1] = 1;
   dimToOrigDim[2] = 2;
   //which original dimension is at which position currently
   origDimToDim[0] = 0;
   origDimToDim[1] = 1;
   origDimToDim[2] = 2;
   
   selectMode = 0; // if > 3 dimensions: select dimensions to display
   tbar = 0;
   invalidDimensions = 0;
   
   systemTreeWidget = NULL;
   scrollArea = NULL;
   
   leftMousePressed = false;
   rightMousePressed = false;
   shiftPressed = false;
   controlPressed = false;
   toSelect = false;
   
   this->topologyId = topologyId;
   planeDistance = 10.0;
   xAngle = 300;
   yAngle = 30;
   userDefinedMinMaxValues = false;
   
   colors.clear();
   selected_rects.clear();
   
   
   setWhatsThis("In many parallel applications, each process (or thread)"
                " communicates only with a limited number of other processes. The parallel"
                " algorithm divides the application domain into smaller chunks known as"
                " sub-domains and a process usually communicates with processes owning"
                " sub-domains adjacent to its own. The mapping of data onto processes and"
                " the neighborhood relationship resulting from this mapping is called a"
                " \"virtual topology\". Many applications use one or more virtual"
                " topologies specified as one-, two- or three-dimensional Cartesian"
                " grids.\n\nAnother kind of topology are physical topologies reflecting"
                " the hardware infrastructure on which the application was run.  A typical"
                " three-dimensional physical topology is given by the (hardware) nodes in"
                " the first dimension, and the arrangement of cores/processors on nodes in"
                " further two dimensions.\n\nThe Cube display supports one-, two-, and"
                " three-dimensional Cartesian grids. If the currently opened cube file"
                " defines such a topology, the topology tab shows performance data mapped"
                " onto the Cartesian topology of the application. The corresponding grid"
                " is specified by the number of dimensions and the size of each dimension."
                " Threads/processes are attached to the grid elements, as specified by the"
                " Cube file. Not all system items have to be attached to a grid element,"
                " and not every grid element has a system item attached.\n\nThe Cartesian"
                " grid is presented by planes stacked on top of each other in a"
                " three-dimensional projection. The number of planes depends on the number"
                " of dimensions in the grid. Each plane is divided into squares (typically"
                " shown as rombi) and the number of squares depends on the dimension size."
                " Each square represents a system resource (e.g, a process or thread) of"
                " the application and has a coordinate associated with it.\n\nThe current"
                " value of each grid element (with respect to the selections on the"
                " left-hand-side and to the current value mode) is represented by coloring"
                " the grid element. To make use of the whole color scale, coloring in"
                " topologies in the absolute value mode is based on the minimal and the"
                " maximal system leaf values, instead of considering all system items, as"
                " for the system tree coloring. In all other value modes, coloring is"
                " based on a value scale from 0.0 to 100.0.  Grid elements without having"
                " a system item attached to it are colored gray.\n\nIf the selected system"
                " item (or the first selected one in case of multiple selection) occurs in"
                " the topology, it is marked by an additional frame and by additional"
                " lines at the side of the plane which contains the corresponding grid,"
                " such that the selected item's position is also visible even if the"
                " corresponding plane is not completely visible.\n\nBesides the functions"
                " offered by the topology tool bar, the following issues are supported:\n-"
                " Item selection: You can change the current system selection by"
                " left-clicking on a grid element which has a system item assigned to it"
                " (resulting in the selection of that system item).\n-Info: By"
                " right-clicking on a grid element an information widget appears with"
                " information about the system item assigned to it.\n- Rotation about the"
                " x- and y-axes: can be done with left-mouse drag (click and hold the"
                " left-mouse button while moving the mouse).\n- Increasing/decreasing the"
                " distance between the planes: with Ctrl+<left-mouse drag>.\n- Moving the"
                " whole topology up/down/left/right: with Shift+<left-mouse drag>.");
  
}

SystemTopologyWidget::~SystemTopologyWidget(){
   for (unsigned i=0; i<colors.size(); i++)
      for (unsigned j=0; j<colors[i].size(); j++)
         for (unsigned k=0; k<colors[i][j].size(); k++)
            if (colors[i][j][k]!=NULL)
               delete colors[i][j][k];
   colors.clear();
   selected_rects.clear();
}


/******************** angle settings ********************/

int SystemTopologyWidget::getXAngle(){
   return xAngle;
}
int SystemTopologyWidget::getYAngle(){
   return yAngle;
}

void SystemTopologyWidget::setSystemTreeWidget(TreeWidget * systemTreeWidget){
   this->systemTreeWidget = systemTreeWidget;
   valueWidget = systemTreeWidget->getTabWidget()->getValueWidget();
   colorWidget = systemTreeWidget->colorWidget;
}

void SystemTopologyWidget::setScrollArea(ScrollArea* scrollArea){
   this->scrollArea = scrollArea;
}

void SystemTopologyWidget::setXAngleFromExtern(int angle){
   if (xAngle!=angle){
      setXAngle(angle);

      draw();
   }
   diffs.angleChanged = true;
}

//if the x/y angle has been changed internally, then we should
//additionally emit the corresponding signal;
//if it has been changed externally, then the signal is emitted
//by the x/y spin

void SystemTopologyWidget::setXAngleFromIntern(int angle){
   setXAngle(angle);
   emit xAngleChanged(xAngle);
}

void SystemTopologyWidget::setXAngle(int angle){
   plane.yRotate(-yAngle);
   plane.xRotate(-xAngle);
   
   while (angle>=360) angle -= 360;
   while (angle<0) angle += 360;
   xAngle = angle;
   
   plane.xRotate(xAngle);
   plane.yRotate(yAngle);
}

void SystemTopologyWidget::setYAngleFromExtern(int angle){
   if (yAngle!=angle){
      setYAngle(angle);

      draw();
   }
   diffs.angleChanged = true;
}

void SystemTopologyWidget::setYAngleFromIntern(int angle){
   setYAngle(angle);
   emit yAngleChanged(yAngle);
}

void SystemTopologyWidget::setYAngle(int angle){
   plane.yRotate(-yAngle);
   
   while (angle>=360) angle -= 360;
   while (angle<0) angle += 360;
   yAngle = angle;
   
   plane.yRotate(yAngle);
}


/**************************** dimension settings *****************/

//set the original dimensions
void SystemTopologyWidget::setDims(unsigned dim0, unsigned dim1, unsigned dim2){
   dim[0] = dim0;
   dim[1] = dim1;
   dim[2] = dim2;
}


//set the order of dimensions
//
void SystemTopologyWidget::setDimOrder(unsigned dim0, unsigned dim1, unsigned dim2){
   
   dimToOrigDim[0] = dim0;
   dimToOrigDim[1] = dim1;
   dimToOrigDim[2] = dim2;
   
   origDimToDim[0] = (dim0==0 ? 0 : (dim1==0 ? 1 : 2));
   origDimToDim[1] = (dim0==1 ? 0 : (dim1==1 ? 1 : 2));
   origDimToDim[2] = (dim0==2 ? 0 : (dim1==2 ? 1 : 2));
   
   reset(true);
   displayItems();
}



/********************** initialization / clean up ********************************/


/** map to 3 dimensions
    method1: show 3 dimensions and select one index of the rest
    @param coord coordinates in n dimensions
    @param id unique id for each matrix element
*/
void SystemTopologyWidget::multiDim0(const std::vector<long>& coord, unsigned id, std::vector<std::string> dimNames)
{
   // selectedDimensions not set? => show first 3 dimensions and index 0 of the rest
   if (selectedDimensions.size() != coord.size()) {
      selectedDimensions.clear();
      for (uint j=0; j<coord.size(); j++) {
         if (j < 3)
            selectedDimensions.push_back(-1); // display all
         else
            selectedDimensions.push_back(0); // display index 0
      }
   }
   
   bool notSelected = false;
   int c[3];
   int k=0;
   for (unsigned int j=0; j<selectedDimensions.size(); j++) {
      if (selectedDimensions[j] >= 0) { // only one element of this dimension is displayed
         if (coord[j] != selectedDimensions[j]) { // current element is not displayed
            notSelected = true;
            break; // dimension is not displayed
         }
      }
      else
         c[k++] = j;
   }
   if (!notSelected) // element ist visible
   {
      int val[3]; // x,y,z
      val[0] = coord[c[0]];
      val[1] = coord[c[1]];
      val[2] = coord[c[2]];
      coordToSystemId[val[0]][val[1]][val[2]] = (int)(id);
      size_t num_of_coords = systemToCoord[  ( int )( id )  ].size();
      for (int j=0; j<3; j++) {
         systemToCoord[(int)(id)][ num_of_coords - 1 ].push_back(val[j]);
      }
      
      // generate info string for each displayed element with complete coordinates
      QString str;
      str="(";
      for (unsigned j = 0; j < coord.size(); j++){
    	  if(dimNames.size()>0) {
    		  str.append(QString::fromStdString(dimNames[j]));
    		  str.append(": ");
    	  }
         str.append(QString::number(coord[j]));
         if (j<coord.size()-1) str.append(",");
      }
      str.append(")");
      systemToCoordStr[(int)id] = str;
   } // !notSelected
}

/** merge up to 2 dimensions into one, only available for 4-6 dimensions
 */
void SystemTopologyWidget::multiDim1(const std::vector<long>& coord, unsigned id, std::vector<std::string> dimNames)
{
   //get the orginal dimensions
   const std::vector<long>& dims = cube->get_cart(topologyId)->get_dimv();
   int max = 3;
   if (foldingDimensions[2][0] < 0 && foldingDimensions[2][1] < 0)
      max=2;
   
   int idx1,idx2;
   int val[max]; // x,y,[z]
   for (int i=0; i<max; i++) {
      idx1 = foldingDimensions[i][0]; idx2 = foldingDimensions[i][1];
      val[i] = coord[idx1];
      if (idx2 > -1) val[i] = val[i]*dims[idx2] + coord[idx2];
   }
   size_t num_of_coords = systemToCoord[  ( int )( id )  ].size();
   if (max == 3) // x, y and z
      coordToSystemId[val[0]][val[1]][val[2]] = (int)(id);
   else { // only x and y
      coordToSystemId[0][val[0]][val[1]] = (int)(id);
      systemToCoord[(int)(id)][ num_of_coords - 1 ].push_back(0); // empty 3rd dimension necessary
   }
   
   for (int i=0; i<max; i++) {
      systemToCoord[(int)(id)][ num_of_coords - 1 ].push_back(val[i]);
   }
   
   // generate info string for each displayed element with complete coordinates
   QString str;
   str="(";
   for (unsigned j = 0; j < coord.size(); j++){
	   if(dimNames.size()>0) {
		   str.append(QString::fromStdString(dimNames[j]));
		   str.append(": ");
	   }
      str.append(QString::number(coord[j]));
      if (j<coord.size()-1) str.append(",");
   }
   str.append(")");
   systemToCoordStr[(int)id] = str;
   
}

/* initializes the topology with data from the cube object
*/
void SystemTopologyWidget::initialize(cube::Cube * cube)
{
   this->cube = cube;
   reinit();
}

/**

  */
void SystemTopologyWidget::reinit(bool resetValues)
{
   assert(systemTreeWidget!=NULL);
   assert(scrollArea!=NULL);
   
   //get the dimensions
   const std::vector<long>& dims = cube->get_cart(topologyId)->get_dimv();
   const std::vector<std::string> dimNames = cube->get_cart(topologyId)->get_namedims();
   
   plane.setFoldingDimensions(); // reset valus
   //set the dimensions
   if (dims.size()==0) return;
   else if (dims.size()==1) setDims(1,1,(unsigned)(dims[0]));
   else if (dims.size()==2) setDims(1,(unsigned)(dims[0]),(unsigned)(dims[1]));
   else if (dims.size()==3) setDims((unsigned)(dims[0]),(unsigned)(dims[1]),(unsigned)(dims[2]));
   else { // more than 3 dimensions => reduce to 3 to display
      // display 3 dimensions and only one item of the rest
      if (selectMode == 0) {
         int d[3];
         int idx=0;
         if (selectedDimensions.size() == dims.size()) {
            for (uint j=0; j<selectedDimensions.size() && idx < 3; j++) {
               if (selectedDimensions[j] == -1) {
                  d[idx++] = j;
               }
            }
         }
         if (idx == 3) setDims(dims[d[0]], dims[d[1]], dims[d[2]]);
         else setDims((unsigned)(dims[0]),(unsigned)(dims[1]),(unsigned)(dims[2])); // invalid => display first 3
      }
      // folding is selected => merge dimensions
      else if (selectMode == 1) {
         int dim0 = dims[foldingDimensions[0][0]];
         int dim1 = dims[foldingDimensions[1][0]];
         int dim2 = 0;
         if (foldingDimensions[2][0] >= 0) dim2 = dims[foldingDimensions[2][0]];
         
         if (foldingDimensions[0][1] >= 0) dim0 *= dims[foldingDimensions[0][1]];
         if (foldingDimensions[1][1] >= 0) dim1 *= dims[foldingDimensions[1][1]];
         if (foldingDimensions[2][1] >= 0) dim2 *= dims[foldingDimensions[2][1]];
         if (dim2 > 0)
            setDims(dim0, dim1, dim2);
         else
            setDims(1, dim0, dim1);

         // mark merged dimensions
         int div[3];
         for (int i=0; i< 3; i++) {
            div[i] = foldingDimensions[i][1] >= 0 ?
                     dims[foldingDimensions[i][1]] : 1;
         }
         if (dim2 == 0) { // merged to 2-dimensional topology
            div[2] = div[1];
            div[1] = div[0];
            div[0] = 0;
         }

         plane.setFoldingDimensions(div);
      }
   }
   
   assert(dim[0]>0);
   assert(dim[1]>0);
   assert(dim[2]>0);
   
   //initialize the color matrix
   
   for (unsigned i=0; i<colors.size(); i++)
      for (unsigned j=0; j<colors[i].size(); j++)
         for (unsigned k=0; k<colors[i][j].size(); k++)
            if (colors[i][j][k]!=NULL)
               delete colors[i][j][k];
   colors.clear();
   selected_rects.clear();
   colors.resize(dim[0]);
   selected_rects.resize( dim[ 0 ] );   
   for (unsigned i=0; i<dim[0]; i++) {
      colors[i].resize(dim[1]);
      selected_rects[ i ].resize( dim[ 1 ] );
      for (unsigned j=0; j<dim[1]; j++){
         colors[i][j].resize(dim[2]);
         selected_rects[ i ][ j ].resize( dim[ 2 ] );	 
         for (unsigned k=0; k<dim[2]; k++){
            colors[i][j][k] = NULL;
	    selected_rects[ i ][ j ][ k ] = false;
         }
      }
   }
   
   //initialize the topology;
   //the value -1 denotes that at that node no thread/process was running
   coordToSystemId.resize(dim[0]);
   for (unsigned i=0; i<dim[0]; i++){
      coordToSystemId[i].resize(dim[1]);
      for (unsigned j=0; j<dim[1]; j++){
         coordToSystemId[i][j].resize(dim[2]);
         for (unsigned k=0; k<dim[2]; k++){
            coordToSystemId[i][j][k] = -1;
         }
      }
   }
   
   const std::multimap<const cube::Sysres*, std::vector<long> >& cubeSystemToCoord =
         cube->get_cart(topologyId)->get_cart_sys();
   std::multimap<const cube::Sysres*, std::vector<long> >::const_iterator itr;
   
   systemToCoord.clear();
   
   systemToCoordStr.clear();
   systemToCoordStr.resize((int)(systemTreeWidget->items.size()));
   
   std::vector<TreeWidgetItem*> & items = systemTreeWidget->items;
   for (unsigned i=0; i<items.size(); i++)
   {
      //toCompute tells if the system item is a thread or
      //a process in the single-threaded case
      bool toCompute = false;
      
      if (items[i]->type==THREADITEM){
         itr = cubeSystemToCoord.find((cube::Sysres*)(items[i]->cubeObject));
         toCompute = true;
      } else if (items[i]->type==PROCESSITEM && items[i]->childCount()==0){
         //single-threaded processes
         if (((cube::Process*)(items[i]->cubeObject))->num_children()==1){
            //the topology can be defined either on processes or on threads, we
            //therefore check both...
            itr = cubeSystemToCoord.find((cube::Sysres*)(((cube::Process*)(items[i]->cubeObject))->get_child(0)));
            if (itr == cubeSystemToCoord.end())
               itr = cubeSystemToCoord.find((cube::Sysres*)(items[i]->cubeObject));
            toCompute = true;
         }
      }
      //if yes, then check if the item is in the topology, and
      //eventually store its topology position
      if (toCompute) {
            std::multimap <const cube::Sysres*, std::vector<long> >::const_iterator                                                                                     it;
            std::pair< std::multimap<const cube::Sysres*, std::vector<long> >::const_iterator, std::multimap<const cube::Sysres*, std::vector<long> >::const_iterator > ret;

            ret = cubeSystemToCoord.equal_range( itr->first );
            if (  ret.first  != ret.second )
            {
                for ( it = ret.first; it != ret.second; ++it ) // run over all coordinates of this thread
                {
		    const std::vector<long>& coord = it->second;
		    
		    systemToCoord[ ( int )( items[ i ]->id ) ].push_back( std::vector< long > ()  );
                    size_t num_of_coords = systemToCoord[ ( int )( items[ i ]->id ) ].size();
		    
		    if (coord.size()==1){
			coordToSystemId[0][0][coord[0]] = (int)(items[i]->id);
			systemToCoord[(int)(items[i]->id)][ num_of_coords - 1 ].push_back(0);
			systemToCoord[(int)(items[i]->id)][ num_of_coords - 1 ].push_back(0);
			systemToCoord[(int)(items[i]->id)][ num_of_coords - 1 ].push_back(coord[0]);
		    } else if (coord.size()==2){
			coordToSystemId[0][coord[0]][coord[1]] = (int)(items[i]->id);
			systemToCoord[(int)(items[i]->id)][ num_of_coords - 1 ].push_back(0);
			systemToCoord[(int)(items[i]->id)][ num_of_coords - 1 ].push_back(coord[0]);
			systemToCoord[(int)(items[i]->id)][ num_of_coords - 1 ].push_back(coord[1]);
		    } else if (coord.size()==3){
			coordToSystemId[coord[0]][coord[1]][coord[2]] = (int)(items[i]->id);
			systemToCoord[(int)(items[i]->id)][ num_of_coords - 1 ].push_back(coord[0]);
			systemToCoord[(int)(items[i]->id)][ num_of_coords - 1 ].push_back(coord[1]);
			systemToCoord[(int)(items[i]->id)][ num_of_coords - 1 ].push_back(coord[2]);
		    } else if (coord.size() > 3) {
		    if (selectMode == 0)
			multiDim0(coord, items[i]->id,dimNames);
		    else
			multiDim1(coord, items[i]->id,dimNames);
		    }
		}
	    }
      } //toCompute
   }
   
   //we store in the tab widget if unused topology planes should be painted
   if (!systemTreeWidget->getTabWidget()->getShowUnusedTopologyPlanes())
      toggleUnused();
   
   updateColors();
   updateSelection();
   reset(resetValues);
} // reinit

/**
  initializes the plane
  sets its size to its minimum size (min pixels for each element)
  */
void SystemTopologyWidget::initPlane() {   
   if (dim[0]==0 || dim[1]==0 || dim[2]==0) return;

   const int min = 2;
   double x = min * (double)dim[dimToOrigDim[1]];
   
   //get height according to width
   double y = x/(double)dim[dimToOrigDim[1]] * (double)dim[dimToOrigDim[2]];
   //minimal height is 3 pixels per square
   if (y < min * (double)dim[dimToOrigDim[2]]) {
      y = min * (double)dim[dimToOrigDim[2]];
      x = y / (double)dim[dimToOrigDim[2]] * (double)dim[dimToOrigDim[1]];
   }
   
   //get the middle
   x = x/2;
   y = y/2;
   
   
   //the four plane corners are the points 0 to 3
   plane.getPoint(0)->setX(-x);
   plane.getPoint(0)->setY(-y);
   plane.getPoint(0)->setZ(0);
   
   plane.getPoint(1)->setX(x);
   plane.getPoint(1)->setY(-y);
   plane.getPoint(1)->setZ(0);
   
   plane.getPoint(2)->setX(x);
   plane.getPoint(2)->setY(y);
   plane.getPoint(2)->setZ(0);
   
   plane.getPoint(3)->setX(-x);
   plane.getPoint(3)->setY(y);
   plane.getPoint(3)->setZ(0);
   
   //plane point 4 is the middle, needed for certain operations like y
   plane.getPoint(4)->setX(0);
   plane.getPoint(4)->setY(0);
   plane.getPoint(4)->setZ(0);
   
   plane.xRotate(xAngle);
   plane.yRotate(yAngle);
   
}

/**
  rescales the drawing to fit into the widget and resets scrollbar position to top left
  @param resetValues if true (default), resets angles of topology and plane distance
  */
void SystemTopologyWidget::reset(bool resetValues){
   assert(scrollArea!=NULL);
   assert(systemTreeWidget!=NULL);

   if (resetValues) {
      diffs.reset();
      scrollArea->horizontalScrollBar()->setValue(0);
      scrollArea->verticalScrollBar()->setValue(0);
   }

   // only reset the angle if desired or not yet changed
   if (resetValues || !diffs.angleChanged) {
      if (dim[0]>1){
         setXAngleFromIntern(300);
         setYAngleFromIntern(30);
      } else {
         setXAngleFromIntern(0);
         setYAngleFromIntern(0);
      }
   }

   if ((diffs.scale < 1.2)  &&   // user has not manually resized to show more details
       (!diffs.distanceChanged)) // user has not changed the plane distance
      rescalePlane(resetValues);
}


void SystemTopologyWidget::cleanUp(){
   setDims(0,0,0);

   draw();
}


/************************* coloring  ***********************/

//get the color for the item with the given id
//
QColor* SystemTopologyWidget::getColor(unsigned id){
   
   assert(systemTreeWidget->items.size()>=id);
#if 0
   bool ok;
   double value;
   //get the current value from the system tree widget
   systemTreeWidget->getValue(systemTreeWidget->items[id],false,value,ok);
   
   QColor* color;
   
   //undefined values get the color gray
   if (!ok){
      color = new QColor(Qt::gray);
   } else {
      
      //colors represent the (math) absolute value
      if (value<0.0) value = -value;
      assert(value>=0.0);
      
      //compute value for minimal color
      double min;
      if (userDefinedMinMaxValues) min = userMinValue;
      else if (systemTreeWidget->tabWidget->getValueModus()==ABSOLUTE) min = minValue;
      else min = 0.0;
      
      //compute value for maximal color
      double max;
      if (userDefinedMinMaxValues) max = userMaxValue;
      else if (systemTreeWidget->tabWidget->getValueModus()==ABSOLUTE) max = maxValue;
      else max = 100.0;
      
      color = new QColor(colorWidget->getColor(value,
                                               min,
                                               max,
                                               systemTreeWidget->getTabWidget()->getWhiteForZero()
                                               ));
   }
   return color;
#else
   return new QColor(systemTreeWidget->items[id]->getColor());
#endif
}


void SystemTopologyWidget::updateColors(){
   
   if (dim[0]==0 || dim[1]==0 || dim[2]==0) return;
   
   assert(colors.size()==dim[0]);
   assert(colors[0].size()==dim[1]);
   assert(colors[0][0].size()==dim[2]);
   
   bool first = true;
   bool firstAbs = true;
   bool ok;
   double value;
   
   //since we consider threads or single-threaded processes only,
   //the min/max values differ from that of the system tree,
   //thus we must compute them;
   minValue = 0.0;
   maxValue = 0.0;
   minAbsValue = 0.0;
   maxAbsValue = 0.0;

   //compute minimal and maximal values
   
   for (unsigned i0=0; i0<dim[0]; i0++){
      for (unsigned i1=0; i1<dim[1]; i1++){
         for (unsigned i2=0; i2<dim[2]; i2++){
            //get the id of the item at the current topology position
            int systemId = coordToSystemId[i0][i1][i2];
            if (systemId>=0) {
               //get its absolute value from the system topology widget
               systemTreeWidget->getValue(systemTreeWidget->items[systemId],false,value,ok,ABSOLUTE);
               //if the value is defined, update the variables storing the
               //minimal and maximal absolute values
               if (ok) {
                  if (firstAbs){
                     firstAbs = false;
                     minAbsValue = value;
                     maxAbsValue = value;
                  } else {
                     if (minAbsValue>value) minAbsValue = value;
                     else if (maxAbsValue<value) maxAbsValue = value;
                  }
               }
               //if the current value modus is not the absolute value modus
               if (!systemTreeWidget->getTabWidget()->getValueModus() == ABSOLUTE){
                  //get the current value for the item at the current
                  //topology position from the system tree widget
                  systemTreeWidget->getValue(systemTreeWidget->items[systemId],false,value,ok);
                  //if the value is defined, then update the variables for
                  //the minimal and maximal values
                  if (ok){
                     if (first){
                        first = false;
                        minValue = value;
                        maxValue = value;
                     } else {
                        if (minValue>value) minValue = value;
                        else if (maxValue<value) maxValue = value;
                     }
                  }
               }
            }
         }
      }
   }
   if (minValue < 0.0 ) minValue = 0.0;
   if (minAbsValue < 0.0) minAbsValue = 0.0;
   
   if (systemTreeWidget->getTabWidget()->getValueModus()==ABSOLUTE){
      minValue = minAbsValue;
      maxValue = maxAbsValue;
   }
   
   
   //determine the colors
   for (unsigned i0=0; i0<dim[0]; i0++){
      for (unsigned i1=0; i1<dim[1]; i1++){
         for (unsigned i2=0; i2<dim[2]; i2++){
            if (colors[i0][i1][i2]!=NULL)
               delete colors[i0][i1][i2];
            int systemId = coordToSystemId[i0][i1][i2];
            if (systemId<0) colors[i0][i1][i2] = new QColor(Qt::lightGray);
            else colors[i0][i1][i2] = getColor((unsigned)systemId);
         }
      }
   }

   updateValueWidget();
}




void
SystemTopologyWidget::updateSelection()
{
    //determine the colors
    for ( unsigned i0 = 0; i0 < dim[ 0 ]; i0++ ){
        for ( unsigned i1 = 0; i1 < dim[ 1 ]; i1++ ){
            for ( unsigned i2 = 0; i2 < dim[ 2 ]; i2++ ){
                int systemId = coordToSystemId[ i0 ][ i1 ][ i2 ];
                if ( systemId < 0 ){
                    selected_rects[ i0 ][ i1 ][ i2 ] = false;
                }
                else{
                    bool                         marked        = false;
                    std::vector<TreeWidgetItem*> _marked_items =  systemTreeWidget->selectedItems();
                    for ( std::vector<TreeWidgetItem*>::iterator iter = _marked_items.begin(); iter != _marked_items.end(); iter++ ){
                        if ( ( int )( ( *iter )->id ) == systemId ){
                            marked = true;
                            break;
                        }
                    }
                    selected_rects[ i0 ][ i1 ][ i2 ] = marked;
                }
            }
        }
    }
}







/****************** painting ************************/



//updates the value widget below the tab widget
//
void SystemTopologyWidget::updateValueWidget(){
   
   //in case the topology is empty or if no items are selected, just
   //clear the vlaue widget
   if (dim[0]==0 || dim[1]==0 || dim[2]==0 || systemTreeWidget->selectedItems().size()==0) {
      
      valueWidget->clear();
      
   } else {
      
      double _sigma = 0.0;
      double _variance = 0.0;
      double _mean = 0.0;
      
      double tmp = 0.;
      bool   _ok = true;
      
      unsigned N = 0;
      for (int i = 0; i < (int)systemTreeWidget->items.size(); i++)
      {
         TreeWidgetItem* item = systemTreeWidget->items.at(i);
         if (item->childCount() != 0) continue;
         
         systemTreeWidget->getValue(item ,item->isExpanded(),tmp,_ok,ABSOLUTE);
         
         _mean += tmp;N++;
      }
      
      _mean = (N!= 0)?_mean/N:0.;
      
      for (int i = 0; i < (int)systemTreeWidget->items.size(); i++)
      {
         TreeWidgetItem* item = systemTreeWidget->items.at(i);
         if (item->childCount() != 0) continue;
         
         systemTreeWidget->getValue(item ,item->isExpanded(),tmp,_ok,ABSOLUTE);
         
         _variance += (_mean - tmp) * (_mean - tmp);
      }
      _sigma = (N!= 0)?sqrt(_variance / N):0.;
      
      
      double value = 0.0;
      double absoluteValue = 0.0;
      bool ok = false;
      bool intType = systemTreeWidget->hasIntegerType();
      
      //in the topology we handle single-selection only: we consider the
      //value of the first selected item (first not in the index but in
      //the time order)
      
      if (systemTreeWidget->selectedItems().size()>0){
         TreeWidgetItem* item = systemTreeWidget->selectedItems().at(0);
         intType = systemTreeWidget->hasAbsoluteIntegerType(item);
         
         systemTreeWidget->getValue(item,false,absoluteValue,ok,ABSOLUTE);
         
         if (ok && systemTreeWidget->getTabWidget()->getValueModus()!=ABSOLUTE)
            systemTreeWidget->getValue(item,false,value,ok);
      }
      
      //compute value for minimal color
      double min;
      if (userDefinedMinMaxValues) min = userMinValue;
      else if (systemTreeWidget->tabWidget->getValueModus()==ABSOLUTE) min = minValue;
      else min = 0.0;
      
      //compute value for minimal color
      double max;
      if (userDefinedMinMaxValues) max = userMaxValue;
      else if (systemTreeWidget->tabWidget->getValueModus()==ABSOLUTE) max = maxValue;
      else max = 100.0;
      
      if (!ok) {
         //if one of the values was undefined, display only the min and
         //max values
         valueWidget->update(min, max, intType, false, _mean, _sigma);
      } else if (systemTreeWidget->getTabWidget()->getValueModus()==ABSOLUTE){
         //in the absolute value mode display the absolute value (with
         //the min/max values)
         valueWidget->update(min,max,absoluteValue,intType,false, _mean, _sigma);
      } else {
         //in value modes other that the absolute value mode display the
         //current minvalue/value/maxvalue plus absolute
         //minvalue/value/maxvalue
         valueWidget->update(0.0,100.0,value,minAbsValue,maxAbsValue,absoluteValue,intType,false, _mean, _sigma);
      }
   }
}

/**
 scales the topology to fit into the window
 @param resetValue recalculate plance distance, if true
*/
void SystemTopologyWidget::rescalePlane(bool resetValues){
   if (scrollArea->width()<=0 || scrollArea->height()<=0)
      return;
   
   bool recalculatePlaneDistance = (resetValues || !diffs.distanceChanged);

   initPlane(); // scale plane to minimum size

   double minX,maxX,minY,maxY;
   plane.getCornerPoints(minX,maxX,minY,maxY);

   assert(minX<=maxX);
   assert(minY<=maxY);
   assert(maxX - minX > 0);
   assert(maxY - minY > 0);
   
   //current total width and height
   double sumW = maxX-minX;
   double sumH = maxY-minY; // height of topmost plane
   double planeH = (dim[dimToOrigDim[0]]-1) * (recalculatePlaneDistance ? 1 : planeDistance); // height to display n planes
   
   int scrollW = scrollArea->verticalScrollBar()->width();
   int scrollH = scrollArea->horizontalScrollBar()->height();
   
   double xScaleFactor = (scrollArea->width() - scrollW - 2 * TOPOLOGY_MARGIN) / sumW;
   double yScaleFactor = (scrollArea->height() - planeH - scrollH - 2 * TOPOLOGY_MARGIN) / sumH ;
   
   double scaleFactor = (xScaleFactor<yScaleFactor ? xScaleFactor : yScaleFactor);
   /* scale factor of 1 shows the plane with its minimal size */
   if (scaleFactor < 1.0) scaleFactor = 1.0;
   
   plane.xScroll(-minX);
   plane.yScroll(-minY);
   plane.scale(0,0,0,scaleFactor);
   plane.xScroll(TOPOLOGY_MARGIN);
   plane.yScroll(TOPOLOGY_MARGIN);
   
   // only reset the plane distance if desired or not yet changed
   if (recalculatePlaneDistance) {
      if (dim[dimToOrigDim[0]]>1){
         plane.getCornerPoints(minX,maxX,minY,maxY);
         planeDistance = ((double)(scrollArea->height())-
                          (maxY-minY) - scrollH - 2 * TOPOLOGY_MARGIN)/(double)(dim[dimToOrigDim[0]]-1);
         if (planeDistance <= 1.0) planeDistance = 1.0;
      } else {
         planeDistance = 1.0;
      }
   }
   draw();
}

void SystemTopologyWidget::displayItems() {
   updateColors();
    updateSelection();
   draw();
}

int SystemTopologyWidget::getOptimalWidth(){
   double minX, maxX, minY, maxY;
   plane.getCornerPoints(minX,maxX,minY,maxY);
   int result = (int)maxX + 5;
   if (result<minimumWidth()) result = minimumWidth();
   return result;
}

void SystemTopologyWidget::rescaleWidget()
{
   double minX, maxX, minY, maxY;
   plane.getCornerPoints(minX,maxX,minY,maxY);
   maxX += 5;
   maxY += (double)(dim[dimToOrigDim[0]]-1) * planeDistance + 5;
   
   int w = scrollArea->width() - scrollArea->verticalScrollBar()->width() - 5;
   int h = scrollArea->height() - scrollArea->horizontalScrollBar()->height() - 5;
   if (w<0) w = 0;
   if (h<0) h = 0;
   
   if ((int)maxX > w) w = (int)maxX;
   if ((int)maxY > h) h = (int)maxY;
   
   resize(w,h);
}

/**
  If no mouse button is pressed, the image data hasn't changed and the image
  can be copied from offscreen image
  If a mouse button is pressed, the image is painted directly on screen to avoid
  unnecessary copy steps
  */
void SystemTopologyWidget::paintEvent(QPaintEvent*)
{
   QPainter painter(this);

   if (offscreen == 0) reset(true); // reset values and draw on offscreen image

   painter.fillRect(rect(), this->palette().brush(QPalette::Window));

   // mouse action cannot be handled with offscreen image
   if (rightMousePressed || leftMousePressed || toSelect) {
      drawOnScreen();
   }
   else { // use offscreen image
      painter.drawPixmap(0, 0, *offscreen);
   }
}

/**
  This method is called, if the drawing data has changed.
  If no mouse button is pressed, the offscreen image will be drawn.
  If mouse is pressed, drawing is done directly on screen using the
  paintEvent method.
  */
void SystemTopologyWidget::draw() {
   if (offscreen == 0) { // initial call
      drawOffScreen(); // do not call repaint(), initial draw may have be called from paintEvent()
   } else {
      if (!rightMousePressed && !leftMousePressed) {
         drawOffScreen();
      }
      repaint();
   }
}

void SystemTopologyWidget::initScale() {
   //if no topology loaded, return
   if (dim[0]==0 || dim[1]==0 || dim[2]==0) return;

   //scroll the plane such that it is in the non-negative coordinate area
   double x, y, z;
   plane.getTopLeftPoint(x,y,z);
   if (x<0.0) plane.xScroll(-x);
   if (y<0.0) plane.yScroll(-y);

   //make sure that the cube fits into the widget
   rescaleWidget();
}

void SystemTopologyWidget::drawOnScreen() {
   //if no topology loaded, return
   if (dim[0]==0 || dim[1]==0 || dim[2]==0) return;

   initScale();
   QPainter painter(this);
   painter.setRenderHint(QPainter::Antialiasing,
                         systemTreeWidget->getTabWidget()->getAntialiasing());
   
   paintTopology(painter, true);
   //qDebug() << "paint on screen " << size() << QTime::currentTime();
}

/**
  draws current topology to offscreen pixmap
  */
void SystemTopologyWidget::drawOffScreen()
{
   //if no topology loaded, return
   if (dim[0]==0 || dim[1]==0 || dim[2]==0) return;
   if (!isVisible()) return; // avoid unnecessary painting

   initScale();

   // create offscreen image
   if (offscreen == 0) {
      offscreen = new QPixmap(size());
   }
   else if ((offscreen->width() < this->width()) ||
            offscreen->height() < this->height()) {
      delete offscreen;
      int pad = 100;
      offscreen = new QPixmap(width() + pad, height() + pad);
   }

   offscreen->fill(this, 0, 0);
   //painter.fillRect(rect(), this->palette().brush(QPalette::Window));

   QPainter painter(offscreen);
   painter.setRenderHint(QPainter::Antialiasing,
                         systemTreeWidget->getTabWidget()->getAntialiasing());

   paintTopology(painter);
}

void SystemTopologyWidget::paintTopology(QPainter &painter, bool optimizeScreen)
{
   initScale();

   int dimIndex[3];
   dimIndex[0] = -1;
   dimIndex[1] = -1;
   dimIndex[2] = -1;

   //if the right mouse is pressed, then we must find the topology item that is selected;
   //the position of the selected item will be stored in dimIndex;
   //similarly, if the left mouse was pressed and released at the same position, we
   //will select the corresponding tree item, and thus need its id

   if (rightMousePressed || toSelect){
      Tetragon polygon;
      polygon.push_back(QPointF(plane.getPoint(0)->getX(),plane.getPoint(0)->getY()));
      polygon.push_back(QPointF(plane.getPoint(1)->getX(),plane.getPoint(1)->getY()));
      polygon.push_back(QPointF(plane.getPoint(2)->getX(),plane.getPoint(2)->getY()));
      polygon.push_back(QPointF(plane.getPoint(3)->getX(),plane.getPoint(3)->getY()));

      for (dimIndex[0]=0; dimIndex[0]<(int)dim[dimToOrigDim[0]]; dimIndex[0]++){
         if (polygon.containsPoint(lastPoint)) break;
         polygon.translate(0,planeDistance);
      }

   } else {
      //otherwise we mark the tree's selected item
      if (systemTreeWidget->selectedItems().size()>0){

         TreeWidgetItem* item = systemTreeWidget->selectedItems().at(0);
         std::vector< std::vector<long> >& coords = systemToCoord[item->id];
         if (coords.size()>0){
	    for ( std::vector< std::vector< long > >::iterator iter = coords.begin(); iter != coords.end(); iter++ ){
		std::vector<long > _coords = *iter;
//                  assert( _coords.size() == 3 );
		if ( _coords.size() >= 3 ){  // instead of assert
		    for ( int i = 0; i < 3; i++ ){
			dimIndex[ i ] = _coords[ dimToOrigDim[ i ] ];
		    }
		}
	    }
         }
      }
   }

   //if the planes are rising, then we paint the lowest plane first,
   //otherwise we start with the plane on the top

   int dummy1 = -1;
   int dummy2 = -1;

   if (plane.isRising()) {

      //since the plane is rising, we paint the lowest plane first
      plane.yScroll((double)(dim[dimToOrigDim[0]]-1)*planeDistance);

      //compute which items on planes are visible if the plane is partly
      //covered by another plane
      plane.computeVisibles(dim,dimToOrigDim,-planeDistance);

      //paint all planes
      for (int i=dim[dimToOrigDim[0]]-1; i>=0; i--){

         bool toPaint = true;
         if (optimizeScreen) {
            //this is the region of this widget which is not covered by other
            //widgets; only planes in this widget must be painted
            QRect rect = visibleRegion().boundingRect();
            //don't paint if whole plane is below the visible rectangle
            if ((int)plane.getPoint(0)->getY() > rect.y()+rect.height() &&
                (int)plane.getPoint(1)->getY() > rect.y()+rect.height() &&
                (int)plane.getPoint(2)->getY() > rect.y()+rect.height() &&
                (int)plane.getPoint(3)->getY() > rect.y()+rect.height()) toPaint = false;
            //also don't paint if plane is above the visible rectangle
            else if ((int)plane.getPoint(0)->getY() < rect.y() &&
                     (int)plane.getPoint(1)->getY() < rect.y() &&
                     (int)plane.getPoint(2)->getY() < rect.y() &&
                     (int)plane.getPoint(3)->getY() < rect.y()) toPaint = false;

         }

         if (toPaint)
            plane.paint(dim,
                        dimToOrigDim,
                        i,
                        painter,
                        colors,
			selected_rects,
                        i>0,
                        (i==dimIndex[0] && (rightMousePressed || toSelect) ? &lastPoint : NULL),
                        (i==dimIndex[0] ? dimIndex[1] : dummy1),
                        (i==dimIndex[0] ? dimIndex[2] : dummy2));
         //scroll the plane to represent the next plane above the
         //currently painted one
         if (i>0) plane.yScroll(-planeDistance);
      }
   } else {

      //compute which items on planes are visible if the plane is partly
      //covered by another plane
      plane.computeVisibles(dim,dimToOrigDim,planeDistance);

      //paint all planes; since the plane is not rising, we paint the
      //plane on the top first
      for (unsigned i=0; i<dim[dimToOrigDim[0]]; i++){

         bool toPaint = true;
         if (optimizeScreen) {
            //this is the region of this widget which is not covered by other
            //widgets; only planes in this widget must be painted
            QRect rect = visibleRegion().boundingRect();
            //don't paint if whole plane is below the visible rectangle
            if ((int)plane.getPoint(0)->getY() > rect.y()+rect.height() &&
                (int)plane.getPoint(1)->getY() > rect.y()+rect.height() &&
                (int)plane.getPoint(2)->getY() > rect.y()+rect.height() &&
                (int)plane.getPoint(3)->getY() > rect.y()+rect.height()) toPaint = false;
            //also don't paint if plane is above the visible rectangle
            else if ((int)plane.getPoint(0)->getY() < rect.y() &&
                     (int)plane.getPoint(1)->getY() < rect.y() &&
                     (int)plane.getPoint(2)->getY() < rect.y() &&
                     (int)plane.getPoint(3)->getY() < rect.y()) toPaint = false;
         }
         if (toPaint)
            plane.paint(dim,
                        dimToOrigDim,
                        i,
                        painter,
                        colors,
			selected_rects,
                        i<dim[dimToOrigDim[0]]-1,
                        ((int)i==dimIndex[0] && (rightMousePressed || toSelect) ? &lastPoint : NULL),
                        ((int)i==dimIndex[0] ? dimIndex[1] : dummy1),
                        ((int)i==dimIndex[0] ? dimIndex[2] : dummy2));
         if (i<dim[dimToOrigDim[0]]-1) plane.yScroll(planeDistance);
      }
      //scroll the plane to represent the next plane below the
      //currently painted one
      plane.yScroll(-(double)(dim[dimToOrigDim[0]]-1)*planeDistance);
   }

   QPen pen(Qt::black);
   pen.setWidth(0);
   painter.setPen(pen);

   //if the left mouse button was pressed and released without moving
   //then we select the corresponding item

   if (toSelect && dimIndex[0]!=-1 && dimIndex[1]!=-1 && dimIndex[2]!=-1){
      int systemId = coordToSystemId[dimIndex[origDimToDim[0]]][dimIndex[origDimToDim[1]]][dimIndex[origDimToDim[2]]];
      if (systemId>=0) newSelection = systemTreeWidget->items[systemId];
      else newSelection = NULL;
   } else newSelection = NULL;

   //paint the info box if the right mouse button is pressed
   //above a topology item

   if (rightMousePressed){

      if (dimIndex[0]>=0 && dimIndex[0]<(int)dim[dimToOrigDim[0]] && dimIndex[1]>=0 && dimIndex[2]>=0){

         QString coordStr, nameStr, valueStr, absValueStr, rankStr, idStr, nodeStr, numberOfElemStr;

         //though we always store internally 3 dimensions, in the output
         //only the real dimensions should be displayed
         int systemId = coordToSystemId[dimIndex[origDimToDim[0]]][dimIndex[origDimToDim[1]]][dimIndex[origDimToDim[2]]];

         //generate the strings of the info box

         if (systemId<0){
            coordStr.append("-");
            nameStr = "-";
            valueStr = "-";
            absValueStr = "-";
            rankStr = "-";
            idStr = "-";
            nodeStr = "-";
	    numberOfElemStr = "-";

         } else {
		std::vector< std::vector<long> >& all_coords = systemToCoord[ systemId ];
		numberOfElemStr.append( QString::number( all_coords.size() ) );
                std::vector<long>                 coord;
                for ( std::vector< std::vector<long> >::iterator iter = all_coords.begin(); iter != all_coords.end(); iter++ ){
                    std::vector<long> _coord = *iter;
                    if (    (long)dimIndex[origDimToDim[ 0 ]] == _coord[ 0 ]
                         && (long)dimIndex[origDimToDim[ 1 ]] == _coord[ 1 ]
                         && (long)dimIndex[origDimToDim[ 2 ]] == _coord[ 2 ]
                         ){
                        coord = _coord;
                        break;
                    }
                }

            // more than 3 dimensions
            if (cube->get_cart(topologyId)->get_dimv().size() > 3) {
               coordStr = systemToCoordStr[systemId];
            } else {
               std::vector<std::string> dim_names= cube->get_cart(topologyId)->get_namedims();
               coordStr.append("(");
               for (unsigned i = 3 - cube->get_cart(topologyId)->get_dimv().size(); i < 3; i++){
            	   if(dim_names.size()>0)
            	   {
            		   coordStr.append(QString::fromStdString(dim_names[i-1]));
            	   	   coordStr.append(": ");
            	   }
            	   coordStr.append(QString::number(coord[i]));
                  if (i<2) coordStr.append(",");
               }
               coordStr.append(")");
            }

            TreeWidgetItem* item = systemTreeWidget->items[systemId];
            cube::Sysres* sysres = ((cube::Sysres*)(item->cubeObject));

            nameStr.append(QString::fromStdString(sysres->get_name()));

            double value, absValue;
            bool ok;
            //get the current total value
            systemTreeWidget->getValue(item,false,value,ok);
            //get the absolute total value
            systemTreeWidget->getValue(item,false,absValue,ok,ABSOLUTE);

            //compute the value string
            if (!ok) {
               valueStr = "-";
            } else {
               valueStr = systemTreeWidget->getTabWidget()->getPrecisionWidget()->numberToQString(value,systemTreeWidget->hasIntegerType(),FORMAT1);
               //compute percentage of the value on the color scale
               double percent = (userDefinedMinMaxValues ? userMaxValue - userMinValue : maxValue - minValue);
               percent = (percent==0.0 ? 100.0 : 100.0 * (value - (userDefinedMinMaxValues ? userMinValue : minValue))/ percent);
               valueStr.append(" (");
               valueStr.append(systemTreeWidget->getTabWidget()->getPrecisionWidget()->numberToQString(percent,false,FORMAT1));
               valueStr.append("%)");
            }

            //compute the absolute value string
            if (!ok) {
               absValueStr = "-";
            } else if (systemTreeWidget->getTabWidget()->getValueModus() != ABSOLUTE){
               absValueStr = systemTreeWidget->getTabWidget()->getPrecisionWidget()->numberToQString(absValue,systemTreeWidget->hasAbsoluteIntegerType(),FORMAT1);
               //compute percentage of the absolute value on the color scale
               double absPercent = (maxAbsValue - minAbsValue);
               absPercent = (absPercent==0.0 ? 100.0 : 100.0 * (absValue - minAbsValue) / absPercent);
               absValueStr.append(" (");
               absValueStr.append(systemTreeWidget->getTabWidget()->getPrecisionWidget()->numberToQString(absPercent,false,FORMAT1));
               absValueStr.append("%)");
            }

            //get rank, node, and id
            if (item->type==THREADITEM){
               assert(((cube::Thread*)sysres)->get_parent()!=NULL);
               assert(((cube::Thread*)sysres)->get_parent()->get_parent()!=NULL);
               rankStr.append(QString::number(((cube::Thread*)sysres)->get_parent()->get_rank()));
               idStr.append(QString::number(((cube::Thread*)sysres)->get_rank()));
               nodeStr.append(QString::fromStdString(((cube::Thread*)sysres)->get_parent()->get_parent()->get_name()));
            } else {
               assert(item->type==PROCESSITEM);
               rankStr.append(QString::number(((cube::Process*)sysres)->get_rank()));
               if (((cube::Process*)sysres)->num_children()==0)
                  idStr.append("-");
               else {
                  assert(((cube::Process*)sysres)->num_children()==1);
                  idStr.append(QString::number(((cube::Process*)sysres)->get_child(0)->get_rank()));
               }
               nodeStr.append(QString::fromStdString(((cube::Process*)sysres)->get_parent()->get_name()));
            }
         }

         QString text1;

	 text1.append("Coord:");
         text1.append("\n");

         text1.append("Node:");
         text1.append("\n");

         text1.append("Name:");
         text1.append("\n");

         text1.append("MPI rank:");
         text1.append("\n");

         text1.append("Thread id:");
         text1.append("\n");

         text1.append("Value:");

         if (systemTreeWidget->getTabWidget()->getValueModus() != ABSOLUTE){
            text1.append("\n");
            text1.append("Absolute:");
         }
	 text1.append( "\n" );
	 text1.append( "Number of elements:" );
	 text1.append( "\n" );
	 
         QString text2;

	 text2.append(coordStr);
         text2.append("\n");

         text2.append(nodeStr);
         text2.append("\n");

         text2.append(nameStr);
         text2.append("\n");

         text2.append(rankStr);
         text2.append("\n");

         text2.append(idStr);
         text2.append("\n");

         text2.append(valueStr);

         if (systemTreeWidget->getTabWidget()->getValueModus() != ABSOLUTE){
            text2.append("\n");
            text2.append(absValueStr);
         }
	 text2.append( "\n" );
	 text2.append( numberOfElemStr );
	 text2.append( "\n" );

         painter.setBrush(Qt::white);
         painter.setPen(Qt::black);

         //get height and width of the text blocks
         QRect rect1 = painter.boundingRect(0,0,0,0,Qt::AlignLeft,text1);
         QRect rect2 = painter.boundingRect(0,0,0,0,Qt::AlignLeft,text2);

         QRect rect(lastPoint.x()+20,
                    lastPoint.y()+20,
                    rect1.width()+rect2.width()+11,
                    (rect1.height() > rect2.height() ? rect1.height() + 6 : rect2.height() + 6));

         int wMin = scrollArea->horizontalScrollBar()->value();
         int wMax = wMin + (scrollArea->width()) - 31;
         wMin += 30;
         int hMin = scrollArea->verticalScrollBar()->value();
         int hMax = hMin + (scrollArea->height()) - 31;
         hMin += 30;

         if (rect.bottomRight().x()>=wMax) rect.translate(-(rect.bottomRight().x()-wMax),0);
         if (rect.bottomRight().y()>=hMax) rect.translate(0,-(rect.bottomRight().y()-hMax));
         if (rect.topLeft().x()<wMin) rect.translate(wMin - rect.topLeft().x(), 0);
         if (rect.topLeft().y()<hMin) rect.translate(0, hMin - rect.topLeft().y());
         painter.drawRect(rect);

         rect1.moveTo(rect.x()+3,rect.y()+3);
         painter.drawText(rect1,Qt::AlignLeft,text1);
         rect2.moveTo(rect1.x()+rect1.width()+5, rect1.y());
         painter.drawText(rect2,Qt::AlignLeft,text2);
      }
   }

   if (invalidDimensions) {
      painter.setOpacity(0.7);
      painter.fillRect(this->rect(), Qt::gray);
   }
}



/**************** mouse and key event handling ******************/

//handle mouse press events
//
void SystemTopologyWidget::mousePressEvent(QMouseEvent *event)
{
   Qt::KeyboardModifiers mods = event->modifiers ();
   //check if shift is pressed
   if (mods.testFlag(Qt::ShiftModifier)) shiftPressed = true;
   else shiftPressed = false;
   //check if control is pressed
   if (mods.testFlag(Qt::ControlModifier)) controlPressed = true;
   else controlPressed = false;
   //get the position of the click
   lastPoint = event->pos();
   //for left-mouse-pull remember the first point to compute the
   //difference later for mouse movement
   if (!leftMousePressed)
      firstPoint = lastPoint;
   //left button is handled at mouse release or mouse movement
   if (event->button() == Qt::LeftButton) {
      leftMousePressed = true;
      event->accept();
   } else if (event->button() == Qt::RightButton) {
      //right button is for context info (painted by  draw())
      rightMousePressed = true;
      draw();
      event->accept();
   } else event->ignore();
}

//handle mouse release events
//
void SystemTopologyWidget::mouseReleaseEvent(QMouseEvent *event)
{
   if (event->button() == Qt::LeftButton){
      leftMousePressed = false;
      //if the left mouse button is pressed and released at the same point
      //then we select the corresponding tree item
      if (firstPoint==event->pos()) {
         toSelect = true;
         draw();
         toSelect = false;
         if (newSelection!=NULL){
            std::string selection = newSelection->getText().toStdString();
            selection.erase(0,selection.find(" ")+1); // remove prepended value
            emit systemTreeWidget->setMessage(QString("Selected \"").append(QString::fromStdString(selection)).append("\""));

            if (newSelection->isHidden()) newSelection->setHidden(false);
            TreeWidgetItem* item = newSelection->parent();
            while (item!=NULL){
               if (item->isHidden()) item->setHidden(false);
               if (!item->isExpanded()) item->setExpanded(true);
               item = item->parent();
            }
            if (systemTreeWidget->selectedItems().size()>0)
               systemTreeWidget->deselect(systemTreeWidget->selectedItems().at(0));
            systemTreeWidget->select(newSelection);
            systemTreeWidget->updateSizes();
            updateValueWidget();
         }
      }
      event->accept();
   } else if (event->button() == Qt::RightButton){
      //remove the info box for the previously right-clicked item
      rightMousePressed = false;
      event->accept();
   } else event->ignore();
   
   updateSelection();
   draw();
}

//handle mouse move events
//
void SystemTopologyWidget::mouseMoveEvent(QMouseEvent *event)
{
   if (event->buttons() & Qt::LeftButton) {
      //if mouse is left-clicked and moved while shift is pressed, then
      //move th topology
      if (shiftPressed) moveTo(event->pos());
      //if mouse is left-clicked and moved while control is pressed,
      //then increase the plane distance
      else if (controlPressed) increasePlaneDistanceTo(event->pos());
      //if mouse is left-clicked and moved, rotate the topology
      else rotateTo(event->pos());
      event->accept();
   } else if (event->buttons() & Qt::RightButton) {
      //if mouse is right-clicked and moved, then change the item for
      //which the info box is displayed
      lastPoint = event->pos();
      draw();
      event->accept();
   } else event->ignore();
}

//handle mouse wheel events
//
void SystemTopologyWidget::wheelEvent(QWheelEvent *e){
   //the mouse wheel can be used for zooming
   if (e->delta()>0) zoomIn();
   else zoomOut();
   e->accept();
}

void SystemTopologyWidget::keyPressEvent(QKeyEvent *event){
   switch (event->key()) {
   case Qt::Key_Shift:
      if (!controlPressed) shiftPressed = true;
      event->accept();
      break;
   case Qt::Key_Control:
      if (!shiftPressed) controlPressed = true;
      event->accept();
      break;
   default:{event->ignore();}
   }
}

void SystemTopologyWidget::keyReleaseEvent(QKeyEvent *event){
   switch (event->key()) {
   case Qt::Key_Shift:
      shiftPressed = false;
      event->accept();
      break;
   case Qt::Key_Control:
      controlPressed = false;
      event->accept();
      break;
   default:{event->ignore();}
   }
}


/****************** basic plane transformations ************************/


void SystemTopologyWidget::moveTo(const QPoint &endPoint){
   int dx = endPoint.x() - lastPoint.x();
   int dy = endPoint.y() - lastPoint.y();
   plane.xScroll(dx);
   plane.yScroll(dy);
   lastPoint = endPoint;

   draw();
}

void SystemTopologyWidget::increasePlaneDistanceTo(const QPoint &endPoint){
   int dy = endPoint.y() - lastPoint.y();
   planeDistance += (double)dy;
   if (planeDistance<=0.0) planeDistance = 1.0;
   lastPoint = endPoint;

   draw();
}

void SystemTopologyWidget::rotateTo(const QPoint &endPoint){
   int dx = endPoint.x() - lastPoint.x();
   int dy = endPoint.y() - lastPoint.y();
   setYAngleFromIntern(yAngle - dx%10);
   if (yAngle<=90 || yAngle>=270) setXAngleFromIntern(xAngle + dy%10);
   else setXAngleFromIntern(xAngle - dy%10);
   lastPoint = endPoint;

   draw();
}

void SystemTopologyWidget::moveDown(){
   plane.yScroll(5.0);

   draw();
}

void SystemTopologyWidget::moveUp(){
   double minX, maxX, minY, maxY;
   plane.getCornerPoints(minX, maxX, minY, maxY);
   if (minY>5)
      plane.yScroll(-5.0);

   draw();
}

void SystemTopologyWidget::moveRight(){
   plane.xScroll(5.0);
   draw();
}

void SystemTopologyWidget::moveLeft(){
   double minX, maxX, minY, maxY;
   plane.getCornerPoints(minX, maxX, minY, maxY);
   if (minX>5)
      plane.xScroll(-5.0);

   draw();
}

void SystemTopologyWidget::increasePlaneDistance(){
   planeDistance += 1.0;

   draw();
   diffs.distanceChanged = true;
}

void SystemTopologyWidget::decreasePlaneDistance(){
   if (planeDistance >= 2.0){
      planeDistance -= 1.0;

      draw();
      diffs.distanceChanged = true;
   }
}

void SystemTopologyWidget::zoomIn(){

   int dx01 = (int)((plane.getPoint(1)->getX() - plane.getPoint(0)->getX())/(double)dim[dimToOrigDim[1]]);
   int dy01 = (int)((plane.getPoint(1)->getY() - plane.getPoint(0)->getY())/(double)dim[dimToOrigDim[1]]);
   int dx03 = (int)((plane.getPoint(3)->getX() - plane.getPoint(0)->getX())/(double)dim[dimToOrigDim[2]]);
   int dy03 = (int)((plane.getPoint(3)->getY() - plane.getPoint(0)->getY())/(double)dim[dimToOrigDim[2]]);

   double factor = 0.0;

   if (dx01>=dy01 && dx01>=dx03 && dx01>=dy03 && dx01>=1)
      factor = (plane.getPoint(1)->getX() - plane.getPoint(0)->getX() + (double)dim[dimToOrigDim[1]])/(plane.getPoint(1)->getX() - plane.getPoint(0)->getX());
   else if (dy01>=dx01 && dy01>=dx03 && dy01>=dy03 && dy01>=1)
      factor = (plane.getPoint(1)->getY() - plane.getPoint(0)->getY() + (double)dim[dimToOrigDim[1]])/(plane.getPoint(1)->getY() - plane.getPoint(0)->getY());
   else if (dx03>=dx01 && dx03>=dy01 && dx03>=dy03 && dx03>=1)
      factor = (plane.getPoint(3)->getX() - plane.getPoint(0)->getX() + (double)dim[dimToOrigDim[2]])/(plane.getPoint(3)->getX() - plane.getPoint(0)->getX());
   else if (dy03>=dx01 && dy03>=dy01 && dy03>=dx03 && dy03>=1)
      factor = (plane.getPoint(3)->getY() - plane.getPoint(0)->getY() + (double)dim[dimToOrigDim[2]])/(plane.getPoint(3)->getY() - plane.getPoint(0)->getY());
   else factor = 1.01;

   diffs.scale *= factor;

   double x,y,z;
   plane.getTopLeftPoint(x,y,z);
   plane.scale(x,y,z,factor);
   planeDistance *= factor;

   draw();
}

void SystemTopologyWidget::zoomOut(){

   int dx01 = (int)((plane.getPoint(1)->getX() - plane.getPoint(0)->getX())/(double)dim[dimToOrigDim[1]]);
   int dy01 = (int)((plane.getPoint(1)->getY() - plane.getPoint(0)->getY())/(double)dim[dimToOrigDim[1]]);
   int dx03 = (int)((plane.getPoint(3)->getX() - plane.getPoint(0)->getX())/(double)dim[dimToOrigDim[2]]);
   int dy03 = (int)((plane.getPoint(3)->getY() - plane.getPoint(0)->getY())/(double)dim[dimToOrigDim[2]]);

   double factor = 0.0;
   if (dx01>1 && dx01>=dy01 && dx01>=dx03 && dx01>=dy03 && dx01>=3)
      factor = (plane.getPoint(1)->getX() - plane.getPoint(0)->getX() - (double)dim[dimToOrigDim[1]])/(plane.getPoint(1)->getX() - plane.getPoint(0)->getX());
   else if (dy01>1 && dy01>=dx01 && dy01>=dx03 && dy01>=dy03 && dy01>=3)
      factor = (plane.getPoint(1)->getY() - plane.getPoint(0)->getY() - (double)dim[dimToOrigDim[1]])/(plane.getPoint(1)->getY() - plane.getPoint(0)->getY());
   else if (dx03>1 && dx03>=dx01 && dx03>=dy01 && dx03>=dy03 && dx03>=3)
      factor = (plane.getPoint(3)->getX() - plane.getPoint(0)->getX() - (double)dim[dimToOrigDim[2]])/(plane.getPoint(3)->getX() - plane.getPoint(0)->getX());
   else if (dy03>1 && dy03>=dx01 && dy03>=dy01 && dy03>=dx03 && dy03>=3)
      factor = (plane.getPoint(3)->getY() - plane.getPoint(0)->getY() - (double)dim[dimToOrigDim[2]])/(plane.getPoint(3)->getY() - plane.getPoint(0)->getY());
   else factor = 0.99;

   diffs.scale *= factor;

   double x,y,z;
   plane.getTopLeftPoint(x,y,z);
   if (plane.scale(x,y,z,factor)){
      planeDistance *= factor;

      draw();
   }
}

/************************ settings ***************************/

//save topology settings
//
void SystemTopologyWidget::saveSettings(QSettings& settings){

   QString groupName("systemTopology");
   groupName.append(QString::number(topologyId));
   settings.beginGroup(groupName);

   settings.setValue("planeDistance",planeDistance);
   settings.setValue("xAngle",xAngle);
   settings.setValue("yAngle",yAngle);

   //save the plane state
   for (int i=0; i<5; i++){
      for (int j=0; j<3; j++){
         QString storeUnder = "plane";
         storeUnder.append(QString::number(i));
         double value = 0.0;
         if (j==0) {
            storeUnder.append("x");
            value = plane.getPoint(i)->getX();
         } else if (j==1) {
            storeUnder.append("y");
            value = plane.getPoint(i)->getY();
         } else {
            storeUnder.append("z");
            value = plane.getPoint(i)->getZ();
         }
         settings.setValue(storeUnder,value);
      }
   }

   settings.setValue("userDefinedMinMaxValues",userDefinedMinMaxValues);
   settings.setValue("userMinValue",userMinValue);
   settings.setValue("userMaxValue",userMaxValue);

   //save the scrollbar states
   assert(scrollArea!=NULL);
   settings.setValue("hScrollValue",scrollArea->horizontalScrollBar()->value());
   settings.setValue("vScrollValue",scrollArea->verticalScrollBar()->value());

   settings.endGroup();
}

bool SystemTopologyWidget::loadSettings(QSettings& settings){

   QString groupName = "systemTopology";
   groupName.append(QString::number(topologyId));
   settings.beginGroup(groupName);

   planeDistance    = settings.value("planeDistance",5.0).toDouble();
   setXAngleFromIntern(settings.value("xAngle",300).toInt());
   setYAngleFromIntern(settings.value("yAngle",30).toInt());

   //load the plane state
   for (int i=0; i<5; i++){
      for (int j=0; j<3; j++){
         QString storeUnder = "plane";
         storeUnder.append(QString::number(i));
         double value;
         if (j==0) {
            storeUnder.append("x");
            value = settings.value(storeUnder,0.0).toDouble();
            plane.getPoint(i)->setX(value);
         } else if (j==1) {
            storeUnder.append("y");
            value = settings.value(storeUnder,0.0).toDouble();
            plane.getPoint(i)->setY(value);
         } else {
            storeUnder.append("z");
            value = settings.value(storeUnder,0.0).toDouble();
            plane.getPoint(i)->setZ(value);
         }
      }
   }

   userDefinedMinMaxValues = settings.value("userDefinedMinMaxValues",false).toBool();
   userMinValue = settings.value("userMinValue",0.0).toDouble();
   userMaxValue = settings.value("userMaxValue",0.0).toDouble();

   //load the scroll bars' states
   assert(scrollArea!=NULL);
   scrollArea->horizontalScrollBar()->setValue(settings.value("hScrollValue",0).toInt());
   scrollArea->verticalScrollBar()->setValue(settings.value("vScrollValue",0).toInt());

   settings.endGroup();

   draw();

   return true;
}


/**************************** miscellaneous **********************/


//get user-defined minimal and maximal values for the minimal and
//maximal colors
void SystemTopologyWidget::minMaxValues(){
   bool ok = true;
   double minValue, maxValue;

   if (QMessageBox::question(this,
                             "Min/max values",
                             "Use user-defined minimal and maximal values for coloring?",
                             QMessageBox::Yes|QMessageBox::No,
                             QMessageBox::Yes)
      != QMessageBox::Yes){

      if (userDefinedMinMaxValues){
         userDefinedMinMaxValues = false;
         displayItems();
      }

   } else {

      minValue = QInputDialog::getDouble(this,
                                         "Minimal value",
                                         "Minimal value for coloring:",
                                         userDefinedMinMaxValues?userMinValue:this->minValue,
                                         0.0,
                                         1e+15,
                                         15,
                                         &ok);

      if (ok)
         maxValue = QInputDialog::getDouble(this,
                                            "Maximal value",
                                            "Maximal value for coloring:",
                                            userDefinedMinMaxValues?userMaxValue:this->maxValue,
                                            0.0,
                                            1e+15,
                                            15,
                                            &ok);

      //change values only if the user completed input (i.e., did not
      //click cancel)
      if (ok){
         userDefinedMinMaxValues = true;
         userMinValue = minValue;
         userMaxValue = maxValue;
         displayItems();
      }
   }
}


//set the line type for topologies: black, gray, white, or no lines
//
void SystemTopologyWidget::setLineType(LineType lineType){
   plane.setLineType(lineType);

   draw();
}


//toggle the state if unused planes should be displayed or not
//
void SystemTopologyWidget::toggleUnused(){

   if (dim[0]==0 || dim[1]==0 || dim[2]==0) return;


   //first case: if unused topology planes are not shown
   if (!systemTreeWidget->getTabWidget()->getShowUnusedTopologyPlanes()){

      //check the first dimension for unused planes
      for (unsigned i=0; i<dim[0]; i++){
         bool unused = true;
         for (unsigned j=0; j<dim[1]; j++){
            for (unsigned k=0; k<dim[2]; k++){
               if (coordToSystemId[i][j][k] != -1){
                  unused = false;
                  break;
               }
            }
            if (!unused) break;
         }
         if (unused){
            for (unsigned i2=i+1; i2<dim[0]; i2++){
               coordToSystemId[(int)i2-1] = coordToSystemId[i2];
            }
            setDims(dim[0]-1,dim[1],dim[2]);
            coordToSystemId.resize(dim[0]);
            i--;
         }
      }

      //check the second dimension for unused planes
      for (unsigned j=0; j<dim[1]; j++){
         bool unused = true;
         for (unsigned i=0; i<dim[0]; i++){
            for (unsigned k=0; k<dim[2]; k++){
               if (coordToSystemId[i][j][k] != -1){
                  unused = false;
                  break;
               }
            }
            if (!unused) break;
         }

         if (unused){
            for (unsigned i=0; i<dim[0]; i++){
               for (unsigned j2=j+1; j2<dim[1]; j2++){
                  coordToSystemId[i][(int)j2-1] = coordToSystemId[i][j2];
               }
               coordToSystemId[i].resize(dim[1]-1);
            }
            setDims(dim[0],dim[1]-1,dim[2]);
            j--;
         }

      }

      //check the third dimension for unused planes
      for (unsigned k=0; k<dim[2]; k++){
         bool unused = true;
         for (unsigned i=0; i<dim[0]; i++){
            for (unsigned j=0; j<dim[1]; j++){
               if (coordToSystemId[i][j][k] != -1){
                  unused = false;
                  break;
               }
            }
            if (!unused) break;
         }
         if (unused){
            for (unsigned i=0; i<dim[0]; i++){
               for (unsigned j=0; j<dim[1]; j++){
                  for (unsigned k2=k+1; k2<dim[2]; k2++){
                     coordToSystemId[i][j][(int)k2-1] = coordToSystemId[i][j][k2];
                  }
                  coordToSystemId[i][j].resize(dim[2]-1);
               }
            }
            setDims(dim[0],dim[1],dim[2]-1);
            k--;
         }
      }
      for (unsigned i=0; i<dim[0]; i++){
         for (unsigned j=0; j<dim[1]; j++){
            for (unsigned k=0; k<dim[2]; k++){
               int id = coordToSystemId[i][j][k];
               if (id >= 0) {
                    systemToCoord[ id ].clear();
               }
            }
         }
      }
      for (unsigned i=0; i<dim[0]; i++){
         for (unsigned j=0; j<dim[1]; j++){
            for (unsigned k=0; k<dim[2]; k++){
               int id = coordToSystemId[i][j][k];
	       systemToCoord[ id ].push_back( std::vector< long > () );
	       size_t num_of_coords = systemToCoord[ id ].size();
               if (id >= 0) {
		  systemToCoord[id][ num_of_coords - 1 ].resize( 3 ); 
                  systemToCoord[id][ num_of_coords - 1 ][0] = i;
                  systemToCoord[id][ num_of_coords - 1 ][1] = j;
                  systemToCoord[id][ num_of_coords - 1 ][2] = k;
               }
            }
         }
      }

      //update the color dimensions

      for (unsigned i=0; i<colors.size(); i++)
         for (unsigned j=0; j<colors[i].size(); j++)
            for (unsigned k=0; k<colors[i][j].size(); k++)
               if (colors[i][j][k]!=NULL)
                  delete colors[i][j][k];
      colors.clear();
      selected_rects.clear();
      colors.resize(dim[0]);
      selected_rects.resize( dim[ 0 ] );
      for (unsigned i=0; i<dim[0]; i++) {
         colors[i].resize(dim[1]);
         selected_rects[ i ].resize( dim[ 1 ] );	 
         for (unsigned j=0; j<dim[1]; j++){
            colors[i][j].resize(dim[2]);
            selected_rects[ i ][ j ].resize( dim[ 2 ] );	    
            for (unsigned k=0; k<dim[2]; k++){
               colors[i][j][k] = NULL;
               selected_rects[ i ][ j ][ k ] = false;	       
            }
         }
      }
      updateColors();
      updateSelection();      
      reset(true);
   } else {
      //second case: unused planes are displayed, too
      //in this case just re-initialize
      reinit(false);
   }
}

/**
  used for topologies with more than 3 dimensions
  the method is called, if the user has changed the displayed dimensions
  */
void SystemTopologyWidget::selectedDimensionsChanged(const std::vector<long> &dims) {
   if (dims.size() > 0) {
      invalidDimensions = false;
      selectedDimensions = dims;
      selectMode = 0;
      reinit();
   } else {
      invalidDimensions = true;
      draw();
   }
}

void SystemTopologyWidget::foldingDimensionsChanged(std::vector<std::vector<int> > fold) {
   if (fold.size() > 0) {
      invalidDimensions = false;
      for (int i=0; i<3; i++)
         for (int j=0; j<2; j++)
            foldingDimensions[i][j] = -1;

      for (uint i=0; i<fold.size(); i++) {
         for (uint j=0; j<fold[i].size(); j++) {
            foldingDimensions[i][j] = fold[i][j];
         }
      }
      selectMode = 1;
      reinit(); // count/size of dimensions may have changed by folding
   } else {
      invalidDimensions = true;
      draw();
   }
}

/** create toolbar to select dimensions to display
  */
QWidget *SystemTopologyWidget::getDimensionSelectionBar() {
   if (tbar == 0) {
      const std::vector<long>& dims = cube->get_cart(topologyId)->get_dimv();
      if (dims.size() > 3) {
         const std::vector<std::string> names = cube->get_cart(topologyId)->get_namedims();
         tbar = new TopologyDimensionBar(dims, names);
         connect(tbar, SIGNAL(selectedDimensionsChanged(std::vector<long>)), this, SLOT(selectedDimensionsChanged(std::vector<long>)));
         connect(tbar, SIGNAL(foldingDimensionsChanged(std::vector<std::vector<int> >)), this, SLOT(foldingDimensionsChanged(std::vector<std::vector<int> >)));
      }
   }
   return tbar;
}

