/* Copyright (C) 2003 The gtkmm Development Team
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include <glibmm/vectorutils.h>

#include <gtk/gtk.h>
#include <gtkmm/window.h>
#include <glibmm/init.h>
#include <giomm/init.h>
#include <pangomm/wrap_init.h>
#include <gdkmm/wrap_init.h>
#include <gtkmm/wrap_init.h>
#include <locale>
#include <clocale>
#include <stdexcept>
#ifndef GTKMM_DISABLE_DEPRECATED
// For the wrap_new() functions in init_gtkmm_internals()
#include <gtkmm/searchentry.h>
#include <gtkmm/searchentry2.h>
#include <gtkmm/private/searchentry_p.h>
#include <gtkmm/private/searchentry2_p.h>
#endif // GTKMM_DISABLE_DEPRECATED

namespace
{
static bool will_use_search_entry2 = false;

static void init_gtkmm_internals()
{
  static bool init_done = false;

  if(!init_done)
  {
    Glib::init();
    Gio::init();

    // Populate the map of GTypes to C++ wrap_new() functions.
    Pango::wrap_init();
    Gdk::wrap_init();
    Gtk::wrap_init();

    // Shall gtk_init() set the global C locale to the user's preferred locale?
    if (!Glib::get_init_to_users_preferred_locale())
      gtk_disable_setlocale();

#ifndef GTKMM_DISABLE_DEPRECATED
    // If gtkmm is built without deprecated API, there is no Gtk::SearchEntry.
    // The wrap_new() function registered in wrap_init.cc will be used.
    if (will_use_search_entry2)
      Glib::wrap_register(gtk_search_entry_get_type(), &Gtk::SearchEntry2_Class::wrap_new);
    else
      Glib::wrap_register(gtk_search_entry_get_type(), &Gtk::SearchEntry_Class::wrap_new);
#endif // GTKMM_DISABLE_DEPRECATED

    init_done = true;
  }
}

// Make sure the C++ locale is the same as the C locale.
// The first call to Glib::init() fixes this. We repeat it here just to be sure.
// The application program may have changed the locale settings after
// the first call to Glib::init(). Unlikely, but possible.
static void set_cxx_locale_to_c_locale()
{
  try
  {
    // Make the C++ locale equal to the C locale.
    std::locale::global(std::locale(std::setlocale(LC_ALL, nullptr)));
  }
  catch (const std::runtime_error& ex)
  {
    g_warning("Can't make the global C++ locale equal to the C locale.\n"
      "   %s\n   C locale = %s\n", ex.what(), std::setlocale(LC_ALL, nullptr));
  }
}

} // anonymous namespace

namespace Gtk
{

//static
void Application::wrap_in_search_entry2(bool search_entry2)
{
  will_use_search_entry2 = search_entry2;
}

Application::Application(const Glib::ConstructParams& construct_params)
:
  Gio::Application(construct_params)
{
  init_gtkmm_internals();
}

Application::Application(GtkApplication* castitem)
:
  Gio::Application((GApplication*)(castitem))
{
  init_gtkmm_internals();
}

const Glib::Class& Application::custom_class_init()
{
  init_gtkmm_internals();
  return application_class_.init();
}

Application::Application(const Glib::ustring& application_id, Gio::Application::Flags flags)
:
  // Mark this class as non-derived to allow C++ vfuncs to be skipped.
  // GApplication complains about "" but allows nullptr, so we avoid passing "".
  Glib::ObjectBase(nullptr),
  Gio::Application(Glib::ConstructParams(custom_class_init(),
    "application_id", Glib::c_str_or_nullptr(application_id),
    "flags", static_cast<GApplicationFlags>(flags), nullptr))
{
  // gtk_init() is called by the 'startup' default signal handler when g_application_run() is called.
  // It's also called here, to make it possible for users of gtkmm to create
  // a window and other widgets before calling run(). (This is not recommended.)
  // See https://bugzilla.gnome.org/show_bug.cgi?id=639925
  gtk_init();
  set_cxx_locale_to_c_locale();
}

Glib::RefPtr<Application> Application::create(const Glib::ustring& application_id, Gio::Application::Flags flags)
{
  return Glib::RefPtr<Application>( new Application(application_id, flags) );
}

int Application::run(int argc, char** argv)
{
  return Gio::Application::run(argc, argv);
}

int Application::run()
{
  return Gio::Application::run(0, nullptr);
}

void Application::set_accel_for_action(const Glib::ustring& detailed_action_name, const Glib::ustring& accel)
{
  std::vector<Glib::ustring> vec;
  vec.push_back(accel);
  set_accels_for_action(detailed_action_name, vec);
}

void Application::unset_accels_for_action(const Glib::ustring& detailed_action_name)
{
  //gtk_application_set_accels_for_action() can take an empty array, but not NULL.
  //See https://bugzilla.gnome.org/show_bug.cgi?id=708905#c10
  std::vector<Glib::ustring> vec;
  set_accels_for_action(detailed_action_name, vec);
}

} // namespace Gtk
