// swad_profile.c: user's public profile

/*
    SWAD (Shared Workspace At a Distance),
    is a web platform developed at the University of Granada (Spain),
    and used to support university teaching.

    This file is part of SWAD core.
    Copyright (C) 1999-2025 Antonio Caas Vargas

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General 3 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 <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************/
/*********************************** Headers *********************************/
/*****************************************************************************/

#define _GNU_SOURCE 		// For asprintf
#include <stddef.h>		// For NULL
#include <stdio.h>		// For asprintf
#include <stdlib.h>		// For free
#include <string.h>		// For string functions

#include "swad_action_list.h"
#include "swad_box.h"
#include "swad_browser_database.h"
#include "swad_config.h"
#include "swad_database.h"
#include "swad_enrolment_database.h"
#include "swad_error.h"
#include "swad_figure.h"
#include "swad_follow_database.h"
#include "swad_form.h"
#include "swad_forum_database.h"
#include "swad_global.h"
#include "swad_hierarchy_type.h"
#include "swad_HTML.h"
#include "swad_language.h"
#include "swad_log_database.h"
#include "swad_message.h"
#include "swad_message_database.h"
#include "swad_network.h"
#include "swad_nickname.h"
#include "swad_notification.h"
#include "swad_notification_database.h"
#include "swad_parameter.h"
#include "swad_photo.h"
#include "swad_privacy.h"
#include "swad_profile.h"
#include "swad_profile_database.h"
#include "swad_role.h"
#include "swad_role_type.h"
#include "swad_scope.h"
#include "swad_setting.h"
#include "swad_setting_database.h"
#include "swad_theme.h"
#include "swad_timeline.h"
#include "swad_timeline_database.h"
#include "swad_user.h"
#include "swad_user_database.h"

/*****************************************************************************/
/************** External global variables from others modules ****************/
/*****************************************************************************/

extern struct Globals Gbl;

/*****************************************************************************/
/***************************** Private prototypes ****************************/
/*****************************************************************************/

static void Prf_ShowTimeSinceFirstClick (const struct Usr_Data *UsrDat,
                                         const struct Prf_UsrFigures *UsrFigures);
static void Prf_ShowNumCrssWithRole (const struct Usr_Data *UsrDat,
				     Rol_Role_t Role);
static void Prf_ShowNumFilesCurrentlyPublished (const struct Usr_Data *UsrDat);
static void Prf_ShowNumClicks (const struct Usr_Data *UsrDat,
                               const struct Prf_UsrFigures *UsrFigures);
static void Prf_ShowNumFileViews (const struct Usr_Data *UsrDat,
                                  const struct Prf_UsrFigures *UsrFigures);
static void Prf_ShowNumTimelinePubs (const struct Usr_Data *UsrDat,
                                     const struct Prf_UsrFigures *UsrFigures);
static void Prf_ShowNumForumPosts (const struct Usr_Data *UsrDat,
                                   const struct Prf_UsrFigures *UsrFigures);
static void Prf_ShowNumMessagesSent (const struct Usr_Data *UsrDat,
                                     const struct Prf_UsrFigures *UsrFigures);
static void Prf_BeginListItem (const char *Title,const char *Icon);
static void Prf_EndListItem (void);
static void Prf_PutLinkCalculateFigures (const char *EncryptedUsrCod);

static void Prf_ShowRanking (unsigned Rank,unsigned NumUsrs);

static void Prf_GetFirstClickFromLogAndStoreAsUsrFigure (long UsrCod);
static void Prf_GetNumClicksAndStoreAsUsrFigure (long UsrCod);
static void Prf_GetNumTimelinePubsAndStoreAsUsrFigure (long UsrCod);
static void Prf_GetNumFileViewsAndStoreAsUsrFigure (long UsrCod);
static void Prf_GetNumForumPostsAndStoreAsUsrFigure (long UsrCod);
static void Prf_GetNumMessagesSentAndStoreAsUsrFigure (long UsrCod);

static void Prf_ResetUsrFigures (struct Prf_UsrFigures *UsrFigures);

static void Prf_GetAndShowRankingFigure (Hie_Level_t HieLvl,const char *FldName);
static void Prf_ShowUsrInRanking (struct Usr_Data *UsrDat,unsigned Rank,
                                  Usr_MeOrOther_t MeOrOther);

/*****************************************************************************/
/************* Suggest who to follow or request user's profile ***************/
/*****************************************************************************/

void Prf_SeeSocialProfiles (void)
  {
   if (Gbl.Usrs.Me.Logged)
      Fol_SuggestUsrsToFollowOnMainZone ();
   else
      Prf_ReqUserProfile ();
  }

/*****************************************************************************/
/************************** Get public profile URL ***************************/
/*****************************************************************************/

char *Prf_GetURLPublicProfile (char URL[WWW_MAX_BYTES_WWW + 1],
                               const char *NickWithoutArr)
  {
   extern const char *Lan_STR_LANG_ID[1 + Lan_NUM_LANGUAGES];

   /***** Build URL using nickname *****/
   snprintf (URL,WWW_MAX_BYTES_WWW + 1,"%s/%s?usr=@%s",
	     Cfg_URL_SWAD_CGI,Lan_STR_LANG_ID[Gbl.Prefs.Language],
	     NickWithoutArr);

   return URL;
  }

/*****************************************************************************/
/******************** Put link to view my public profile *********************/
/*****************************************************************************/

void Prf_PutLinkMyPublicProfile (void)
  {
   extern const char *Txt_My_public_profile;

   Lay_PutContextualLinkIconText (ActSeeOthPubPrf,NULL,
				  Usr_PutParOtherUsrCodEncrypted,Gbl.Usrs.Me.UsrDat.EnUsrCod,
				  "circle-user.svg",Ico_BLACK,
				  Txt_My_public_profile,NULL);
  }

/*****************************************************************************/
/***************** Put link to request another user's profile ****************/
/*****************************************************************************/

void Prf_PutLinkReqAnotherUsrProfile (void)
  {
   extern const char *Txt_Another_user_s_profile;

   Lay_PutContextualLinkIconText (ActReqOthPubPrf,NULL,
                                  NULL,NULL,
				  "circle-user.svg",Ico_BLACK,
				  Txt_Another_user_s_profile,NULL);
  }

/*****************************************************************************/
/************************** Request a user's profile *************************/
/*****************************************************************************/

void Prf_ReqUserProfile (void)
  {
   extern const char *Hlp_START_Profiles_view_public_profile;
   extern const char *Txt_Another_user_s_profile;
   extern const char *Txt_Nickname;
   char NickWithArr[Nck_MAX_BYTES_NICK_WITH_ARROBA + 1];

   if (Gbl.Usrs.Me.Logged)
     {
      /***** Contextual menu *****/
      Mnu_ContextMenuBegin ();
	 Prf_PutLinkMyPublicProfile ();	// My public profile
	 Fol_PutLinkWhoToFollow ();	// Users to follow
      Mnu_ContextMenuEnd ();
     }

   /***** Begin form *****/
   Frm_BeginForm (ActSeeOthPubPrf);

      /***** Begin box *****/
      Box_BoxBegin (Txt_Another_user_s_profile,NULL,NULL,
		    Hlp_START_Profiles_view_public_profile,Box_NOT_CLOSABLE);

	 /***** Form to request user's @nickname *****/
	 /* By default, the nickname is filled with my nickname
	    If no user logged ==> the nickname is empty */
	 HTM_LABEL_Begin ("class=\"FORM_IN_%s\"",The_GetSuffix ());
	    HTM_Txt (Txt_Nickname); HTM_Colon (); HTM_NBSP ();
	    snprintf (NickWithArr,sizeof (NickWithArr),"@%s",
		      Gbl.Usrs.Me.UsrDat.Nickname);
	    HTM_INPUT_TEXT ("usr",sizeof (NickWithArr) - 1,NickWithArr,
			    HTM_REQUIRED,
			    "size=\"16\" class=\"INPUT_%s\"",The_GetSuffix ());
	 HTM_LABEL_End ();

      /***** Send button and end box *****/
      Box_BoxWithButtonEnd (Btn_CONTINUE);

   /***** End form *****/
   Frm_EndForm ();
  }

/*****************************************************************************/
/******************** Get user and show a user's profile *********************/
/*****************************************************************************/
// Gbl.Usrs.Other.UsrDat.UsrCod may be already taken. If not ==> try to get it

void Prf_GetUsrDatAndShowUserProfile (void)
  {
   struct Tml_Timeline Timeline;
   Err_SuccessOrError_t ProfileShown = Err_ERROR;

   /***** Get user's data *****/
   if (Gbl.Usrs.Other.UsrDat.UsrCod <= 0)
      Usr_GetParOtherUsrCodEncryptedAndGetListIDs ();

   /***** Show profile and timeline *****/
   if (Usr_ChkUsrCodAndGetAllUsrDataFromUsrCod (&Gbl.Usrs.Other.UsrDat,
                                                Usr_DONT_GET_PREFS,
                                                Usr_GET_ROLE_IN_CRS) == Exi_EXISTS)
     {
      /* Show profile */
      ProfileShown = Prf_ShowUsrProfile (&Gbl.Usrs.Other.UsrDat);
      if (ProfileShown == Err_SUCCESS)
	 if (Gbl.Usrs.Me.Logged)	// Timeline visible only by logged users
	   {
	    /* Reset timeline context */
            Tml_ResetTimeline (&Timeline);

	    /* Show timeline */
	    HTM_SECTION_Begin (Tml_TIMELINE_SECTION_ID);
	       Tml_ShowTimelineUsr (&Timeline);
	    HTM_SECTION_End ();
	   }
     }

   /***** If profile could not be shown... *****/
   if (ProfileShown == Err_ERROR)
     {
      /* Show error message */
      Ale_ShowAlertUserNotFoundOrYouDoNotHavePermission ();

      /* Request a user's profile */
      Prf_ReqUserProfile ();
     }

   /***** If it's not me, mark possible notification as seen *****/
   if (Usr_ItsMe (Gbl.Usrs.Other.UsrDat.UsrCod) == Usr_OTHER)	// Not me
      Ntf_DB_MarkNotifAsSeenUsingCod (Ntf_EVENT_FOLLOWER,
                                      Gbl.Usrs.Other.UsrDat.UsrCod);
  }

/*****************************************************************************/
/*************************** Show a user's profile ***************************/
/*****************************************************************************/
// Return Err_ERROR on error

Err_SuccessOrError_t Prf_ShowUsrProfile (struct Usr_Data *UsrDat)
  {
   static void (*PutLinkToUsrProfile[Usr_NUM_ME_OR_OTHER]) (void) =
     {
      [Usr_ME   ] = Prf_PutLinkReqAnotherUsrProfile,	// Request another user's profile
      [Usr_OTHER] = Prf_PutLinkMyPublicProfile		// My public profile
     };
   unsigned NumFollowing;
   unsigned NumFollowers;
   bool UsrFollowsMe;
   bool IFollowUsr;
   Usr_MeOrOther_t MeOrOther = Usr_ItsMe (UsrDat->UsrCod);

   /***** Check if I can see the public profile *****/
   if (Pri_CheckIfICanView (UsrDat->BaPrfVisibility,UsrDat) == Usr_CAN)
     {
      if (Gbl.Usrs.Me.Logged)
	{
	 /***** Contextual menu *****/
	 Mnu_ContextMenuBegin ();
	    PutLinkToUsrProfile[MeOrOther] ();	// Public profile
	    Fol_PutLinkWhoToFollow ();		// Users to follow
	 Mnu_ContextMenuEnd ();
	}

      /***** Shared record card *****/
      if (MeOrOther == Usr_OTHER &&		// If not me...
	  Gbl.Hierarchy.HieLvl == Hie_CRS)	// ...and a course is selected
	{
	 /* Get user's role in current course */
	 UsrDat->Roles.InCurrentCrs = Rol_GetRoleUsrInCrs (UsrDat->UsrCod,
	                                                   Gbl.Hierarchy.Node[Hie_CRS].HieCod);

	 /* Get if user has accepted enrolment in current course */
	 UsrDat->Accepted = Enr_CheckIfUsrHasAcceptedInCurrentCrs (UsrDat);
	}
      Rec_ShowSharedUsrRecord (Rec_SHA_RECORD_PUBLIC,UsrDat,NULL);

      /***** Extended profile *****/
      if (Pri_CheckIfICanView (UsrDat->ExPrfVisibility,UsrDat) == Usr_CAN)
        {
         /***** Show details of user's profile *****/
         Prf_ShowDetailsUserProfile (UsrDat);

	 /***** Count following and followers *****/
	 Fol_GetNumFollow (UsrDat->UsrCod,&NumFollowing,&NumFollowers);
	 if (NumFollowing)
	    UsrFollowsMe = Fol_DB_CheckUsrIsFollowerOf (UsrDat->UsrCod,
						        Gbl.Usrs.Me.UsrDat.UsrCod);
	 else
	    UsrFollowsMe = false;
	 if (NumFollowers)
	    IFollowUsr   = Fol_DB_CheckUsrIsFollowerOf (Gbl.Usrs.Me.UsrDat.UsrCod,
						        UsrDat->UsrCod);
	 else
	    IFollowUsr   = false;

	 /***** Show following and followers *****/
	 Fol_ShowFollowingAndFollowers (UsrDat,
					NumFollowing,NumFollowers,
					UsrFollowsMe,IFollowUsr);
        }

      return Err_SUCCESS;
     }
   return Err_ERROR;
  }

/*****************************************************************************/
/******************** Change my public profile visibility ********************/
/*****************************************************************************/

void Prf_ChangeBasicProfileVis (void)
  {
   /***** Get param with public/private photo *****/
   Gbl.Usrs.Me.UsrDat.BaPrfVisibility = Pri_GetParVisibility ("VisBasPrf",
	                                                      Pri_BASIC_PROFILE_ALLOWED_VIS);

   /***** Store public/private basic profile visibility in database *****/
   Set_DB_UpdateMySettingsAboutBasicProfile ();

   /***** Show form again *****/
   Set_EditSettings ();
  }

void Prf_ChangeExtendedProfileVis (void)
  {
   /***** Get param with public/private photo *****/
   Gbl.Usrs.Me.UsrDat.ExPrfVisibility = Pri_GetParVisibility ("VisExtPrf",
	                                                      Pri_EXTENDED_PROFILE_ALLOWED_VIS);

   /***** Store public/private extended profile visibility in database *****/
   Set_DB_UpdateMySettingsAboutExtendedProfile ();

   /***** Show form again *****/
   Set_EditSettings ();
  }

/*****************************************************************************/
/********************** Show details of user's profile ***********************/
/*****************************************************************************/

void Prf_ShowDetailsUserProfile (const struct Usr_Data *UsrDat)
  {
   bool UsrIsBannedFromRanking;
   struct Prf_UsrFigures UsrFigures;
   Rol_Role_t Role;

   /***** Get figures *****/
   Prf_GetUsrFigures (UsrDat->UsrCod,&UsrFigures);

   /***** Container *****/
   HTM_DIV_Begin ("class=\"PRF_FIG_CONT\"");

      /***** Left list *****/
      /* Begin left list */
      HTM_DIV_Begin ("class=\"PRF_FIG_LEFT_RIGHT\"");
	 HTM_UL_Begin ("class=\"PRF_FIG_UL DAT_STRONG_%s NOWRAP\"",
		       The_GetSuffix ());

	    /* Time since first click */
	    Prf_ShowTimeSinceFirstClick (UsrDat,&UsrFigures);

	    /* Number of courses in which the user has a role */
	    for (Role  = Rol_TCH;
		 Role >= Rol_STD;
		 Role--)
	       Prf_ShowNumCrssWithRole (UsrDat,Role);

	    /* Number of files currently published */
	    Prf_ShowNumFilesCurrentlyPublished (UsrDat);

	 /* End left list */
	 HTM_UL_End ();
      HTM_DIV_End ();

      /***** Right list *****/
      HTM_DIV_Begin ("class=\"PRF_FIG_LEFT_RIGHT\"");

	 UsrIsBannedFromRanking = Prf_DB_CheckIfUsrBanned (UsrDat->UsrCod);
	 if (!UsrIsBannedFromRanking)
	   {
	    /* Begin right list */
	    HTM_UL_Begin ("class=\"PRF_FIG_UL DAT_STRONG_%s NOWRAP\"",
			  The_GetSuffix ());

	       /* Number of clicks */
	       Prf_ShowNumClicks (UsrDat,&UsrFigures);

	       /* Number of file views */
	       Prf_ShowNumFileViews (UsrDat,&UsrFigures);

	       /* Number of timeline publications */
	       Prf_ShowNumTimelinePubs (UsrDat,&UsrFigures);

	       /* Number of posts in forums */
	       Prf_ShowNumForumPosts (UsrDat,&UsrFigures);

	       /* Number of messages sent */
	       Prf_ShowNumMessagesSent (UsrDat,&UsrFigures);

	    /* End right list */
	    HTM_UL_End ();
	   }

      HTM_DIV_End ();

   HTM_DIV_End ();
  }

/*****************************************************************************/
/************** Show time since first click in user's profile ****************/
/*****************************************************************************/

static void Prf_ShowTimeSinceFirstClick (const struct Usr_Data *UsrDat,
                                         const struct Prf_UsrFigures *UsrFigures)
  {
   extern const char *Txt_TIME_Since;
   extern const char *Txt_year;
   extern const char *Txt_years;
   extern const char *Txt_month;
   extern const char *Txt_months;
   extern const char *Txt_day;
   extern const char *Txt_days;
   char IdFirstClickTime[Frm_MAX_BYTES_ID + 1];
   int NumYears;
   int NumMonths;

   /***** Time since first click *****/
   Prf_BeginListItem (Txt_TIME_Since,"clock.svg");

   if (UsrFigures->FirstClickTimeUTC)
     {
      /* Create unique id */
      Frm_SetUniqueId (IdFirstClickTime);

      HTM_SPAN_Begin ("id=\"%s\"",IdFirstClickTime);
      HTM_SPAN_End ();
      if (UsrFigures->NumDays > 0)
        {
	 HTM_NBSP ();
	 HTM_OpenParenthesis ();
	    NumYears = UsrFigures->NumDays / 365;
	    if (NumYears)
	      {
	       HTM_Int (NumYears);
	       HTM_NBSP ();
	       HTM_Txt (NumYears == 1 ? Txt_year :
					Txt_years);
	      }
	    else		// Less than one year
	      {
	       NumMonths = UsrFigures->NumDays / 30;
	       if (NumMonths)
	         {
		  HTM_Int (NumMonths);
		  HTM_NBSP ();
		  HTM_Txt (NumMonths == 1 ? Txt_month :
					    Txt_months);
	         }
	       else	// Less than one month
	         {
		  HTM_Int (UsrFigures->NumDays);
		  HTM_NBSP ();
		  HTM_Txt (UsrFigures->NumDays == 1 ? Txt_day :
						      Txt_days);
	         }
	      }
	 HTM_CloseParenthesis ();
        }
      Dat_WriteLocalDateHMSFromUTC (IdFirstClickTime,UsrFigures->FirstClickTimeUTC,
				    Gbl.Prefs.DateFormat,Dat_SEPARATOR_COMMA,
				    Dat_WRITE_TODAY |
				    Dat_WRITE_DATE_ON_SAME_DAY |
				    Dat_WRITE_HOUR |
				    Dat_WRITE_MINUTE);
     }
   else	// First click time is unknown or user never logged
      /***** Button to fetch and store user's figures *****/
      Prf_PutLinkCalculateFigures (UsrDat->EnUsrCod);

   Prf_EndListItem ();
  }

/*****************************************************************************/
/*** Show number of courses in which the user has a role in user's profile ***/
/*****************************************************************************/

static void Prf_ShowNumCrssWithRole (const struct Usr_Data *UsrDat,
				     Rol_Role_t Role)
  {
   extern const char *Rol_Icons[Rol_NUM_ROLES];
   extern const char *Txt_ROLES_SINGUL_Abc[Rol_NUM_ROLES][Usr_NUM_SEXS];
   extern const char *Txt_courses_ABBREVIATION;
   extern const char *Txt_teachers_ABBREVIATION;
   extern const char *Txt_students_ABBREVIATION;
   unsigned NumCrss;

   /***** Number of courses in which the user has a given role *****/
   NumCrss = Enr_DB_GetNumCrssOfUsrWithARole (UsrDat->UsrCod,Role);

   Prf_BeginListItem (Txt_ROLES_SINGUL_Abc[Role][UsrDat->Sex],Rol_Icons[Role]);

      HTM_Unsigned (NumCrss); HTM_NBSP (); HTM_Txt (Txt_courses_ABBREVIATION);

      if (NumCrss)
        {
	 HTM_NBSP ();
	 HTM_OpenParenthesis ();
	    HTM_Unsigned (Enr_DB_GetNumUsrsInCrssOfAUsr (UsrDat->UsrCod,Role,
						         (1 << Rol_NET) |
						         (1 << Rol_TCH)));
	    HTM_NBSP (); HTM_Txt (Txt_teachers_ABBREVIATION); HTM_Slash ();
	    HTM_Unsigned (Enr_DB_GetNumUsrsInCrssOfAUsr (UsrDat->UsrCod,Role,
						         (1 << Rol_STD)));
	    HTM_NBSP (); HTM_Txt (Txt_students_ABBREVIATION);
	 HTM_CloseParenthesis ();
        }

   Prf_EndListItem ();
  }

/*****************************************************************************/
/******** Show number of files currently published in user's profile *********/
/*****************************************************************************/

static void Prf_ShowNumFilesCurrentlyPublished (const struct Usr_Data *UsrDat)
  {
   extern const char *Txt_Files_uploaded;
   extern const char *Txt_file;
   extern const char *Txt_files;
   extern const char *Txt_public_FILES;
   unsigned NumFiles;
   unsigned NumPublicFiles;

   /***** Number of files currently published *****/
   if ((NumFiles = Brw_DB_GetNumFilesUsr (UsrDat->UsrCod)))
      NumPublicFiles = Brw_DB_GetNumPublicFilesUsr (UsrDat->UsrCod);
   else
      NumPublicFiles = 0;

   Prf_BeginListItem (Txt_Files_uploaded,"file.svg");

      HTM_UnsignedTxt (NumFiles,Txt_file,Txt_files); HTM_NBSP ();
      HTM_OpenParenthesis ();
         HTM_Unsigned (NumPublicFiles); HTM_NBSP (); HTM_Txt (Txt_public_FILES);
      HTM_CloseParenthesis ();

   Prf_EndListItem ();
  }

/*****************************************************************************/
/****************** Show number of clicks in user's profile ******************/
/*****************************************************************************/

static void Prf_ShowNumClicks (const struct Usr_Data *UsrDat,
                               const struct Prf_UsrFigures *UsrFigures)
  {
   extern const char *Txt_Clicks;
   extern const char *Txt_clicks;
   extern const char *Txt_day;

   /***** Number of clicks *****/
   Prf_BeginListItem (Txt_Clicks,"mouse-pointer.svg");

      if (UsrFigures->NumClicks >= 0)
	{
	 HTM_Long (UsrFigures->NumClicks); HTM_NBSP ();
         HTM_Txt (Txt_clicks); HTM_NBSP ();
	 Prf_ShowRanking (Prf_DB_GetUsrRankingFigure (UsrDat->UsrCod,"NumClicks"),
			  Prf_DB_GetNumUsrsWithFigure ("NumClicks"));
	 if (UsrFigures->NumDays > 0)
	   {
	    HTM_NBSP ();
	    HTM_OpenParenthesis ();
	       HTM_DoubleFewDigits ((double) UsrFigures->NumClicks /
				    (double) UsrFigures->NumDays);
	       HTM_Slash (); HTM_Txt (Txt_day); HTM_NBSP ();
	       Prf_ShowRanking (Prf_DB_GetRankingNumClicksPerDay (UsrDat->UsrCod),
				Prf_DB_GetNumUsrsWithNumClicksPerDay ());
	   HTM_CloseParenthesis ();
	   }
	}
      else	// Number of clicks is unknown
	 /***** Button to fetch and store user's figures *****/
	 Prf_PutLinkCalculateFigures (UsrDat->EnUsrCod);

   Prf_EndListItem ();
  }

/*****************************************************************************/
/*************** Show number of file views in user's profile *****************/
/*****************************************************************************/

static void Prf_ShowNumFileViews (const struct Usr_Data *UsrDat,
                                  const struct Prf_UsrFigures *UsrFigures)
  {
   extern const char *Txt_Downloads;
   extern const char *Txt_download;
   extern const char *Txt_downloads;
   extern const char *Txt_day;

   /***** Number of file views *****/
   Prf_BeginListItem (Txt_Downloads,"download.svg");

      if (UsrFigures->NumFileViews >= 0)
	{
	 HTM_Long (UsrFigures->NumFileViews);
	 HTM_NBSP ();
         HTM_Txt (UsrFigures->NumFileViews == 1 ? Txt_download :
						  Txt_downloads);
	 HTM_NBSP ();
	 Prf_ShowRanking (Prf_DB_GetUsrRankingFigure (UsrDat->UsrCod,"NumFileViews"),
			  Prf_DB_GetNumUsrsWithFigure ("NumFileViews"));
	 if (UsrFigures->NumDays > 0)
	   {
	    HTM_NBSP ();
	    HTM_OpenParenthesis ();
	       HTM_DoubleFewDigits ((double) UsrFigures->NumFileViews /
				    (double) UsrFigures->NumDays);
	       HTM_Slash ();
	       HTM_Txt (Txt_day);
	    HTM_CloseParenthesis ();
	   }
	}
      else	// Number of file views is unknown
	 /***** Button to fetch and store user's figures *****/
	 Prf_PutLinkCalculateFigures (UsrDat->EnUsrCod);

   Prf_EndListItem ();
  }

/*****************************************************************************/
/********* Show number of timeline publications in user's profile ************/
/*****************************************************************************/

static void Prf_ShowNumTimelinePubs (const struct Usr_Data *UsrDat,
                                     const struct Prf_UsrFigures *UsrFigures)
  {
   extern const char *Txt_Timeline;
   extern const char *Txt_TIMELINE_post;
   extern const char *Txt_TIMELINE_posts;
   extern const char *Txt_day;

   /***** Number of timeline publications *****/
   Prf_BeginListItem (Txt_Timeline,"comment-dots.svg");

      if (UsrFigures->NumTimelinePubs >= 0)
	{
	 HTM_Int (UsrFigures->NumTimelinePubs);
	 HTM_NBSP ();
         HTM_Txt (UsrFigures->NumTimelinePubs == 1 ? Txt_TIMELINE_post :
						     Txt_TIMELINE_posts);
	 HTM_NBSP ();
	 Prf_ShowRanking (Prf_DB_GetUsrRankingFigure (UsrDat->UsrCod,"NumSocPub"),
			  Prf_DB_GetNumUsrsWithFigure ("NumSocPub"));
	 if (UsrFigures->NumDays > 0)
	   {
	    HTM_NBSP ();
	    HTM_OpenParenthesis ();
	       HTM_DoubleFewDigits ((double) UsrFigures->NumTimelinePubs /
				    (double) UsrFigures->NumDays);
	       HTM_Slash ();
	       HTM_Txt (Txt_day);
	    HTM_CloseParenthesis ();
	   }
	}
      else	// Number of timeline publications is unknown
	 /***** Button to fetch and store user's figures *****/
	 Prf_PutLinkCalculateFigures (UsrDat->EnUsrCod);

   Prf_EndListItem ();
  }

/*****************************************************************************/
/********** Show number of timeline publications in user's profile ***********/
/*****************************************************************************/

static void Prf_ShowNumForumPosts (const struct Usr_Data *UsrDat,
                                   const struct Prf_UsrFigures *UsrFigures)
  {
   extern const char *Txt_Forums;
   extern const char *Txt_FORUM_post;
   extern const char *Txt_FORUM_posts;
   extern const char *Txt_day;

   /***** Number of posts in forums *****/
   Prf_BeginListItem (Txt_Forums,"comments.svg");

      if (UsrFigures->NumForumPosts >= 0)
	{
	 HTM_Long (UsrFigures->NumForumPosts);
	 HTM_NBSP ();
         HTM_Txt (UsrFigures->NumForumPosts == 1 ? Txt_FORUM_post :
						   Txt_FORUM_posts);
	 HTM_NBSP ();
	 Prf_ShowRanking (Prf_DB_GetUsrRankingFigure (UsrDat->UsrCod,"NumForPst"),
			  Prf_DB_GetNumUsrsWithFigure ("NumForPst"));
	 if (UsrFigures->NumDays > 0)
	   {
	    HTM_NBSP ();
	    HTM_OpenParenthesis ();
	       HTM_DoubleFewDigits ((double) UsrFigures->NumForumPosts /
				    (double) UsrFigures->NumDays);
	       HTM_Slash ();
	       HTM_Txt (Txt_day);
	    HTM_CloseParenthesis ();
	   }
	}
      else	// Number of forum posts is unknown
	 /***** Button to fetch and store user's figures *****/
	 Prf_PutLinkCalculateFigures (UsrDat->EnUsrCod);

   Prf_EndListItem ();
  }

/*****************************************************************************/
/************** Show number of messages sent in user's profile ***************/
/*****************************************************************************/

static void Prf_ShowNumMessagesSent (const struct Usr_Data *UsrDat,
                                     const struct Prf_UsrFigures *UsrFigures)
  {
   extern const char *Txt_Messages;
   extern const char *Txt_message;
   extern const char *Txt_messages;
   extern const char *Txt_day;

   /***** Number of messages sent *****/
   Prf_BeginListItem (Txt_Messages,"envelope.svg");

      if (UsrFigures->NumMessagesSent >= 0)
	{
	 HTM_Long (UsrFigures->NumMessagesSent);
	 HTM_NBSP ();
         HTM_Txt (UsrFigures->NumMessagesSent == 1 ? Txt_message :
						     Txt_messages);
	 HTM_NBSP ();
	 Prf_ShowRanking (Prf_DB_GetUsrRankingFigure (UsrDat->UsrCod,"NumMsgSnt"),
			  Prf_DB_GetNumUsrsWithFigure ("NumMsgSnt"));
	 if (UsrFigures->NumDays > 0)
	   {
	    HTM_NBSP ();
	    HTM_OpenParenthesis ();
	       HTM_DoubleFewDigits ((double) UsrFigures->NumMessagesSent /
				    (double) UsrFigures->NumDays);
	       HTM_Slash ();
	       HTM_Txt (Txt_day);
	    HTM_CloseParenthesis ();
	   }
	}
      else	// Number of messages sent is unknown
	 /***** Button to fetch and store user's figures *****/
	 Prf_PutLinkCalculateFigures (UsrDat->EnUsrCod);

   Prf_EndListItem ();
  }

/*****************************************************************************/
/****************** Begin/end list item in user's profile ********************/
/*****************************************************************************/

static void Prf_BeginListItem (const char *Title,const char *Icon)
  {
   HTM_LI_Begin ("title=\"%s\" class=\"PRF_FIG_LI\"",Title);
      HTM_DIV_Begin ("class=\"PRF_FIG_ICO\"");
	 Ico_PutIcon (Icon,Ico_BLACK,Title,"CONTEXT_ICOx16");
      HTM_DIV_End ();
  }

static void Prf_EndListItem (void)
  {
   HTM_LI_End ();
  }

/*****************************************************************************/
/******** Put contextual link with animated icon to update an action *********/
/*****************************************************************************/

static void Prf_PutLinkCalculateFigures (const char *EncryptedUsrCod)
  {
   extern const char *Txt_Calculate;

   Frm_BeginForm (ActCalFig);
      Usr_PutParUsrCodEncrypted (EncryptedUsrCod);

      /***** Put icon to refresh *****/
      HTM_BUTTON_Submit_Begin (Act_GetActionText (ActCalFig),NULL,
			       "class=\"BT_LINK FORM_OUT_%s BOLD\"",
			       The_GetSuffix ());
	 Ico_PutIconTextUpdate (Txt_Calculate);
      HTM_BUTTON_End ();

   Frm_EndForm ();
  }

/*****************************************************************************/
/********************** Select values on user's figures **********************/
/*****************************************************************************/

void Prf_GetUsrFigures (long UsrCod,struct Prf_UsrFigures *UsrFigures)
  {
   MYSQL_RES *mysql_res;
   MYSQL_ROW row;

   /***** Get user's figures from database *****/
   switch (Prf_DB_GetUsrFigures (&mysql_res,UsrCod))
     {
      case Exi_EXISTS:
	 /***** Get user's figures *****/
	 row = mysql_fetch_row (mysql_res);

	 /* Get first click (row[0] holds first click time UTC) */
	 UsrFigures->FirstClickTimeUTC = Dat_GetUNIXTimeFromStr (row[0]);

	 /* Get number of days since first click (row[1]) */
	 if (UsrFigures->FirstClickTimeUTC)
	   {
	    if (sscanf (row[1],"%d",&UsrFigures->NumDays) != 1)
	       UsrFigures->NumDays = -1;
	   }
	 else
	    UsrFigures->NumDays = -1;

	 /* Get number of clicks (row[2]) */
	 if (sscanf (row[2],"%d",&UsrFigures->NumClicks) != 1)
	    UsrFigures->NumClicks = -1;

	 /* Get number of timeline publications (row[3]) */
	 if (sscanf (row[3],"%d",&UsrFigures->NumTimelinePubs) != 1)
	    UsrFigures->NumTimelinePubs = -1;

	 /* Get number of file views (row[4]) */
	 if (sscanf (row[4],"%d",&UsrFigures->NumFileViews) != 1)
	    UsrFigures->NumFileViews = -1;

	 /* Get number of forum posts (row[5]) */
	 if (sscanf (row[5],"%d",&UsrFigures->NumForumPosts) != 1)
	    UsrFigures->NumForumPosts = -1;

	 /* Get number of messages sent (row[6]) */
	 if (sscanf (row[6],"%d",&UsrFigures->NumMessagesSent) != 1)
	    UsrFigures->NumMessagesSent = -1;
	 break;
      case Exi_DOES_NOT_EXIST:
      default:
	 /***** Return special user's figures indicating "not present" *****/
	 Prf_ResetUsrFigures (UsrFigures);
	 break;
     }

   /***** Free structure that stores the query result *****/
   DB_FreeMySQLResult (&mysql_res);
  }

/*****************************************************************************/
/************************* Show position in ranking **************************/
/*****************************************************************************/

static void Prf_ShowRanking (unsigned Rank,unsigned NumUsrs)
  {
   extern const char *Txt_of_PART_OF_A_TOTAL;
   char *Title;

   /***** Rank in form to go to ranking *****/
   Frm_BeginForm (ActSeeUseGbl);
      Sco_PutParScope ("ScopeSta",Hie_SYS);
      Par_PutParUnsigned (NULL,"FigureType",(unsigned) Fig_USERS_RANKING);
      if (asprintf (&Title,"#%u %s %u",Rank,Txt_of_PART_OF_A_TOTAL,NumUsrs) < 0)
	 Err_NotEnoughMemoryExit ();
      HTM_BUTTON_Submit_Begin (Title,NULL,
                               "class=\"BT_LINK FORM_OUT_%s\"",
                               The_GetSuffix ());
      free (Title);
         HTM_Number (); HTM_Unsigned (Rank);
      HTM_BUTTON_End ();
   Frm_EndForm ();
  }

/*****************************************************************************/
/********** Calculate user's figures and show user's profile again ***********/
/*****************************************************************************/

void Prf_CalculateFigures (void)
  {
   struct Prf_UsrFigures UsrFigures;

   /***** Get user's code *****/
   Usr_GetParOtherUsrCodEncryptedAndGetListIDs ();

   /***** Get first click time from log and store as user's figure *****/
   Prf_GetUsrFigures (Gbl.Usrs.Other.UsrDat.UsrCod,&UsrFigures);
   Prf_GetAndStoreAllUsrFigures (Gbl.Usrs.Other.UsrDat.UsrCod,&UsrFigures);

   /***** Show user's profile again *****/
   Prf_GetUsrDatAndShowUserProfile ();
  }

/*****************************************************************************/
/*** Calculate and store all figures in user's profile not yet calculated ****/
/*****************************************************************************/
// Return true if any figure has been calculated and stored

bool Prf_GetAndStoreAllUsrFigures (long UsrCod,struct Prf_UsrFigures *UsrFigures)
  {
   bool UsrFiguresModified = false;

   if (!UsrFigures->FirstClickTimeUTC)
     {
      /***** Get first click time from log and store as user's figure *****/
      Prf_GetFirstClickFromLogAndStoreAsUsrFigure (UsrCod);
      UsrFiguresModified = true;
     }
   if (UsrFigures->NumClicks < 0)
     {
      /***** Get number of clicks and store as user's figure *****/
      Prf_GetNumClicksAndStoreAsUsrFigure (UsrCod);
      UsrFiguresModified = true;
     }
   if (UsrFigures->NumTimelinePubs < 0)
     {
      /***** Get number of timeline publications and store as user's figure *****/
      Prf_GetNumTimelinePubsAndStoreAsUsrFigure (UsrCod);
      UsrFiguresModified = true;
     }
   if (UsrFigures->NumFileViews < 0)
     {
      /***** Get number of file views and store as user's figure *****/
      Prf_GetNumFileViewsAndStoreAsUsrFigure (UsrCod);
      UsrFiguresModified = true;
     }
   if (UsrFigures->NumForumPosts < 0)
     {
      /***** Get number of forum posts and store as user's figure *****/
      Prf_GetNumForumPostsAndStoreAsUsrFigure (UsrCod);
      UsrFiguresModified = true;
     }
    if (UsrFigures->NumMessagesSent < 0)
     {
      /***** Get number of messages sent and store as user's figure *****/
      Prf_GetNumMessagesSentAndStoreAsUsrFigure (UsrCod);
      UsrFiguresModified = true;
     }

   return UsrFiguresModified;
  }

/*****************************************************************************/
/*** Get first click of a user from log table and store in user's figures ****/
/*****************************************************************************/

static void Prf_GetFirstClickFromLogAndStoreAsUsrFigure (long UsrCod)
  {
   MYSQL_RES *mysql_res;
   MYSQL_ROW row;
   struct Prf_UsrFigures UsrFigures;

   if (Usr_DB_ChkIfUsrCodExists (UsrCod) == Exi_EXISTS)
     {
      /***** Reset user's figures *****/
      Prf_ResetUsrFigures (&UsrFigures);

      /***** Get first click from log table *****/
      if (Log_DB_GetUsrFirstClick (&mysql_res,UsrCod) == Exi_EXISTS)
	{
	 /* Get first click */
	 row = mysql_fetch_row (mysql_res);

	 /* Get first click (row[0] holds the start date-time UTC) */
	 if (row[0])	// It is NULL when user never logged
	    UsrFigures.FirstClickTimeUTC = Dat_GetUNIXTimeFromStr (row[0]);
	}
      /* Free structure that stores the query result */
      DB_FreeMySQLResult (&mysql_res);

      /***** Update first click time in user's figures *****/
      switch (Prf_DB_CheckIfUsrFiguresExists (UsrCod))
	{
	 case Exi_EXISTS:
	    Prf_DB_UpdateFirstClickTimeUsr (UsrCod,UsrFigures.FirstClickTimeUTC);
	    break;
	 case Exi_DOES_NOT_EXIST:
	 default:
	    Prf_DB_CreateUsrFigures (UsrCod,&UsrFigures,Usr_OTHER);
	    break;
	}
     }
  }

/*****************************************************************************/
/* Get number of clicks of a user from log table and store in user's figures */
/*****************************************************************************/

static void Prf_GetNumClicksAndStoreAsUsrFigure (long UsrCod)
  {
   struct Prf_UsrFigures UsrFigures;

   if (Usr_DB_ChkIfUsrCodExists (UsrCod) == Exi_EXISTS)
     {
      /***** Reset user's figures *****/
      Prf_ResetUsrFigures (&UsrFigures);

      /***** Get number of clicks from database *****/
      UsrFigures.NumClicks = (int) Log_DB_GetUsrNumClicks (UsrCod);

      /***** Update number of clicks in user's figures *****/
      switch (Prf_DB_CheckIfUsrFiguresExists (UsrCod))
	{
	 case Exi_EXISTS:
	    Prf_DB_UpdateNumClicksUsr (UsrCod,UsrFigures.NumClicks);
	    break;
	 case Exi_DOES_NOT_EXIST:
	 default:
	    Prf_DB_CreateUsrFigures (UsrCod,&UsrFigures,Usr_OTHER);
	    break;
	}
     }
  }

/*****************************************************************************/
/**** Get number of timeline pubs. from a user and store in user's figures ***/
/*****************************************************************************/

static void Prf_GetNumTimelinePubsAndStoreAsUsrFigure (long UsrCod)
  {
   struct Prf_UsrFigures UsrFigures;

   if (Usr_DB_ChkIfUsrCodExists (UsrCod) == Exi_EXISTS)
     {
      /***** Reset user's figures *****/
      Prf_ResetUsrFigures (&UsrFigures);

      /***** Get number of forum posts from database *****/
      UsrFigures.NumTimelinePubs = (int) Tml_DB_GetNumPubsUsr (UsrCod);

      /***** Update number of timeline publications in user's figures *****/
      switch (Prf_DB_CheckIfUsrFiguresExists (UsrCod))
	{
	 case Exi_EXISTS:
	    Prf_DB_UpdateNumTimelinePubsUsr (UsrCod,UsrFigures.NumTimelinePubs);
	    break;
	 case Exi_DOES_NOT_EXIST:
	 default:
	    Prf_DB_CreateUsrFigures (UsrCod,&UsrFigures,Usr_OTHER);
	    break;
	}
     }
  }

/*****************************************************************************/
/**** Get number of file views sent by a user and store in user's figures ****/
/*****************************************************************************/

static void Prf_GetNumFileViewsAndStoreAsUsrFigure (long UsrCod)
  {
   struct Prf_UsrFigures UsrFigures;

   if (Usr_DB_ChkIfUsrCodExists (UsrCod) == Exi_EXISTS)
     {
      /***** Reset user's figures *****/
      Prf_ResetUsrFigures (&UsrFigures);

      /***** Get number of file views from database *****/
      UsrFigures.NumFileViews = (long) Brw_DB_GetNumFileViewsUsr (UsrCod);

      /***** Update number of file views in user's figures *****/
      switch (Prf_DB_CheckIfUsrFiguresExists (UsrCod))
	{
	 case Exi_EXISTS:
	    Prf_DB_UpdateNumFileViewsUsr (UsrCod,UsrFigures.NumFileViews);
	    break;
	 case Exi_DOES_NOT_EXIST:
	 default:
	    Prf_DB_CreateUsrFigures (UsrCod,&UsrFigures,Usr_OTHER);
	    break;
	}
     }
  }

/*****************************************************************************/
/**** Get number of forum posts sent by a user and store in user's figures ***/
/*****************************************************************************/

static void Prf_GetNumForumPostsAndStoreAsUsrFigure (long UsrCod)
  {
   struct Prf_UsrFigures UsrFigures;

   if (Usr_DB_ChkIfUsrCodExists (UsrCod) == Exi_EXISTS)
     {
      /***** Reset user's figures *****/
      Prf_ResetUsrFigures (&UsrFigures);

      /***** Get number of forum posts from database *****/
      UsrFigures.NumForumPosts = For_DB_GetNumPostsUsr (UsrCod);

      /***** Update number of forum posts in user's figures *****/
      switch (Prf_DB_CheckIfUsrFiguresExists (UsrCod))
	{
	 case Exi_EXISTS:
	    Prf_DB_UpdateNumForumPostsUsr (UsrCod,UsrFigures.NumForumPosts);
	    break;
	 case Exi_DOES_NOT_EXIST:
	 default:
	    Prf_DB_CreateUsrFigures (UsrCod,&UsrFigures,Usr_OTHER);
	    break;
	}
     }
  }

/*****************************************************************************/
/***** Get number of messages sent by a user and store in user's figures *****/
/*****************************************************************************/

static void Prf_GetNumMessagesSentAndStoreAsUsrFigure (long UsrCod)
  {
   struct Prf_UsrFigures UsrFigures;

   if (Usr_DB_ChkIfUsrCodExists (UsrCod) == Exi_EXISTS)
     {
      /***** Reset user's figures *****/
      Prf_ResetUsrFigures (&UsrFigures);

      /***** Get number of messages sent from database *****/
      UsrFigures.NumMessagesSent = (int) Msg_DB_GetNumMsgsSentByUsr (UsrCod);

      /***** Update number of messages sent in user's figures *****/
      switch (Prf_DB_CheckIfUsrFiguresExists (UsrCod))
	{
	 case Exi_EXISTS:
	    Prf_DB_UpdateNumMessagesSentUsr (UsrCod,UsrFigures.NumMessagesSent);
	    break;
	 case Exi_DOES_NOT_EXIST:
	 default:
	    Prf_DB_CreateUsrFigures (UsrCod,&UsrFigures,Usr_OTHER);
	    break;
	}
     }
  }

/*****************************************************************************/
/******************* Create user's figures for a new user ********************/
/*****************************************************************************/

void Prf_CreateNewUsrFigures (long UsrCod,Usr_MeOrOther_t MeOrOther)
  {
   struct Prf_UsrFigures UsrFigures;

   /***** Reset user's figures *****/
   Prf_ResetUsrFigures (&UsrFigures);
   UsrFigures.NumClicks       = 0;	// set number of clicks to 0
   UsrFigures.NumTimelinePubs = 0;	// set number of timeline publications to 0
   UsrFigures.NumFileViews    = 0;	// set number of file views to 0
   UsrFigures.NumForumPosts   = 0;	// set number of forum posts to 0
   UsrFigures.NumMessagesSent = 0;	// set number of messages sent to 0

   /***** Create user's figures *****/
   Prf_DB_CreateUsrFigures (UsrCod,&UsrFigures,MeOrOther);
  }

/*****************************************************************************/
/********************** Reset values of user's figures ***********************/
/*****************************************************************************/

static void Prf_ResetUsrFigures (struct Prf_UsrFigures *UsrFigures)
  {
   UsrFigures->FirstClickTimeUTC = (time_t) 0;	// unknown first click time or user never logged
   UsrFigures->NumDays         = -1;	// not applicable
   UsrFigures->NumClicks       = -1;	// unknown number of clicks
   UsrFigures->NumTimelinePubs = -1;	// unknown number of timeline publications
   UsrFigures->NumFileViews    = -1;	// unknown number of file views
   UsrFigures->NumForumPosts   = -1;	// unknown number of forum posts
   UsrFigures->NumMessagesSent = -1;	// unknown number of messages sent
  }

/*****************************************************************************/
/******** Get and show ranking of users attending to number of clicks ********/
/*****************************************************************************/

void Prf_GetAndShowRankingClicks (Hie_Level_t HieLvl)
  {
   Prf_GetAndShowRankingFigure (HieLvl,"NumClicks");
  }

void Prf_GetAndShowRankingTimelinePubs (Hie_Level_t HieLvl)
  {
   Prf_GetAndShowRankingFigure (HieLvl,"NumSocPub");
  }

void Prf_GetAndShowRankingFileViews (Hie_Level_t HieLvl)
  {
   Prf_GetAndShowRankingFigure (HieLvl,"NumFileViews");
  }

void Prf_GetAndShowRankingForPsts (Hie_Level_t HieLvl)
  {
   Prf_GetAndShowRankingFigure (HieLvl,"NumForPst");
  }

void Prf_GetAndShowRankingMsgsSnt (Hie_Level_t HieLvl)
  {
   Prf_GetAndShowRankingFigure (HieLvl,"NumMsgSnt");
  }

static void Prf_GetAndShowRankingFigure (Hie_Level_t HieLvl,const char *FldName)
  {
   MYSQL_RES *mysql_res;
   unsigned NumUsrs;

   /***** Get ranking from database *****/
   NumUsrs = Prf_DB_GetRankingFigure (&mysql_res,HieLvl,FldName);

   Prf_ShowRankingFigure (&mysql_res,NumUsrs);
  }

void Prf_ShowRankingFigure (MYSQL_RES **mysql_res,unsigned NumUsrs)
  {
   static const char *Class[Usr_NUM_ME_OR_OTHER] =
     {
      [Usr_ME   ] = "DAT_SMALL_STRONG",
      [Usr_OTHER] = "DAT_SMALL"
     };
   MYSQL_ROW row;
   unsigned NumUsr;
   unsigned Rank;
   struct Usr_Data UsrDat;
   Usr_MeOrOther_t MeOrOther;
   long FigureHigh = LONG_MAX;
   long Figure;

   if (NumUsrs)
     {
      /***** Initialize structure with user's data *****/
      Usr_UsrDataConstructor (&UsrDat);

      HTM_TABLE_Begin (NULL);

	 for (NumUsr  = 1, Rank = 1, The_ResetRowColor ();
	      NumUsr <= NumUsrs;
	      NumUsr++, The_ChangeRowColor ())
	   {
	    /***** Get user and number of clicks *****/
	    row = mysql_fetch_row (*mysql_res);

	    /* Get user's code (row[0]) */
	    UsrDat.UsrCod = Str_ConvertStrCodToLongCod (row[0]);
	    Usr_GetAllUsrDataFromUsrCod (&UsrDat,
					 Usr_DONT_GET_PREFS,
					 Usr_DONT_GET_ROLE_IN_CRS);

	    /* Get figure (row[1]) */
	    if (sscanf (row[1],"%ld",&Figure) != 1)
	       Err_ShowErrorAndExit ("Error reading user's figure.");

	    if (Figure < FigureHigh)
	      {
	       Rank = NumUsr;
	       FigureHigh = Figure;
	      }

	    /***** Show row *****/
	    HTM_TR_Begin (NULL);
	       MeOrOther = Usr_ItsMe (UsrDat.UsrCod);
	       Prf_ShowUsrInRanking (&UsrDat,Rank,MeOrOther);
	       HTM_TD_Begin ("class=\"RM %s_%s %s\"",Class[MeOrOther],
		             The_GetSuffix (),The_GetColorRows ());
		  HTM_Long (Figure);
	       HTM_TD_End ();
	    HTM_TR_End ();
	   }

      HTM_TABLE_End ();

      /***** Free memory used for user's data *****/
      Usr_UsrDataDestructor (&UsrDat);
     }

   /***** Free structure that stores the query result *****/
   DB_FreeMySQLResult (mysql_res);
  }

/*****************************************************************************/
/**** Get and show ranking of users attending to number of clicks per day ****/
/*****************************************************************************/

void Prf_GetAndShowRankingClicksPerDay (Hie_Level_t HieLvl)
  {
   static const char *Class[Usr_NUM_ME_OR_OTHER] =
     {
      [Usr_ME   ] = "DAT_SMALL_STRONG",
      [Usr_OTHER] = "DAT_SMALL"
     };
   MYSQL_RES *mysql_res;
   MYSQL_ROW row;
   unsigned NumUsrs = 0;	// Initialized to avoid warning
   unsigned NumUsr;
   unsigned Rank;
   struct Usr_Data UsrDat;
   Usr_MeOrOther_t MeOrOther;
   double NumClicksPerDayHigh = (double) LONG_MAX;
   double NumClicksPerDay;

   /***** Get ranking from database *****/
   if ((NumUsrs = Prf_DB_GetRankingClicksPerDay (&mysql_res,HieLvl)))
     {
      /***** Initialize structure with user's data *****/
      Usr_UsrDataConstructor (&UsrDat);

      HTM_TABLE_Begin (NULL);

	 for (NumUsr  = 1, Rank = 1, The_ResetRowColor ();
	      NumUsr <= NumUsrs;
	      NumUsr++, The_ChangeRowColor ())
	   {
	    /***** Get user and number of clicks *****/
	    row = mysql_fetch_row (mysql_res);

	    /* Get user's code (row[0]) */
	    UsrDat.UsrCod = Str_ConvertStrCodToLongCod (row[0]);
	    Usr_GetAllUsrDataFromUsrCod (&UsrDat,
					 Usr_DONT_GET_PREFS,
					 Usr_DONT_GET_ROLE_IN_CRS);

	    /* Get average number of clicks per day (row[1]) */
	    if (Str_GetDoubleFromStr (row[1],&NumClicksPerDay) == Err_SUCCESS)
	       if (NumClicksPerDay < NumClicksPerDayHigh)
		 {
		  Rank = NumUsr;
		  NumClicksPerDayHigh = NumClicksPerDay;
		 }

	    /***** Show row *****/
	    HTM_TR_Begin (NULL);
	       MeOrOther = Usr_ItsMe (UsrDat.UsrCod);
	       Prf_ShowUsrInRanking (&UsrDat,Rank,MeOrOther);
	       HTM_TD_Begin ("class=\"RM %s_%s %s\"",Class[MeOrOther],
			     The_GetSuffix (),The_GetColorRows ());
		  HTM_DoubleFewDigits (NumClicksPerDay);
	       HTM_TD_End ();
	    HTM_TR_End ();
	   }

      HTM_TABLE_End ();

      /***** Free memory used for user's data *****/
      Usr_UsrDataDestructor (&UsrDat);
     }

   /***** Free structure that stores the query result *****/
   DB_FreeMySQLResult (&mysql_res);
  }

/*****************************************************************************/
/************** Show user's photo and nickname in ranking list ***************/
/*****************************************************************************/

static void Prf_ShowUsrInRanking (struct Usr_Data *UsrDat,unsigned Rank,
                                  Usr_MeOrOther_t MeOrOther)
  {
   static const char *Class[Usr_NUM_ME_OR_OTHER] =
     {
      [Usr_ME   ] = "DAT_SMALL_STRONG",
      [Usr_OTHER] = "DAT_SMALL"
     };
   static const char *ClassPhoto[PhoSha_NUM_SHAPES] =
     {
      [PhoSha_SHAPE_CIRCLE   ] = "PHOTOC30x40",
      [PhoSha_SHAPE_ELLIPSE  ] = "PHOTOE30x40",
      [PhoSha_SHAPE_OVAL     ] = "PHOTOO30x40",
      [PhoSha_SHAPE_RECTANGLE] = "PHOTOR30x40",
     };
   Usr_Can_t ICanView = Pri_CheckIfICanView (UsrDat->BaPrfVisibility,UsrDat);

   HTM_TD_Begin ("class=\"RM %s_%s %s\"",
		 Class[MeOrOther],The_GetSuffix (),The_GetColorRows ());
      HTM_Number (); HTM_Unsigned (Rank);
   HTM_TD_End ();

   /***** Check if I can see the public profile *****/
   HTM_TD_Begin ("class=\"RANK_PHOTO %s\"",The_GetColorRows ());
      if (ICanView == Usr_CAN)
	 /***** User's photo *****/
	 Pho_ShowUsrPhotoIfAllowed (UsrDat,
	                            ClassPhoto[Gbl.Prefs.PhotoShape],Pho_ZOOM);
   HTM_TD_End ();

   /***** Put form to go to public profile *****/
   HTM_TD_Begin ("class=\"RANK_USR %s_%s %s\"",	// Limited width
                 Class[MeOrOther],The_GetSuffix (),The_GetColorRows ());
      if (ICanView == Usr_CAN)
	{
	 Frm_BeginForm (ActSeeOthPubPrf);
	    Usr_PutParUsrCodEncrypted (UsrDat->EnUsrCod);
	    HTM_BUTTON_Submit_Begin (Act_GetActionText (ActSeeOthPubPrf),NULL,
				     "class=\"LM BT_LINK\"");
	       Usr_WriteFirstNameBRSurnames (UsrDat);
	    HTM_BUTTON_End ();
	 Frm_EndForm ();
	}
   HTM_TD_End ();
  }
