/*=========================================================================

  Copyright (c) Kitware, Inc.
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/VolViewCopyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "vtkObjectFactory.h"
#include "vtkURIHandler.h"
#include "vtkKWRemoteIOManager.h"

#include "Curl/curl/mprintf.h"

//------------------------------------------------------------------------------
// The point of this function would be to return a string of the input data,
// but never longer than 5 columns (+ one zero byte).
// Add suffix k, M, G when suitable...
static char *max6data(curl_off_t bytes, char *max6)
{
  #define ONE_KILOBYTE 1024
  #define ONE_MEGABYTE (1024* ONE_KILOBYTE)
  #define ONE_GIGABYTE (1024* ONE_MEGABYTE)
  #define ONE_TERRABYTE ((curl_off_t)1024* ONE_GIGABYTE)
  #define ONE_PETABYTE ((curl_off_t)1024* ONE_TERRABYTE)
  
  #if SIZEOF_CURL_OFF_T > 4
  #define FORMAT_OFF_T "lld"
  #else
  #define FORMAT_OFF_T "ld"
  #endif /* SIZEOF_CURL_OFF_T */
    
  if(bytes < 1000) 
    {
    curl_msnprintf(max6, 5, "%3" FORMAT_OFF_T "B", bytes);
    }
  else if(bytes < (100*ONE_KILOBYTE)) 
    {
    /* 'XX.XKB' is good as long as we're less than 100 kB */
    curl_msnprintf(max6, 7, "%2d.%0dKB",
             (int)(bytes/ONE_KILOBYTE),
             (int)(bytes%ONE_KILOBYTE)/(ONE_KILOBYTE/10) );
    }
  else if(bytes < (1000*ONE_KILOBYTE)) 
    {
    curl_msnprintf(max6, 6, "%3" FORMAT_OFF_T "kB", (curl_off_t)(bytes/ONE_KILOBYTE));
    }
  else if(bytes < (100*ONE_MEGABYTE)) 
    {
    /* 'XX.XM' is good as long as we're less than 100 megs */
    curl_msnprintf(max6, 7, "%2d.%0dMB",
             (int)(bytes/ONE_MEGABYTE),
             (int)(bytes%ONE_MEGABYTE)/(ONE_MEGABYTE/10) );
    }

  #if SIZEOF_CURL_OFF_T > 4
  else if(bytes < ( (curl_off_t)10000*ONE_MEGABYTE))
    {
    /* 'XXXXM' is good until we're at 10000MB or above */
    curl_msnprintf(max6, 7, "%4" FORMAT_OFF_T "MB", (curl_off_t)(bytes/ONE_MEGABYTE));
    }

  else if(bytes < (curl_off_t)100*ONE_GIGABYTE)
    {
    /* 10000 MB - 100 GB, we show it as XX.XG */
    curl_msnprintf(max6, 7, "%2d.%0dGB",
             (int)(bytes/ONE_GIGABYTE),
             (int)(bytes%ONE_GIGABYTE)/(ONE_GIGABYTE/10) );
    }

  else if(bytes < (curl_off_t)10000 * ONE_GIGABYTE)
    {
    /* up to 10000GB, display without decimal: XXXXG */
    curl_msnprintf(max6, 7, "%4dGB", (int)(bytes/ONE_GIGABYTE));
    }

  else if(bytes < (curl_off_t)10000 * ONE_TERRABYTE)
    {
    /* up to 10000TB, display without decimal: XXXXT */
    curl_msnprintf(max6, 7, "%4dTB", (int)(bytes/ONE_TERRABYTE));
    }
  else 
    {
    /* up to 10000PB, display without decimal: XXXXP */
    curl_msnprintf(max6, 7, "%4dPB", (int)(bytes/ONE_PETABYTE));

    /* 16384 petabytes (16 exabytes) is maximum a 64 bit number can hold,
       but this type is signed so 8192PB will be max.*/
    }

  #else
  else
    curl_msnprintf(max6, 7, "%4" FORMAT_OFF_T "MB", (curl_off_t)(bytes/ONE_MEGABYTE));
  #endif

  return max6;
}

//----------------------------------------------------------------------------
vtkCxxRevisionMacro ( vtkURIHandler, "$Revision: 1.9 $" );

//----------------------------------------------------------------------------
vtkURIHandler::vtkURIHandler()
{
  this->LocalFile = NULL;
  this->Progress = 0.0;
  this->LastReportedProgress = 0.0;
  this->DownloadedSize = 0.0;
  this->TotalDownloadSize = 0.0;
  strcpy( this->EstimatedTimeRemaining, "--h --m --s" );
}

//----------------------------------------------------------------------------
vtkURIHandler::~vtkURIHandler()
{
  this->LocalFile = NULL;
}

//----------------------------------------------------------------------------
void vtkURIHandler::PrintSelf(ostream& os, vtkIndent indent)
{
  Superclass::PrintSelf ( os, indent );
}

//----------------------------------------------------------------------------
void vtkURIHandler::SetLocalFile (FILE *localFile )
{
  this->LocalFile = localFile;
}

//----------------------------------------------------------------------------
size_t vtkURIHandler::BufferedWrite ( char *buffer, size_t size, size_t nitems )
{
  if ( this->LocalFile != NULL )
    {
    //this->LocalFile->write(buffer,size*nitems);
    fwrite(buffer, sizeof(char), size*nitems, this->LocalFile);
    size *= nitems;
    return size;
    }
  else
    {
    return ( 0 );
    }
}

//----------------------------------------------------------------------------
void vtkURIHandler::SetProgress( double progress )
{
  this->Progress = progress;

  // Report progress only in 10% increments.
  if ((this->Progress - this->LastReportedProgress) > 0.1)
    {
    this->InvokeEvent( vtkKWRemoteIOManager::TransferUpdateEvent, NULL ); 
    this->LastReportedProgress = this->Progress;
    }
}

//----------------------------------------------------------------------------
void vtkURIHandler::SetTotalDownloadSize( double d )
{
  this->TotalDownloadSize = d;
}

//----------------------------------------------------------------------------
void vtkURIHandler::SetDownloadedSize( double d )
{
  this->DownloadedSize = d;
}

//----------------------------------------------------------------------------
const char * vtkURIHandler::GetFormattedDownloadFractionAsString()
{
  char strNumerator[7], strDenominator[7];
  max6data( this->DownloadedSize, strNumerator );
  max6data( this->TotalDownloadSize, strDenominator );
  this->FormattedDownloadFractionString = strNumerator;
  this->FormattedDownloadFractionString += "/";
  this->FormattedDownloadFractionString += strDenominator;
  return this->FormattedDownloadFractionString.c_str();
}

