/*************************************************
* Initalization Function Source File             *
* (C) 1999-2003 The Botan Project                *
*************************************************/

#include <botan/init.h>
#include <botan/allocate.h>
#include <botan/rng.h>
#include <botan/mutex.h>
#include <botan/timers.h>
#include <botan/look_add.h>
#include <botan/randpool.h>
#include <botan/x917_rng.h>
#include <botan/util.h>
#include <botan/conf.h>
#include <map>

#if defined(BOTAN_EXT_MUTEX_PTHREAD)
  #include <botan/mux_pthr.h>
#elif defined(BOTAN_EXT_MUTEX_WIN32)
  #include <botan/mux_win.h>
#endif

#if defined(BOTAN_EXT_ALLOC_MMAP)
  #include <botan/mmap_mem.h>
#endif

#if defined(BOTAN_EXT_TIMER_HARDWARE)
  #include <botan/tm_hard.h>
#elif defined(BOTAN_EXT_TIMER_POSIX)
  #include <botan/tm_posix.h>
#elif defined(BOTAN_EXT_TIMER_UNIX)
  #include <botan/tm_unix.h>
#endif

namespace Botan {

/*************************************************
* Library Initalization                          *
*************************************************/
LibraryInitializer::LibraryInitializer(const std::string& arg_string)
   {
   Init::initialize(arg_string);
   }

/*************************************************
* Library Shutdown                               *
*************************************************/
LibraryInitializer::~LibraryInitializer()
   {
   Init::deinitialize();
   }

namespace {

/*************************************************
* Register a mutex type, if possible             *
*************************************************/
bool set_mutex(bool must_have_mutex)
   {
   if(!must_have_mutex) { set_mutex_type(0); return true; }

#if defined(BOTAN_EXT_MUTEX_PTHREAD)
   set_mutex_type(new Pthread_Mutex);
   return true;
#elif defined(BOTAN_EXT_MUTEX_WIN32)
   set_mutex_type(new Win32_Mutex);
   return true;
#else
   return false;
#endif
   }

/*************************************************
* Register a high resolution timer, if possible  *
*************************************************/
void set_timer()
   {
#if defined(BOTAN_EXT_TIMER_HARDWARE)
   set_timer_type(new Hardware_Timer);
#elif defined(BOTAN_EXT_TIMER_POSIX)
   set_timer_type(new POSIX_Timer);
#elif defined(BOTAN_EXT_TIMER_UNIX)
   set_timer_type(new Unix_Timer);
#endif
   }

/*************************************************
* Register a more secure allocator, if possible  *
*************************************************/
void set_safe_allocator()
   {
   set_default_allocator("locking");

#if defined(BOTAN_EXT_ALLOC_MMAP)
   add_allocator_type("mmap", new MemoryMapping_Allocator);
   set_default_allocator("mmap");
#endif
   }

/*************************************************
* Parse the options string                       *
*************************************************/
std::map<std::string, std::string> parse_args(const std::string& arg_string)
   {
   std::map<std::string, std::string> arg_map;
   std::vector<std::string> args = split_on(arg_string, ' ');
   for(u32bit j = 0; j != args.size(); j++)
      {
      if(args[j].find('=') == std::string::npos)
         arg_map[args[j]] = "";
      else
         {
         std::vector<std::string> name_and_value = split_on(args[j], '=');
         arg_map[name_and_value[0]] = name_and_value[1];
         }
      }

   return arg_map;
   }

/*************************************************
* Check if an option is set in the argument      *
*************************************************/
bool arg_set(const std::map<std::string, std::string>& args,
             const std::string& option)
   {
   return (args.find(option) != args.end());
   }

}

/*************************************************
* Internal startup/shutdown functions            *
*************************************************/
extern void initalize_memory_subsystem();
extern void shutdown_memory_subsystem();
extern void create_global_bigints();
extern void delete_global_bigints();
extern void set_default_options();

namespace Init {

/*************************************************
* Library Initalization                          *
*************************************************/
void initialize(const std::string& arg_string)
   {
   std::map<std::string, std::string> args = parse_args(arg_string);

   if(!set_mutex(arg_set(args, "thread_safe")))
      throw Exception("LibraryInitializer: thread safety impossible");

   set_default_options();
   initalize_memory_subsystem();
   create_global_bigints();
   init_lookup_tables();

   if(arg_set(args, "secure_memory"))
      set_safe_allocator();
   set_timer();

   load_algorithms_list();
   if(!arg_set(args, "no_aliases")) add_default_aliases();
   if(!arg_set(args, "no_oids"))    add_default_oids();
   if(arg_set(args, "config") && args["config"] != "")
      Config::load(args["config"]);

   setup_global_rng(new Randpool, new ANSI_X917_RNG);

   const u32bit min_entropy = Config::get_u32bit("rng/min_entropy");
   u32bit total_bits = 0;
   for(u32bit j = 0; j != 4; j++)
      {
      total_bits += Global_RNG::seed(true, min_entropy - total_bits);
      if(total_bits >= min_entropy)
         break;
      }
   if(total_bits < min_entropy)
      throw PRNG_Unseeded("Unable to collect sufficient entropy");
   }

/*************************************************
* Library Shutdown                               *
*************************************************/
void deinitialize()
   {
   destroy_global_rng();
   destroy_lookup_tables();
   delete_global_bigints();
   set_timer_type(0);
   shutdown_memory_subsystem();
   }

}

}
