// Copyright distributed.net 1998 - All Rights Reserved
// For use in distributed.net projects only.
// Any other distribution or use of this source violates copyright.

// $Log: clisrate.cpp,v $
// Revision 1.33.2.2  1998/11/08 11:50:37  remi
// Lots of $Log tags.
//

#include "cputypes.h"  // for u64
#include "problem.h"   // for Problem class
#include "client.h"    // for Fileentry struct
#include "baseincs.h"  // for timeval, sprintf et al
#include "clitime.h"   // for CliTimer(), CliTimerDiff(), CliGetTimeString()
#include "clirate.h"   // for CliGetKeyrateFor[Problem|Contest]()
#include "clicdata.h"  // for CliGetContestInfo[Base|Summary]Data()
#include "network.h"   // for ntohl()/htonl()
#include "clisrate.h"  // included just to keep prototypes accurate

/*
 *
 * Synopsis:
 *
 *  char *num_sep(char *number)
 *
 * Description:
 *
 *  This routine takes a string which actually represents a number
 *  for instance "1234567" and converts it to "1,234,567". Effectively
 *  it adds a comma for each 3 digits. The function checks whether
 *  a 'fractional part' indicated by a '.' is present and takes care
 *  of this by skipping the '.', the fraction, and whatever follows
 *  the fraction.
 *
 * Known Side Effects:
 *
 *  The function currently does not work correctly if 'number' points
 *  to a string with some other characters prefixed to the number in
 *  question. It also doesn't work, if the 'number' string is a pure
 *  integer without a fraction and is still postfixed with some non-number
 *  data.
 *
 * Return Value:
 *
 *  Good case:
 *     char * to buffer with number separators inserted.
 *
 *  Bad case:
 *     char *number (exactly what it got as input) The bad case happens
 *                  if the input points to a huge string which would
 *                  cause a buffer overflow during processing of the
 *                  input data.
 *
 */

static char *num_sep(char *number)
{

    #define STR_LEN 32
    static char num_string[STR_LEN + 1];

    char *rp, *wp, *xp;             /* read ptr, write ptr, aux ptr */
    register unsigned int digits;
    register unsigned int i, j;

    /*
     * just in case somebody passes a pointer to a long string which
     * then obviously holds much more than just a number. In this case
     * we simply return what we got and don't do anything.
     */

    if (strlen(number) >= STR_LEN)
        return(number);

    strcpy(num_string, number);
    digits = strlen(num_string);
    rp = num_string + digits - 1;
    wp = num_string + STR_LEN;
    *wp-- = '\0';
    if ((xp = strchr(num_string, '.')) != NULL) {
        while (rp >= xp) {
            *wp-- = *rp--;
            digits--;
        }
    }
    for (i = digits, j = 1; i; i--, j++) {
        *wp-- = *rp--;
        if (j && ((j % 3) == 0))
           *wp-- = ',';
    }
    if (wp[1] == ',')
       wp++;
    return(wp + 1);
}


// ---------------------------------------------------------------------------

// returns keyrate as string (len<=26) "nnnn.nn ['K'|'M'|'G'|'T']"
// return value is a pointer to buffer.
static char *__CliGetKeyrateAsString( char *buffer, double rate, double limit )
{
  if (rate<=((double)(0)))  // unfinished (-2) or error (-1) or impossible (0)
    strcpy( buffer, "---.-- " );
  else
  {
    unsigned int t1, t2 = 0;
    const char *t3[]={"","k","M","G","T"}; // "", "kilo", "mega", "giga", "tera"
    while (t2<=5 && (((double)(rate))>=((double)(limit))) )
    {
      t2++;
      rate=((double)(rate))/((double)(1000));
    }
    if (t2>4)
      strcpy( buffer, "***.** " ); //overflow (rate>1.0 TKeys. Str>25 chars)
    else
    {
      t1 = (unsigned int)(rate);
      sprintf( buffer, "%u.%02u %s", t1,
         ((unsigned int)((((double)(rate-((double)(t1)))))*((double)(100)))),
         t3[t2] );
    }
  }
  return buffer;
}

// returns keyrate as string (len<=26) "nnnn.nn ['k'|'M'|'G'|'T']"
// return value is a pointer to buffer.
char *CliGetKeyrateAsString( char *buffer, double rate )
{ 
  return (num_sep(__CliGetKeyrateAsString( buffer, rate, _U32LimitDouble_ )));
}

// ---------------------------------------------------------------------------

// "4 RC5 blocks 12:34:56.78 - [234.56 Kkeys/s]" 
const char *CliGetSummaryStringForContest( int contestid )
{
  static char str[70];
  char keyrate[32];
  const char *keyrateP, *name;
  unsigned blocks;
  struct timeval ttime;

  if ( CliIsContestIDValid( contestid ) ) //clicdata.cpp
  {
    CliGetContestInfoBaseData( contestid, &name, NULL ); //clicdata.cpp
    CliGetContestInfoSummaryData( contestid, &blocks, NULL, &ttime ); //ditto
    keyrateP=__CliGetKeyrateAsString(keyrate,
          CliGetKeyrateForContest(contestid),((double)(1000)));
  }
  else
  {
    name = "???";
    blocks = 0;
    ttime.tv_sec = 0;
    ttime.tv_usec = 0;
    keyrateP = "---.-- ";
  }

  sprintf(str, "%d %s block%s %s%c- [%skeys/s]", 
       blocks, name, ((blocks==1)?(""):("s")),
       CliGetTimeString( &ttime, 2 ), ((!blocks)?(0):(' ')), keyrateP );
  return str;
}

// ---------------------------------------------------------------------------

// return iter/keysdone/whatever as string. set inNetOrder if 'u'
// needs ntohl()ing first, set contestID = -1 to have the ID ignored
const char *CliGetU64AsString( u64 *u, int inNetOrder, int contestid )
{
  static char str[32];
  unsigned int i;
  u64 norm;
  double d;

  norm.hi = u->hi;
  norm.lo = u->lo;

  if (inNetOrder)
  {
    norm.hi = ntohl( norm.hi );
    norm.lo = ntohl( norm.lo );
  }

  d = U64TODOUBLE(norm.hi, norm.lo);
  if (CliGetContestInfoBaseData( contestid, NULL, &i )==0 && i>1) //clicdata
    d = d * ((double)(i));

  i = 0;
  norm.hi = (unsigned int)(d / 1000000000.0);
  norm.lo = (unsigned int)(d - (((double)(norm.hi))*1000000000.0));
  d = d / 1000000000.0;
  if (d > 0)
  {
    i = (unsigned int)(d / 1000000000.0);
    norm.hi = (unsigned int)(d - (((double)(i))*1000000000.0));
  }

  if (i)            sprintf( str, "%u%09u%09u", (unsigned) i, (unsigned) norm.hi, (unsigned) norm.lo );
  else if (norm.hi) sprintf( str, "%u%09u", (unsigned) norm.hi, (unsigned) norm.lo );
  else              sprintf( str, "%u", (unsigned) norm.lo );

  return str;
}

// ---------------------------------------------------------------------------

// internal - with or without adjusting cumulative stats
// Completed RC5 block 68E0D85A:A0000000 (123456789 keys)
//          123:45:67:89 - [987654321 keys/s]
static const char *__CliGetMessageForProblemCompleted( Problem *prob, int doSave )
{
  static char str[160];
  RC5Result rc5result;
  struct timeval tv;
  char keyrate[32];
  unsigned int mulfactor;
  const char *keyrateP, *name;
  int contestid = prob->GetResult( &rc5result );

  if (CliGetContestInfoBaseData( contestid, &name, &mulfactor )==0) //clicdata
    {
    keyrateP = CliGetKeyrateAsString( keyrate, 
        ((doSave) ? ( CliGetKeyrateForProblem( prob ) ) :
                    ( CliGetKeyrateForProblemNoSave( prob ) ))  );
    }                    
  else
    {
    keyrateP = "---.-- ";
    name = "???";
    }

  tv.tv_sec = prob->timehi;
  tv.tv_usec = prob->timelo;
  CliTimerDiff( &tv, &tv, NULL );

/*                               Old method with numeric keycount display
  sprintf( str, "Completed %s block %08lX:%08lX (%s keys)\n"
                "[%s] %s - [%skeys/sec]\n",
                name,
                (unsigned long) ntohl( rc5result.key.hi ) ,
                (unsigned long) ntohl( rc5result.key.lo ),
                CliGetU64AsString( &(rc5result.iterations), 1, contestid ),
                CliGetTimeString( NULL, 1 ),
                CliGetTimeString( &tv, 2 ),
                keyrateP );
*/

  unsigned int /* size=1, count=32, */ itermul=16;
  if (!rc5result.iterations.hi)
    {
    u32 iter = ntohl(rc5result.iterations.lo);
    /*
    count = 1;
    size = 0;
    while ((iter & count)==0)
      { size++; count <<= 1; }
    count = iter / (1<<size);
    iter = ntohl(rc5result.iterations.lo);
    */
    itermul = 0;
    while (iter > 1 && itermul < 28)
      { iter>>=1; itermul++; }
    itermul = (unsigned int)iter;
    }

//"Completed one RC5 block 00000000:00000000 (4*2^28 keys)\n"
//"%s - [%skeys/sec]\n"

  sprintf( str, "Completed one %s block %08lX:%08lX (%u*2^28 keys)\n"
                "%s - [%skeys/sec]\n",  
                name, 
                (unsigned long) ntohl( rc5result.key.hi ) ,
                (unsigned long) ntohl( rc5result.key.lo ),
                (unsigned int)(itermul),
                CliGetTimeString( &tv, 2 ),
                keyrateP );

  return str;
}

// Completed RC5 block 68E0D85A:A0000000 (123456789 keys)
//          123:45:67:89 - [987654321 keys/s]
const char *CliGetMessageForProblemCompleted( Problem *prob )
{ return __CliGetMessageForProblemCompleted( prob, 1 ); }

const char *CliGetMessageForProblemCompletedNoSave( Problem *prob )
{ return __CliGetMessageForProblemCompleted( prob, 0 ); }

// ---------------------------------------------------------------------------

const char *CliReformatMessage( const char *header, const char *message )
{
  static char strspace[160];

  unsigned int prelen, linelen, doquote = (header!=NULL);
  char buffer[84];
  char *bptr, *sptr;
  const char *mptr = message;

  strspace[0]=0;
  if (mptr && *mptr)
  {
    while (*mptr == ' ' || *mptr == '\n')
      mptr++;

    sprintf( strspace, "[%s] ", CliGetTimeString(NULL,1) );
    prelen = strlen( strspace );
    if (header && *header) strcat( strspace, header );
    if (doquote) strcat( strspace, "\"" );

    //first line

    linelen = strlen( strspace );
    bptr = buffer;

    while (linelen < 78)
    {
      if (!*mptr)
        break;
      if (*mptr=='\n')
      {
        mptr++;
        break;
      }
      *bptr++ = *mptr++;
      linelen++;
    }
    if (linelen >= 78)
    {
      *bptr = 0;
      if ((sptr = strrchr( buffer, ' '))!=NULL)
      {
        mptr -= (bptr-sptr);
        bptr = sptr;
      }
    }
    while (*mptr==' ' || *mptr=='\n')
      mptr++;
    if (!*mptr && doquote)
      *bptr++ = '\"';
    *bptr++='\n';
    *bptr=0;

    strcat(strspace, buffer );

    //second line

    if (*mptr)
    {
      bptr = strspace+strlen( strspace );
      for (linelen=0;linelen<prelen;linelen++)
        *bptr++ = ' ';
      *bptr = 0;

      linelen = prelen;
      bptr = buffer;

      while (linelen < 78)
      {
        if (!*mptr)
          break;
        if (*mptr=='\n')
        {
          mptr++;
          break;
        }
        *bptr++ = *mptr++;
        linelen++;
      }
      if (linelen >= 78)
      {
        *bptr = 0;
        if ((sptr = strrchr( buffer, ' '))!=NULL)
        {
          mptr -= (bptr-sptr);
          bptr = sptr;
        }
      }
      if (!*mptr && doquote)
        *bptr++ = '\"';
      *bptr++='\n';
      *bptr=0;

      strcat(strspace, buffer );
    }

    //end of second line

    bptr = strspace;                 //convert non-breaking space
    while (*bptr)
    {
      if (*bptr == (char) 0xFF)
        *bptr = ' ';
      bptr++;
    }
  }
  return (const char *)(strspace);
}

// ---------------------------------------------------------------------------

