//  BMP
//  Copyright (C) 2005-2007 BMP development.
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
//  --
//
//  The BMPx project hereby grants permission for non GPL-compatible GStreamer
//  plugins to be used and distributed together with GStreamer and BMPx. This
//  permission is above and beyond the permissions granted by the GPL license
//  BMPx is covered by.

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <cstdlib>

#include <glib/gi18n.h>
#include <glibmm/main.h>

#include <unistd.h>
#include <sys/types.h>
#include <signal.h>

#include "signals.hh"

#include "main.hh"
#include "core.hh"

#define SIGNAL_CHECK_INTERVAL 1000

namespace Bmp
{

  namespace Signals
  {
    namespace
    {
      typedef void (*SignalHandler) (int);


      bool terminate = false;

      SignalHandler
      install_handler_full (int           signal_number,
                            SignalHandler handler,
                            int*          signals_to_block,
                            std::size_t   n_signals)
      {
          struct sigaction action, old_action;

          action.sa_handler = handler;
          action.sa_flags = SA_RESTART;

          sigemptyset (&action.sa_mask);

          for (std::size_t i = 0; i < n_signals; i++)
              sigaddset (&action.sa_mask, signals_to_block[i]);

          if (sigaction (signal_number, &action, &old_action) == -1)
          {
              debug("signals","%s: Failed to install handler for signal %d", G_STRLOC, signal_number);
              return 0;
          }

          return old_action.sa_handler;
      }

      // A version of signal() that works more reliably across different
      // platforms. It:
      // a. restarts interrupted system calls
      // b. does not reset the handler
      // c. blocks the same signal within the handler
      //
      // (adapted from Unix Network Programming Vol. 1)

      static SignalHandler
      install_handler (int           signal_number,
                       SignalHandler handler)
      {
          return install_handler_full (signal_number, handler, NULL, 0);
      }

      void
      empty_handler (int signal_number)
      {
          // empty
      }

      void
      sigsegv_handler (int signal_number)
      {
          g_printerr (_("BMPx has crashed. If you suspect a bug, please consult "
                        "http://bmpx.backtrace.info/site/Reporting_bugs\n"));

#ifdef HANDLE_SIGSEGV
          // TODO: backup session, config and playlist

          std::exit (EXIT_FAILURE);
#else
          std::abort ();
#endif
      }

      void
      sigterm_handler (int signal_number)
      {
          terminate = TRUE;
      }

      bool
      process_signals ()
      {
          if (terminate)
          {
              debug("signals",_("%s: SIGTERM received, quitting."), G_STRLOC);

              if (in_startup)
              {
                  std::exit (EXIT_SUCCESS);
              }
              else
              {
                  Core::Obj()->stop_no_questions_asked ();
              }

              return false;
          }

          return true;
      }

    } // anonymous namespace

    void
    handlers_init ()
    {
        install_handler (SIGPIPE, empty_handler);
        install_handler (SIGSEGV, sigsegv_handler);
        install_handler (SIGINT, sigterm_handler);
        install_handler (SIGTERM, sigterm_handler);

        Glib::signal_timeout ().connect (sigc::ptr_fun (&process_signals), SIGNAL_CHECK_INTERVAL);
    }

  } // Signals

} // Bmp
