//===========================================================================//
// Purpose : Method definitions for a TC_MinGrid minimum grid class.
//
//           Public methods include:
//           - NewInstance, DeleteInstance, GetInstance
//           - SetGrid
//           - IntToFloat, UnsignedIntToFloat, LongIntToFloat
//           - FloatToInt, FloatToUnsignedInt, FloatToLongInt
//           - SnapToGrid
//           - SnapToGridCeil
//           - IsOnGrid
//
//           Protected methods include:
//           - TC_MinGrid_c, ~TC_MinGrid_c
//
//===========================================================================//

#include "limits.h"
#include "math.h"

#include "TCT_Generic.h"

#include "TC_Typedefs.h"
#include "TC_MinGrid.h"

// Initialize the min grid "singleton" class, as needed
TC_MinGrid_c* TC_MinGrid_c::pinstance_ = 0;

//===========================================================================//
// Method         : TC_MinGrid_c
// Author         : Jeff Rudolph
//---------------------------------------------------------------------------//
// Version history
// 05/30/12 jeffr : Original
//===========================================================================//
TC_MinGrid_c::TC_MinGrid_c( 
      void )
      :   
      grid_( 0.1 ),
      units_( 10 ),
      magnitude_( 10 ),
      precision_( 1 )
{
   this->SetGrid( TC_MIN_GRID_DEF );
}

//===========================================================================//
// Method         : ~TC_MinGrid_c
// Author         : Jeff Rudolph
//---------------------------------------------------------------------------//
// Version history
// 05/30/12 jeffr : Original
//===========================================================================//
TC_MinGrid_c::~TC_MinGrid_c( 
      void )
{
}

//===========================================================================//
// Method         : NewInstance
// Author         : Jeff Rudolph
//---------------------------------------------------------------------------//
// Version history
// 05/30/12 jeffr : Original
//===========================================================================//
void TC_MinGrid_c::NewInstance( 
      void )
{
   pinstance_ = new TC_NOTHROW TC_MinGrid_c;
}

//===========================================================================//
// Method         : DeleteInstance
// Author         : Jeff Rudolph
//---------------------------------------------------------------------------//
// Version history
// 05/30/12 jeffr : Original
//===========================================================================//
void TC_MinGrid_c::DeleteInstance( 
      void )
{
   if ( pinstance_ != NULL )
   {
      delete pinstance_;
      pinstance_ = NULL;
   }
}

//===========================================================================//
// Method         : GetInstance
// Author         : Jeff Rudolph
//---------------------------------------------------------------------------//
// Version history
// 05/30/12 jeffr : Original
//===========================================================================//
TC_MinGrid_c& TC_MinGrid_c::GetInstance( 
      void )
{
   if ( !pinstance_ )
   {
      NewInstance( );
   }
   return( *pinstance_ );
}

//===========================================================================//
// Method         : SetGrid
// Author         : Jeff Rudolph
//---------------------------------------------------------------------------//
// Version history
// 05/30/12 jeffr : Original
//===========================================================================//
void TC_MinGrid_c::SetGrid( 
      double grid )
{
   this->grid_ = grid;
   if ( this->grid_ > TC_FLT_EPSILON )
   {
      // Compute database units (integer) based on min grid value
      double units = ( 1.0 / this->grid_ ) + TC_FLT_EPSILON;
      this->units_ = static_cast< unsigned int >( units );

      // Compute format precision (integer) based on min grid value
      double precision = 10.0;
      while ( precision <= 1000000.0 + TC_FLT_EPSILON )
      {
         double f = this->grid_ * precision;
         double c = ceil( f - TC_FLT_EPSILON );
         if ( fabs( c - f ) < TC_FLT_EPSILON )
            break;

         precision *= 10.0;
      }
      precision = ceil( log10( precision ));
      this->precision_ = static_cast< unsigned int >( precision );

      // Compute database magnitude (integer) based on precision value
      this->magnitude_ = 1;
      for ( unsigned int n = 1; n <= this->precision_; ++n )  
      {
         this->magnitude_ *= 10;
      }
   }
}

//===========================================================================//
// Method         : IntToFloat
// Author         : Jeff Rudolph
//---------------------------------------------------------------------------//
// Version history
// 05/30/12 jeffr : Original
//===========================================================================//
double TC_MinGrid_c::IntToFloat( 
      int i ) const
{
   double f;
   TCT_UnitToFloat( i, &f, this->units_ );

   return( f );
}

//===========================================================================//
// Method         : UnsignedIntToFloat
// Author         : Jeff Rudolph
//---------------------------------------------------------------------------//
// Version history
// 05/30/12 jeffr : Original
//===========================================================================//
double TC_MinGrid_c::UnsignedIntToFloat( 
      unsigned int ui ) const
{
   double f;
   TCT_UnitToFloat( ui, &f, this->units_ );

   return( f );
}

//===========================================================================//
// Method         : LongIntToFloat
// Author         : Jeff Rudolph
//---------------------------------------------------------------------------//
// Version history
// 05/30/12 jeffr : Original
//===========================================================================//
double TC_MinGrid_c::LongIntToFloat( 
      long int li ) const
{
   double f;
   TCT_UnitToFloat( li, &f, this->units_ );

   return( f );
}

//===========================================================================//
// Method         : FloatToInt
// Author         : Jeff Rudolph
//---------------------------------------------------------------------------//
// Version history
// 05/30/12 jeffr : Original
//===========================================================================//
int TC_MinGrid_c::FloatToInt( 
      double f ) const
{
   int i;
   if ( f < static_cast< double >( INT_MIN ) - TC_FLT_EPSILON )
   {
      i = INT_MIN;
   } 
   else if ( f > static_cast< double >( INT_MAX ) - TC_FLT_EPSILON )
   {
      i = INT_MAX;
   } 
   else
   {
      TCT_FloatToUnit( f, &i, this->units_ );
   } 
   return( i );
}

//===========================================================================//
// Method         : FloatToUnsignedInt
// Author         : Jeff Rudolph
//---------------------------------------------------------------------------//
// Version history
// 05/30/12 jeffr : Original
//===========================================================================//
unsigned int TC_MinGrid_c::FloatToUnsignedInt( 
      double f ) const
{
   unsigned int ui;
   if ( f < static_cast< double >( 0 ) - TC_FLT_EPSILON )
   {
      ui = UINT_MAX;
   } 
   else if ( f > static_cast< double >( UINT_MAX ) - TC_FLT_EPSILON )
   {
      ui = UINT_MAX;
   } 
   else
   {
      TCT_FloatToUnit( f, &ui, this->units_ );
   } 
   return( ui );
}

//===========================================================================//
// Method         : FloatToLongInt
// Author         : Jeff Rudolph
//---------------------------------------------------------------------------//
// Version history
// 05/30/12 jeffr : Original
//===========================================================================//
long int TC_MinGrid_c::FloatToLongInt( 
      double f ) const
{
   long int li;
   if ( f < static_cast< double >( LONG_MIN ) - TC_FLT_EPSILON )
   {
      li = LONG_MIN;
   } 
   else if ( f > static_cast< double >( LONG_MAX ) - TC_FLT_EPSILON )
   {
      li = LONG_MAX;
   } 
   else
   {
      TCT_FloatToUnit( f, &li, this->units_ );
   } 
   return( li );
}

//===========================================================================//
// Method         : SnapToGrid
// Author         : Jeff Rudolph
//---------------------------------------------------------------------------//
// Version history
// 05/30/12 jeffr : Original
//===========================================================================//
double TC_MinGrid_c::SnapToGrid(
      double f ) const
{
   int i = this->FloatToInt( f );

   double fdelta = 0.0;
   if (( i == INT_MIN ) || ( i == INT_MAX ))
   {
      double fmin = static_cast< double >( INT_MIN );
      double fmax = static_cast< double >( INT_MAX );
      if ( TCTF_IsGT( f, fmin ) && TCTF_IsLT( f, fmax ))
      {
	 fdelta = floor( f );
         i = this->FloatToInt( f - fdelta );
      }
   }

   double fprime = 0.0;
   if ( i == INT_MIN )
   {
      fprime = static_cast< double >( INT_MIN );
   }
   else if ( i == INT_MAX )
   {
      fprime = static_cast< double >( INT_MAX );
   }
   else
   {
      fprime = this->IntToFloat( i ) + fdelta;
   }
   return( fprime );
}

//===========================================================================//
// Method         : SnapToGridCeil
// Author         : Jeff Rudolph
//---------------------------------------------------------------------------//
// Version history
// 05/30/12 jeffr : Original
//===========================================================================//
double TC_MinGrid_c::SnapToGridCeil(
      double f ) const
{
   int i = static_cast< int >( ceil( f / this->grid_ - TC_FLT_EPSILON ));

   return( static_cast< double >( i ) * this->grid_ );
}

//===========================================================================//
// Method         : IsOnGrid
// Author         : Jeff Rudolph
//---------------------------------------------------------------------------//
// Version history
// 05/30/12 jeffr : Original
//===========================================================================//
bool TC_MinGrid_c::IsOnGrid( 
      double  f, 
      double* pf ) const
{
   double fprime = this->SnapToGrid( f );

   if ( pf )
   {
      *pf = fprime;
   }
   return( fabs( f - fprime ) < TC_FLT_EPSILON ? true : false );
}