// =============================================================================
//! \file
//! \brief Global functions and startup entry point
//!
//! Copyright (c) 2012-2015 by the developers. See the LICENSE file for details.


// =============================================================================
// Include headers

// C++98
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <sstream>

// Local
extern "C"
{
#include "bdate.h"
#include "conf.h"
#include "config.h"
#include "core.h"
#include "digest.h"
#include "database.h"
#include "filter.h"
#include "group.h"
#include "hmac.h"
#include "log.h"
#include "nls.h"
#include "sighandler.h"
#include "unicode.h"
}
#include "compression.hxx"
#include "main.hxx"
#include "tls.hxx"
#include "ui.hxx"


// =============================================================================
//! \addtogroup MAIN
// @{


// =============================================================================
//! Print program version information

static void  print_version(void)
{
   std::cout << CFG_NAME << " " << CFG_VERSION << " for " << CFG_OS << "\n"
#if defined(CFG_MODIFIED) && CFG_MODIFIED != 0
             << "(This is a modified version!)" << "\n"
#endif  // CFG_MODIFIED
             << "Unicode version: " << UC_VERSION << "\n"
             << "Compiled with options:"
#if CFG_USE_CLB
             << " CLB"
#endif  // CFG_USE_CLB
#if CFG_USE_FSC
             << " FSC"
#endif  // CFG_USE_FSC
#if CFG_USE_IP6
             << " IP6"
#endif  // CFG_USE_IP6
#if CFG_USE_XSI && !CFG_NLS_DISABLE
             << " NLS"
#endif  // CFG_USE_XSI && !CFG_NLS_DISABLE
#if  CFG_USE_POSIX_API_200112
             << " P2K"
#endif  // CFG_USE_POSIX_API_200112
#if CFG_USE_TLS
             << " TLS"
#endif  // CFG_USE_TLS
#if !CFG_DB_DISABLE
             << " XDBE"
#endif  // CFG_DB_DISABLE
#if CFG_USE_XSI
             << " XSI"
#endif  // CFG_USE_XSI
#if CFG_USE_ZLIB
             << " ZLIB"
#endif  // CFG_USE_ZLIB
             << "\n"
             << "Build: " << BDATE << std::endl;
}


// =============================================================================
// Print help

static void  print_help(void)
{
   std::cout << "Usage: " << CFG_NAME << " [options]\n"
             << "\n"
             << "Options:\n\n"
             << "   -display [host]:display  X window system display\n"
             << "   -geometry WxH[+X+Y]      Window size and position\n"
             << "   -iconic                  Starts with minimized window\n"
             << "   -notooltips              Disable tooltip messages\n"
             << "   -h                       Print help message "
             <<                             "to standard output and exit\n"
             << "   -v                       Print version information "
             <<                             "to standard output and exit\n"
             << "\n"
             << "The GUI style can be customized with the environment "
             << "variable FLTK_SCHEME.\n"
             << "(See manual page for details)"
             << std::endl;
}


// =============================================================================
// Save configuration and exit

static void  save_exit(int  ret)
{
   if(EXIT_SUCCESS == ret)
   {
      // Save configuration
      conf_store(config);
   }
   // Release memory allocated for configuration data
   conf_delete(config);
   // Regular exit
   std::exit(ret);
}


// =============================================================================
// Loop for normal execution

static void  loop(void)
{
   char  sbuf[] = "Terminate due to signal";

   while(1)
   {
      // Drive UI
      if(ui_exec())  { break; }

      // Check whether signal to exit is pending
      if(sighandler_check_abort())
      {
         // Yes
         std::clog << std::endl;
         print_error(sbuf);
         break;
      }
   }
}


// =============================================================================
//! \brief Lock UI thread
//!
//! Exported as C style function
//!
//! \note
//! It is save to call this function for all threads, including the UI thread
//! itself.
//!
//! \attention
//! It is not allowed for a thread to call this function multiple times without
//! unlock between them!
//!
//! \return
//! - 0 on success
//! - Negative value on error

int  ts_lock_ui(void)
{
   int  res = 0;

   // Execute as NOP for UI thread itself
   if(!core_check_thread_ui())
   {
      res = ui_lock();
   }

   return(res);
}


// =============================================================================
//! \brief Unlock UI thread
//!
//! Exported as C style function
//!
//! \note
//! It is save to call this function for all threads, including the UI thread
//! itself.
//!
//! \return
//! - 0 on success
//! - Negative value on error

int  ts_unlock_ui(void)
{
   int  res = 0;

   // Execute as NOP for UI thread itself
   if(!core_check_thread_ui())
   {
      res = ui_unlock();
   }

   return(res);
}


// =============================================================================
//! \brief Print error message
//!
//! Exported as C style function
//!
//! \param[in] msg  String (Error message)

void  print_error(const char*  msg)
{
   std::clog << CFG_NAME << ": " << msg << std::endl << std::flush;
}


// =============================================================================
//! \brief Program entry point
//!
//! \param[in] argc  Number of command line arguments
//! \param[in] argv  Array containing command line argument strings
//!
//! Parse command line and control startup and shutdown
//!
//! \return
//! - \c EXIT_SUCCESS on success
//! - \c EXIT_FAILURE on error

int  main(int  argc, char**  argv)
{
   int  rv;
   enum { OK, ERROR, ABORT }  rv2 = OK;
   char  option = 0;               // Flag indicating option value will follow
   unsigned int  i;


   // Install signal handlers
   // Note:
   // This is done in a separate C module because POSIX require a C compiler
   rv = sighandler_install();
   if(0 > rv)  rv2 = ERROR;

   // Delete old logfile
   if(OK == rv2)  { log_delete_logfile(); }

   // Load configuration
   if(OK == rv2)
   {
      conf_load(config);
   }

   // Process parameters
   if (OK == rv2)
   {
      for(i = 1; i < (unsigned int) argc; i++)
      {
         if(!std::strcmp(argv[i], "-v") || !std::strcmp(argv[i], "--version"))
         {
            // Print version information and report success
            print_version();
            rv2 = ABORT;
            break;
         }
         else if(!std::strcmp(argv[i], "-h") || !std::strcmp(argv[i], "--help"))
         {
            // Print help and report success
            print_help();
            rv2 = ABORT;
            break;
         }
         else if(!std::strcmp(argv[i], "-display")
                 || !std::strcmp(argv[i], "-geometry")
                 || !std::strcmp(argv[i], "-iconic")
                 || !std::strcmp(argv[i], "-notooltips"))
         {
            // Handled by FLTK
            option = 1;
         }
         else if(option)
         {
            // Accept option value
            option = 0;
         }
         else if('-' == argv[i][0])
         {
            // Unknown option
            print_error("Unknown option");
            rv2 = ERROR;
            break;
         }
      }
      if(ERROR == rv2)
      {
         print_error("Use '-h' or read the man page for help");
      }
   }

   // Check what to do
   switch(rv2)
   {
      case OK:
      {
         // Normal startup into user interface
         // Attention:
         // All modules that spawn additional threads must be initialized by the
         // UI, not here!
         nls_init();  // Don't abort on error, the NLS module will always work
         if(0 > filter_init(ui_get_locale_utf8()))  // Do this after NLS init
         {
            rv = EXIT_FAILURE;
            break;
         }
#if !CFG_CMPR_DISABLE
         if(0 > cmpr_init())
         {
            rv = EXIT_FAILURE;
            break;
         }
#endif  // !CFG_CMPR_DISABLE
#if CFG_USE_TLS
         if(0 > tls_init())
         {
            rv = EXIT_FAILURE;
            break;
         }
#endif  // CFG_USE_TLS
         digest_init();
         hmac_init();
         group_init();
         if(0 > db_init())
         {
            rv = EXIT_FAILURE;
            break;
         }
         // Core init is handled by UI
         ui_init(argc, argv);
         loop();
         // Core shutdown is handled by UI
         ui_exit();
         db_exit();
         group_exit();
         hmac_exit();
         digest_exit();
#if CFG_USE_TLS
         tls_exit();
#endif  // CFG_USE_TLS
#if !CFG_CMPR_DISABLE
         cmpr_exit();
#endif  // !CFG_CMPR_DISABLE
         filter_exit();
         nls_exit();
         rv = EXIT_SUCCESS;
         break;
      }
      case ABORT:
      {
         // Nothing more to do, but no error
         rv = EXIT_SUCCESS;
         break;
      }
      default:
      {
         // Error
         rv = EXIT_FAILURE;
         break;
      }
   }

   save_exit(rv);
   // We should never run up to here
   print_error("Fatal error: Hit end of main()");
}


// @}

// EOF
