/******************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Initialization.
 *//*
 * LEGAL:   COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 ******************************************************************************/

#define BASE_MODULE_CC 1
#include "base/module.hh"
#include "base/misc.hh"
#include "base/file.hh"
#include "base/conf.hh"
#include "base/conf_bin.hh"
#include "base/random.hh"
#include "base/globals.hh"  // to define globals
#include "base/stream.hh"
using namespace base;
#include "math/module.hh"
#include "math/funcs.hh"
using namespace math;

namespace base {

INTERN void SelfCheck( void );
/*INTERN*/ void SelfCheck_IfNan( volatile fp* zero );

#if ! PROFILE
INTERN void EXIT0( void )
{
    EXIT(0);
}
#endif

/*****************************************************************************
 * Initialize module.
 *****************************************************************************/
void
Init( const InitArgs& initArgs )
{

BEGIN_ONCE {

    // Show number base in output.
    COUT.setf( std::ios::showbase );
    CERROR.setf( std::ios::showbase );
    CLOG.setf( std::ios::showbase );
    CDEBUG.setf( std::ios::showbase );

    // Read text .conf file.
    const string confTxtFilename = GetConfTxtFilename( initArgs.mProgramName );
  //BASE_CONF.Init( initArgs.mArgc, initArgs.mArgv, initArgs.mProgramName, confTxtFilename );
  // Directly since GetConf() checks mValid which isn't true yet.
    base::global::gConf.Init( initArgs, confTxtFilename );

#if ! PROFILE
    // atexit() breaks ability to generate gmon.out for gprof
    atexit( EXIT0 );
#endif

    SelfCheck();

    CheckObsoleteConfFilename( initArgs.mProgramName );  // legacy

    // Seed the random number generator.
    Random::randomize( BASE_CONF.mRandomSeed );

} END_ONCE

}

/*****************************************************************************
 * Start module.
 *****************************************************************************/
void
Start( void )
{
    // NOP.
}

/*****************************************************************************
 * Exit to MSDOS.
 *****************************************************************************/
void
EXIT( int exitCode )
{
#if 1
    // Advent of threading caused the evident problem of threads
    // still running while the main thread executes C++ destructors .
    // Call _exit() to forcibly exit.
#if PROFILE
    exit( exitCode );  // to produce gmon.out
#endif
    _exit( exitCode );  // notice '_'
#else
    // Tolerate being called repeatedly.
    BEGIN_ONCE
    {
        // Exit flag affects many destructors.
        // Dtors that ordinarily delete or access objects become NOPs
        // to prevent accessing destroyed objects.
        gGlobals.mExiting = true;
    } END_ONCE
    exit( exitCode );  // no '_'
#endif
}

/*****************************************************************************
 * Ensure assumptions of this code hold on this platform.
 * A serious failure causes program to exit.
 *****************************************************************************/
INTERN void
SelfCheck( void )
{
    if ( sizeof(int8)   != 1
      or sizeof(uint8)  != 1
      or sizeof(int16)  != 2
      or sizeof(uint16) != 2
      or sizeof(int32)  != 4
      or sizeof(uint32) != 4
      or sizeof(int64)  != 8
      or sizeof(uint64) != 8 )
    {
        CERROR << "ERROR: exact-size int types compiled incorrectly" << std::endl;
        EXIT( 1 );
    }

    if ( sizeof(void*) != sizeof(long) )
    {
        CERROR << "ERROR: sizeof(void*) != sizeof(long)" << std::endl;
        EXIT( 1 );
    }

    // Check 64-bit int64/uint64.  Note double 'L' is critical.
    if ( int64(1LL       <<      63LL ) !=  int64(0x8000000000000000LL)
      or (1ULL           <<      63ULL) !=        0x8000000000000000ULL
      or int64( int64(1) <<  int64(63)) !=  int64(0x8000000000000000LL)
      or (uint64(1)      << uint64(63)) != uint64(0x8000000000000000ULL)
       )
    {
        CERROR << "ERROR: int64/uint64" << std::endl;
        EXIT( 1 );
    }

    if ( not (sizeof(fpx) > sizeof(fp)) )
    {
        CERROR << "WARNING: fpx type isn't more precise than fp type" << std::endl;
        // can continue
    }

#ifdef CLEAR_BIT_FIELDS  // [2008/08] this func macro was deprecated
    // Test CLEAR_BIT_FIELDS()'s assumption that a bitfield is equivalent to unsigned int.
    struct Bitfield
    {
        unsigned int b0 : 1;
        unsigned int b1 : 4;
        unsigned int b2 : 16;
    };
    if ( sizeof(Bitfield) != sizeof(uint) )
    {
        CERROR << "ERROR: sizeof(bitfield) != sizeof(unsigned int)" << std::endl;
        EXIT( 1 );
    }
#endif

    // Test IfNan().
    // This tests IfNan() in RELEASE with optimizations enabled.
    volatile fp zero = 0.0f;
    SelfCheck_IfNan( &zero );  // pass zero to circumvent compiler warning of divide-by-zero
}

/*INTERN*/ void  // INTERN might allow compiler to optimize the above function call
SelfCheck_IfNan( volatile fp* zero )
{
    if ( not IfNan( (*zero) / (*zero) )  // really infinity
      or not IfNan( acos(999.0f) )       // acos(n) returns NaN if n > 1.0
      or IfNan( 0.0f ) )
    {
        CERROR << "ERROR: IfNan() failed" << std::endl;
        EXIT( 1 );
    }
}

} // namespace base
