/*******************************************************************************
 * gui/settings/RSPermissionMatrixWidget.cpp                                   *
 *                                                                             *
 * Copyright (c) 2014 Retroshare Team <retroshare.project@gmail.com>           *
 *                                                                             *
 * This program is free software: you can redistribute it and/or modify        *
 * it under the terms of the GNU Affero General Public License as              *
 * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.                         *
 *                                                                             *
 * You should have received a copy of the GNU Affero General Public License    *
 * along with this program. If not, see <https://www.gnu.org/licenses/>.       *
 *                                                                             *
 *******************************************************************************/

#ifndef WINDOWS_SYS
#include <sys/times.h>
#endif

#include <iostream>
#include <math.h>
#include <QtGlobal>
#include <QPainter>
#include <QDateTime>
#include <QTimer>
#include <QMouseEvent>

#include "RSPermissionMatrixWidget.h"
#include "gui/settings/ServicePermissionsPage.h"
#include <retroshare/rsstatus.h>
#include <retroshare/rspeers.h>
#include <retroshare/rsservicecontrol.h>
#include "rsitems/rsserviceids.h"
#include <QTextDocument>

#define NOT_IMPLEMENTED std::cerr << __PRETTY_FUNCTION__ << ": not yet implemented." << std::endl;

// The behavior of the widget is the following:
// - when default is changed, switch all icons to the default position. Then every icon switch back by the user will be
//   in the white list.
// - each peer/service slot has a switch. If the peer doesn't allow the service, the  switch is shown with some
//   indication. When both the current and adverse permissions are granted, the switch should show it as well.
// - we should use tooltips

const float RSPermissionMatrixWidget::fICON_SIZE_X    = 40/16.0 ;
const float RSPermissionMatrixWidget::fICON_SIZE_Y    = 40/16.0 ;
const float RSPermissionMatrixWidget::fROW_SIZE       = 42/16.0 ;
const float RSPermissionMatrixWidget::fCOL_SIZE       = 42/16.0 ;
const float RSPermissionMatrixWidget::fMATRIX_START_X =  5/16.0 ;
const float RSPermissionMatrixWidget::fMATRIX_START_Y =100/16.0 ;

/** Default contructor */
RSPermissionMatrixWidget::RSPermissionMatrixWidget(QWidget *parent)
  :QFrame(parent)
{
  _painter = new QPainter();
  _current_service_id = 0;

  setMouseTracking(true) ;

  _timer = new QTimer ;
  QObject::connect(_timer,SIGNAL(timeout()),this,SLOT(updateDisplay())) ;
  _timer->start(5000);

  _max_width = 400 ;
  _max_height = 0 ;

  mHideOffline = false;
}

void RSPermissionMatrixWidget::setHideOffline(bool hide)
{
    if (mHideOffline == hide) {
        return;
    }

    mHideOffline = hide;
    repaint();
}

void RSPermissionMatrixWidget::updateDisplay()
{
    if(isHidden())
        return ;

    setMinimumWidth(_max_width) ;
    setMinimumHeight(_max_height) ;

    update() ;
}

void RSPermissionMatrixWidget::mousePressEvent(QMouseEvent *e)
{
#ifdef DEBUG_PERMISSION_MATRIX
    std::cerr << "mouse pressed at x=" << e->x() << ", y=" << e->y() << std::endl;
#endif

    uint32_t service_id ;
    RsPeerId peer_id ;

    if(computeServiceAndPeer(e->x(),e->y(),service_id,peer_id))
    {
#ifdef DEBUG_PERMISSION_MATRIX
        std::cerr << "Peer id: " << peer_id << ", service: " << service_id << std::endl;
#endif

        // Make sure the service is not globally disabled

        RsServicePermissions serv_perms;
        rsServiceControl->getServicePermissions(service_id,serv_perms);

        if (!serv_perms.mDefaultAllowed) return;

        switchPermission(service_id,peer_id) ;
        update() ;
    }
    else if(computeServiceGlobalSwitch(e->x(),e->y(),service_id))
    {
        switchPermission(service_id) ;
        update();
    }
    else
        QFrame::mousePressEvent(e) ;
}

void RSPermissionMatrixWidget::switchPermission(uint32_t service,const RsPeerId& pid)
{
    RsServicePermissions serv_perms ;

    if(!rsServiceControl->getServicePermissions(service,serv_perms))
        return ;

    if(serv_perms.peerHasPermission(pid))
        serv_perms.resetPermission(pid) ;
    else
        serv_perms.setPermission(pid) ;

    rsServiceControl->updateServicePermissions(service,serv_perms);
}

void RSPermissionMatrixWidget::switchPermission(uint32_t service)
{
    RsServicePermissions serv_perms ;

    if(!rsServiceControl->getServicePermissions(service,serv_perms))
        return ;

    if(serv_perms.mDefaultAllowed)
    {
        serv_perms.mPeersAllowed.clear() ;
    serv_perms.mDefaultAllowed = false ;
    }
    else
    {
        serv_perms.mDefaultAllowed = true ;
        serv_perms.mPeersDenied.clear() ;
    }

    rsServiceControl->updateServicePermissions(service,serv_perms);
}

void RSPermissionMatrixWidget::mouseMoveEvent(QMouseEvent *e)
{
    uint32_t service_id ;
    RsPeerId peer_id ;

    if(computeServiceAndPeer(e->x(),e->y(),service_id,peer_id))
    {
        if(_current_service_id != service_id || _current_peer_id != peer_id)
        {
            _current_service_id = service_id ;
            _current_peer_id = peer_id ;

            // redraw!
            update() ;
        }
    }
    else if(computeServiceGlobalSwitch(e->x(),e->y(),service_id))
    {
        peer_id.clear() ;

    if(_current_service_id != service_id || _current_peer_id != peer_id)
    {
        _current_service_id = service_id ;
        _current_peer_id = peer_id ;

        // redraw!
        update() ;
    }
    }
    else
    {
        service_id = ~0 ;
        peer_id.clear() ;
    }
}

/** Default destructor */
RSPermissionMatrixWidget::~RSPermissionMatrixWidget()
{
    _timer->stop() ;

    delete _timer ;
    delete _painter;
}

bool sortRsPeerIdByNameLocation(const RsPeerId &a, const RsPeerId &b)
{
	RsPeerDetails detailsA, detailsB;
	rsPeers->getPeerDetails(a, detailsA);
	rsPeers->getPeerDetails(b, detailsB);
	QString stringA, stringB;

	// name
	stringA += QString::fromUtf8(detailsA.name.c_str());
	stringB += QString::fromUtf8(detailsB.name.c_str());

	// location
	stringA += QString::fromUtf8(detailsA.location.c_str());
	stringB += QString::fromUtf8(detailsB.location.c_str());

	return stringA.toLower() < stringB.toLower();
}

/** Overloads default QWidget::paintEvent. Draws the actual
 * bandwidth graph. */
void RSPermissionMatrixWidget::paintEvent(QPaintEvent *)
{
    //std::cerr << "In paint event!" << std::endl;

    int S = QFontMetricsF(font()).height();

  /* Set current graph dimensions */
  _rec = this->frameRect();

  /* Start the painter */
  _painter->begin(this);

  /* We want antialiased lines and text */
  _painter->setRenderHint(QPainter::Antialiasing);
  _painter->setRenderHint(QPainter::TextAntialiasing);

  /* Fill in the background */
  _painter->fillRect(_rec, QBrush(BACK_COLOR));
  _painter->drawRect(_rec);

  // draw one line per friend.
  std::list<RsPeerId> ssllist ;
  rsPeers->getFriendList(ssllist) ;

  // sort list
  {
      // sort out offline peers
      if(mHideOffline) {
          RsPeerDetails peerDetails;
          for(std::list<RsPeerId>::iterator it = ssllist.begin(); it != ssllist.end();) {
              rsPeers->getPeerDetails(*it, peerDetails);

              switch (peerDetails.connectState) {
              case RS_PEER_CONNECTSTATE_OFFLINE:
              case RS_PEER_CONNECTSTATE_TRYING_TCP:
              case RS_PEER_CONNECTSTATE_TRYING_UDP:
                  it = ssllist.erase(it);
                  break;
              default:
                  it++;
                  break;
              }
          }
      }

      // sort by name
      ssllist.sort(sortRsPeerIdByNameLocation);
  }

  RsPeerServiceInfo ownServices;
  rsServiceControl->getOwnServices(ownServices);

  // Display friend names at the beginning of each column

  const QFont& font(_painter->font()) ;
  QFontMetrics fm(font);
  int peer_name_size = 0 ;
  float line_height = 2 + fm.height() ;

  std::vector<QString> names ;

  for(std::list<RsPeerId>::const_iterator it(ssllist.begin());it!=ssllist.end();++it)
  {
      RsPeerDetails details ;
      rsPeers->getPeerDetails(*it,details) ;

      QString name = QString::fromUtf8(details.name.c_str()) + " (" + QString::fromUtf8(details.location.c_str()) + ")";
      // TODO does not work correctly with hieroglyphs
      if(name.length() > 20 + 3)
          name = name.left(20)+"..." ;

      peer_name_size = std::max(peer_name_size, fm.width(name)) ;
      names.push_back(name) ;
  }

  QPen pen ;
  pen.setWidth(2) ;
  pen.setBrush(FOREGROUND_COLOR) ;

  _painter->setPen(pen) ;
  int i=0;
  //int x=5/14.0*S ;
  int y=S*fMATRIX_START_Y ;

  for(std::list<RsPeerId>::const_iterator it(ssllist.begin());it!=ssllist.end();++it,++i)
  {
      float X = S*fMATRIX_START_X + peer_name_size - fm.width(names[i]) ;
      float Y = S*fMATRIX_START_Y + (i+0.5)*S*fROW_SIZE + line_height/2.0f-2 ;

      _painter->drawText(QPointF(X,Y),names[i]) ;

      if(*it == _current_peer_id)
          _painter->drawLine(QPointF(X,Y+3),QPointF(X+fm.width(names[i]),Y+3)) ;

      y += line_height ;
  }

  matrix_start_x = S*5/14.0 + S*fMATRIX_START_X + peer_name_size ;

  // now draw the service names

  i=0 ;
  std::vector<int> last_width(10,0) ;

  for(std::map<uint32_t, RsServiceInfo>::const_iterator it(ownServices.mServiceList.begin());it!=ownServices.mServiceList.end();++it,++i)
  {
      QString name = QString::fromUtf8(it->second.mServiceName.c_str()) ;
      int text_width = fm.width(name) ;

      int X = matrix_start_x + S*fCOL_SIZE/2 - 2 + i*S*fCOL_SIZE - text_width/2;

      int height_index = 0 ;
      while(last_width[height_index] > X-5 && height_index < ((int)last_width.size()-1) )
          ++height_index ;

      int Y = S*fMATRIX_START_Y - S*fICON_SIZE_Y - 2 - line_height * height_index;

      last_width[height_index] = X + text_width ;
       // draw a half-transparent rectangle

      QBrush brush ;
      brush.setColor(QColor::fromHsvF(0.0f,0.0f,1.0f,0.8f));
      brush.setStyle(Qt::SolidPattern) ;

      QPen pen ;
      pen.setWidth(2) ;

      if(_current_service_id == it->second.mServiceType)
          pen.setBrush(FOREGROUND_COLOR) ;
      else
          pen.setBrush(Qt::gray) ;

      _painter->setPen(pen) ;

      QRect info_pos( X-5,Y-line_height-2, text_width + 10, line_height + 5) ;

      //_painter->fillRect(info_pos,brush) ;
      //_painter->drawRect(info_pos) ;

      _painter->drawLine(QPointF(X,Y+3),QPointF(X+text_width,Y+3)) ;
      _painter->drawLine(QPointF(X+text_width/2, Y+3), QPointF(X+text_width/2,S*fMATRIX_START_Y+peer_ids.size()*S*fROW_SIZE - S*fROW_SIZE+5)) ;

      pen.setBrush(FOREGROUND_COLOR) ;
      _painter->setPen(pen) ;

      _painter->drawText(QPointF(X,Y),name);
  }

  // Now draw the global switches.

  peer_ids.clear() ;
  for(std::list<RsPeerId>::const_iterator it(ssllist.begin());it!=ssllist.end();++it)
      peer_ids.push_back(*it) ;
  service_ids.clear() ;
  for(std::map<uint32_t, RsServiceInfo>::const_iterator sit(ownServices.mServiceList.begin());sit!=ownServices.mServiceList.end();++sit)
      service_ids.push_back(sit->first) ;

  static const std::string global_switch[2] = { ":/icons/global_switch_off_128.png",
                                                ":/icons/global_switch_on_128.png" } ;

  for(uint32_t i=0;i<service_ids.size();++i)
  {
      RsServicePermissions serv_perm ;
      rsServiceControl->getServicePermissions(service_ids[i],serv_perm) ;

      QPixmap pix(global_switch[serv_perm.mDefaultAllowed].c_str()) ;
      QRect position = computeNodePosition(0,i,false) ;

      position.setY(position.y() - S*fICON_SIZE_Y + 8/14.0*S) ;
      position.setX(position.x() + 3/14.0*S) ;
      position.setHeight(30/14.0*S) ;
      position.setWidth(30/14.0*S) ;

      _painter->drawPixmap(position,pix.scaledToHeight(S*fICON_SIZE_Y*0.9,Qt::SmoothTransformation),QRect(0,0,S*fICON_SIZE_X,S*fICON_SIZE_Y)) ;
  }

  // We draw for each service.

  static const std::string pixmap_names[4] = { ":/icons/switch00_128.png",
                                               ":/icons/switch01_128.png",
                                               ":/icons/switch10_128.png",
                                               ":/icons/switch11_128.png" } ;

  int n_col = 0 ;
  int n_col_selected = -1 ;
  int n_row_selected = -1 ;

  for(std::map<uint32_t, RsServiceInfo>::const_iterator sit(ownServices.mServiceList.begin());sit!=ownServices.mServiceList.end();++sit,++n_col)
  {
      RsServicePermissions service_perms ;

      rsServiceControl->getServicePermissions(sit->first,service_perms) ;

      // draw the default switch.


      // draw one switch per friend.

      int n_row = 0 ;

      for(std::list<RsPeerId>::const_iterator it(ssllist.begin());it!=ssllist.end();++it,++n_row)
      {
          RsPeerServiceInfo local_service_perms ;
          RsPeerServiceInfo remote_service_perms ;

          rsServiceControl->getServicesAllowed (*it, local_service_perms) ;
          rsServiceControl->getServicesProvided(*it,remote_service_perms) ;

          bool  local_allowed =  local_service_perms.mServiceList.find(sit->first) !=  local_service_perms.mServiceList.end() ;
          bool remote_allowed = remote_service_perms.mServiceList.find(sit->first) != remote_service_perms.mServiceList.end() ;

          QPixmap pix(pixmap_names[(local_allowed << 1) + remote_allowed].c_str()) ;

          bool selected = (sit->first == _current_service_id && *it == _current_peer_id) ;
          QRect position = computeNodePosition(n_row,n_col,selected) ;

          if(selected)
          {
              n_row_selected = n_row ;
              n_col_selected = n_col ;
          }
          _painter->drawPixmap(position,pix.scaledToHeight(S*fICON_SIZE_X,Qt::SmoothTransformation),QRect(0,0,S*fICON_SIZE_X,S*fICON_SIZE_Y)) ;
      }
  }

  // now display some info about current node.

  if(n_row_selected >= 0 && n_col_selected >= 0 && n_row_selected < (int)peer_ids.size() && n_col_selected < (int)service_ids.size())
  {
      QRect position = computeNodePosition(n_row_selected,n_col_selected,false) ;

      // draw text info

      RsServicePermissions service_perms ;

      rsServiceControl->getServicePermissions(service_ids[n_col_selected],service_perms) ;

      QString service_name    = tr("Service name:")+" "+QString::fromUtf8(service_perms.mServiceName.c_str()) ;
      QString service_default = service_perms.mDefaultAllowed?tr("Allowed by default"):tr("Denied by default");
      QString peer_name = tr("Peer name:")+" " + names[n_row_selected] ;
      QString peer_id = tr("Peer Id:")+" "+QString::fromStdString(_current_peer_id.toStdString()) ;

      RsPeerServiceInfo pserv_info ;
      rsServiceControl->getServicesAllowed(_current_peer_id,pserv_info) ;

      bool locally_allowed = pserv_info.mServiceList.find(_current_service_id) != pserv_info.mServiceList.end();
      bool remotely_allowed = false ; // default, if the peer is offline

      if(rsServiceControl->getServicesProvided(_current_peer_id,pserv_info))
          remotely_allowed = pserv_info.mServiceList.find(_current_service_id) != pserv_info.mServiceList.end();

      QString local_status  = locally_allowed ?tr("Enabled for this peer") :tr("Disabled for this peer") ;
      QString remote_status = remotely_allowed?tr("Enabled by remote peer"):tr("Disabled by remote peer") ;

      if(!service_perms.mDefaultAllowed)
          local_status = tr("Globally switched Off") ;

      const QFont& font(_painter->font()) ;
      QFontMetrics fm(font);

      int text_size_x = 0 ;
      text_size_x = std::max(text_size_x,fm.width(service_name));
      text_size_x = std::max(text_size_x,fm.width(peer_name));
      text_size_x = std::max(text_size_x,fm.width(peer_id));
      text_size_x = std::max(text_size_x,fm.width(local_status));
      text_size_x = std::max(text_size_x,fm.width(remote_status));

       // draw a half-transparent rectangle

      QBrush brush ;
      brush.setColor(QColor::fromHsvF(0.0f,0.0f,1.0f,0.8f));
      brush.setStyle(Qt::SolidPattern) ;

      QPen pen ;
      pen.setWidth(2) ;
      pen.setBrush(FOREGROUND_COLOR) ;

      _painter->setPen(pen) ;

      int popup_x = position.x() + (50 * S / 14.0);
      int popup_y = position.y() - (10 * S / 14.0);
      int popup_width = text_size_x + (10 * S / 14.0);
      int popup_height = (line_height * 5) + (5 * S / 14.0);
      if (popup_x + popup_width > _max_width)
        popup_x = position.x() - popup_width;
      if (popup_y + popup_height > _max_height)
        popup_y -= popup_height;

      QRect info_pos(popup_x, popup_y, popup_width, popup_height) ;

      _painter->fillRect(info_pos,brush) ;
      _painter->drawRect(info_pos) ;

      // draw the text

      float x = info_pos.x()               + 5*S/14.0 ;
      float y = info_pos.y() + line_height + 1*S/14.0 ;

      _painter->drawText(QPointF(x,y), service_name)  ; y += line_height ;
      _painter->drawText(QPointF(x,y), peer_name)     ; y += line_height ;
      _painter->drawText(QPointF(x,y), peer_id)       ; y += line_height ;
      _painter->drawText(QPointF(x,y), remote_status) ; y += line_height ;
      _painter->drawText(QPointF(x,y), local_status)  ; y += line_height ;
  }

  _max_height = S*fMATRIX_START_Y + (peer_ids.size()+3) * S*fROW_SIZE ;
  _max_width  = matrix_start_x + (service_ids.size()+3) * S*fCOL_SIZE ;

	if(n_row_selected == -1)
	{
		for(uint32_t i=0;i<service_ids.size();++i)
		{
			if(_current_service_id == service_ids[i])
			{
				//uint16_t serviceOrig = (uint16_t)((service_ids[i] - (((uint32_t)RS_PKT_VERSION_SERVICE) << 24) ) >> 8);
				uint16_t serviceOrig=(service_ids[i]>>8) & 0xffff;
				QTextDocument td;
				td.setHtml(ServiceDescription(serviceOrig));
				QString service_name = "Service: 0x"+ QString::number(serviceOrig,16) + "  " + QString::fromStdString(rsServiceControl->getServiceName(service_ids[i]));
				QBrush brush ;
				brush.setColor(QColor::fromHsvF(0.0f,0.0f,1.0f,0.8f));
				brush.setStyle(Qt::SolidPattern) ;
				QPen pen ;
				pen.setWidth(1) ;
				pen.setBrush(FOREGROUND_COLOR) ;
				_painter->setPen(pen) ;
				QRect position = computeNodePosition(0,i,false) ;
				int popup_x = position.x() + (50 * S / 14.0);
				int popup_y = position.y() - (10 * S / 14.0) + line_height;
				int popup_width = std::max((int)td.size().width(), fm.width(service_name)) + S;
				int popup_height = td.size().height() + line_height*2;
				while (popup_x + popup_width > _max_width)
					popup_x -= S;
				if (popup_y + popup_height > _max_height)
					popup_y -= popup_height;
				QRect info_pos(popup_x, popup_y, popup_width, popup_height) ;
				_painter->fillRect(info_pos,brush) ;
				_painter->drawRect(info_pos) ;
				float x = info_pos.x()               + 5*S/14.0 ;
				float y = info_pos.y() + line_height + 1*S/14.0 ;
				_painter->drawText(QPointF(x,y), service_name)  ; y += line_height ;
				_painter->translate(QPointF(popup_x, popup_y+2*line_height));
				td.drawContents(_painter);
			}
		}
	}

  /* Stop the painter */
  _painter->end();
}

QRect RSPermissionMatrixWidget::computeNodePosition(int n_row,int n_col,bool selected) const
{
    float fact = selected?1.2f:1.0f;

    float S = QFontMetricsF(font()).height();

    return QRect(matrix_start_x + n_col * S*fCOL_SIZE + (S*fCOL_SIZE-S*fICON_SIZE_X*fact)/2,
                 S*fMATRIX_START_Y + n_row * S*fROW_SIZE + (S*fROW_SIZE-S*fICON_SIZE_Y*fact)/2,
                 S*fICON_SIZE_X*fact,
                 S*fICON_SIZE_Y*fact) ;
}

// This function is the inverse of the previous function. Given a mouse position, it
// computes the peer/service switch that is under the mouse. The zoom factor is not
// accounted for, on purpose.

bool RSPermissionMatrixWidget::computeServiceAndPeer(int x,int y,uint32_t& service_id,RsPeerId& peer_id) const
{
    // 1 - make sure that x and y are on a widget

    float S = QFontMetricsF(font()).height();

    x -= matrix_start_x ;
    y -= S*fMATRIX_START_Y ;

    if(x < 0 || x >= service_ids.size() * S*fCOL_SIZE) return false ;
    if(y < 0 || y >= peer_ids.size()    * S*fROW_SIZE) return false ;

    if( (x % (int)(S*fCOL_SIZE)) < (S*fCOL_SIZE - S*fICON_SIZE_X)/2) return false ;
    if( (x % (int)(S*fCOL_SIZE)) > (S*fCOL_SIZE + S*fICON_SIZE_X)/2) return false ;

    if( (y % (int)(S*fROW_SIZE)) < (S*fROW_SIZE - S*fICON_SIZE_Y)/2) return false ;
    if( (y % (int)(S*fROW_SIZE)) > (S*fROW_SIZE + S*fICON_SIZE_Y)/2) return false ;

    // 2 - find which widget, by looking into the service perm matrix

    service_id = service_ids[x / (int)(S*fCOL_SIZE)] ;
    peer_id = peer_ids[y / (int)(S*fCOL_SIZE)] ;

    return true ;
}

bool RSPermissionMatrixWidget::computeServiceGlobalSwitch(int x,int y,uint32_t& service_id) const
{
    // 1 - make sure that x and y are on a widget

    float S = QFontMetricsF(font()).height();

    x -= matrix_start_x ;
    y -= S*fMATRIX_START_Y ;

    if(x < 0 || x >= service_ids.size() * S*fCOL_SIZE) return false ;

    if( (x % (int)(S*fCOL_SIZE)) < (S*fCOL_SIZE - S*fICON_SIZE_X)/2) return false ;
    if( (x % (int)(S*fCOL_SIZE)) > (S*fCOL_SIZE + S*fICON_SIZE_X)/2) return false ;

    if( y < -S*fROW_SIZE ) return false ;
    if( y >  0        ) return false ;

    // 2 - find which widget, by looking into the service perm matrix

    service_id = service_ids[x / (int)(S*fCOL_SIZE)] ;

    return true ;
}

void RSPermissionMatrixWidget::defaultPermissionSwitched(uint32_t /* ServiceId */,bool /* b */)
{
    NOT_IMPLEMENTED ;
}

void RSPermissionMatrixWidget::userPermissionSwitched(uint32_t /* ServiceId */,const RsPeerId& /* friend_id */,bool /* b */)
{
    NOT_IMPLEMENTED ;
}

QString RSPermissionMatrixWidget::ServiceDescription(uint16_t serviceid)
{
	switch(serviceid)
	{
		case RS_SERVICE_TYPE_DISC:			return tr("Location info exchange between friends. Helps to find actual address in case of dynamic IPs<br>Without it you will have to rely on DHT only for getting fresh addresses");
		case RS_SERVICE_TYPE_CHAT:			return tr("Used by direct F2F chat, distant chat and chat lobbies");
		case RS_SERVICE_TYPE_MSG:			return tr("Mailing service. Also required for direct f2f chat");
		case RS_SERVICE_TYPE_TURTLE:		return tr("Anonymous routing. Used by file transfers and file search,<br> distant chat, distant mail and distant channels/etc sync");
		case RS_SERVICE_TYPE_HEARTBEAT:		return tr("Checks if peers alive");
		case RS_SERVICE_TYPE_FILE_TRANSFER:	return tr("File transfer. If you kill it - you won't be able to dl files from friend shares. Anonymous access unnaffected");
		case RS_SERVICE_TYPE_GROUTER:		return tr("Used by distant mail for immediate delivery using anonymous tunnels (turtle router)");
		case RS_SERVICE_TYPE_FILE_DATABASE:	return tr("Exchange shared directories info, aka browsable(visible) files");
		case RS_SERVICE_TYPE_SERVICEINFO:	return tr("Allows your node to tell to your friends which service are ON on your side, and vice-versa");
		case RS_SERVICE_TYPE_BWCTRL:		return tr("Speed management");
		case RS_SERVICE_TYPE_GXS_TUNNEL:	return tr("Used by distant chat, distant mail, and distant channels sync for transfer data using anonymous tunnels");
		case RS_SERVICE_TYPE_BANLIST:		return tr("IP filter lists exchange");
		case RS_SERVICE_TYPE_STATUS:		return tr("Share user status like online, away, busy with friends");
		case RS_SERVICE_GXS_TYPE_GXSID:		return tr("Identity data exchange. Required by all identities-related functions like chats, forums, mail, etc");
		case RS_SERVICE_GXS_TYPE_PHOTO:		return tr("RS_SERVICE_GXS_TYPE_PHOTO");
		case RS_SERVICE_GXS_TYPE_WIKI:		return tr("RS_SERVICE_GXS_TYPE_WIKI");
		case RS_SERVICE_GXS_TYPE_FORUMS:	return tr("Transfer Forums");
		case RS_SERVICE_GXS_TYPE_POSTED:	return tr("Transfer Posted");
		case RS_SERVICE_GXS_TYPE_CHANNELS:	return tr("Transfer Channels");
		case RS_SERVICE_GXS_TYPE_GXSCIRCLE:	return tr("Transfer Circles");
		case RS_SERVICE_GXS_TYPE_REPUTATION:return tr("Votes exchange - bans/upvotes for Identities");
		case RS_SERVICE_TYPE_GXS_TRANS:		return tr("Used by distant mail for deferred delivery - stored at friends when target offline");
		case RS_SERVICE_TYPE_RTT:			return tr("Measures the Round Trip Time between you and your friends");
		default:							return tr("unknown");
	}
}
