// SPDX-FileCopyrightText: 2022-2024 Geoffrey D. Bennett <g@b4.vu>
// SPDX-License-Identifier: GPL-3.0-or-later

#include "about.h"
#include "file.h"
#include "menu.h"
#include "window-hardware.h"

// helper for common code of activate_*() functions
static void update_visibility(
  GSimpleAction *action,
  GtkWidget     *widget
) {
  GVariant *state = g_action_get_state(G_ACTION(action));
  gboolean new_state = !g_variant_get_boolean(state);

  g_action_change_state(G_ACTION(action), g_variant_new_boolean(new_state));
  gtk_widget_set_visible(widget, new_state);
}

static void activate_hardware(
  GSimpleAction *action,
  GVariant      *parameter,
  gpointer       data
) {
  (void) data;
  update_visibility(action, window_hardware);
}

static void activate_quit(
  GSimpleAction *action,
  GVariant      *parameter,
  gpointer       data
) {
  g_application_quit(G_APPLICATION(data));
}

static void activate_routing(
  GSimpleAction *action,
  GVariant      *parameter,
  gpointer       data
) {
  struct alsa_card *card = data;

  update_visibility(action, card->window_routing);
}

static void activate_mixer(
  GSimpleAction *action,
  GVariant      *parameter,
  gpointer       data
) {
  struct alsa_card *card = data;

  update_visibility(action, card->window_mixer);
}

static void activate_levels(
  GSimpleAction *action,
  GVariant      *parameter,
  gpointer       data
) {
  struct alsa_card *card = data;

  update_visibility(action, card->window_levels);
}

static void activate_startup(
  GSimpleAction *action,
  GVariant      *parameter,
  gpointer       data
) {
  struct alsa_card *card = data;

  update_visibility(action, card->window_startup);
}

static const GActionEntry app_entries[] = {
  {"hardware", activate_hardware, NULL, "false"},
  {"quit",     activate_quit},
};

struct menu_item {
  const char *label;
  const char *action_name;
  const char *accelerators[2];
};

struct menu_data {
  const char       *label;
  struct menu_item *items;
};

static const struct menu_data menus[] = {
  {
    "_File",
    (struct menu_item[]){
      { "_Load Configuration",   "win.load", { "<Control>O", NULL } },
      { "_Save Configuration",   "win.save", { "<Control>S", NULL } },
      { "_Interface Simulation", "win.sim",  { "<Control>I", NULL } },
      { "E_xit",                 "app.quit", { "<Control>Q", NULL } },
      {}
    }
  },
  {
    "_View",
    (struct menu_item[]){
      { "_Routing", "win.routing", { "<Control>R", NULL } },
      { "_Mixer",   "win.mixer",   { "<Control>M", NULL } },
      { "_Levels",  "win.levels",  { "<Control>L", NULL } },
      { "_Startup", "win.startup", { "<Control>T", NULL } },
      {}
    }
  },
  {
    "_Help",
    (struct menu_item[]){
      { "_Supported Hardware", "app.hardware", { "<Control>H",     NULL } },
      { "_About",              "win.about",    { "<Control>slash", NULL } },
      {}
    }
  },
  {}
};

static void populate_submenu(
  GtkApplication         *app,
  GMenu                  *menu,
  const struct menu_data *data
) {
  GMenu *submenu = g_menu_new();
  g_menu_append_submenu(menu, data->label, G_MENU_MODEL(submenu));

  // An empty-initialised menu_item marks the end
  for (struct menu_item *item = data->items; item->label; item++) {
    g_menu_append(submenu, item->label, item->action_name);
    gtk_application_set_accels_for_action(
      app, item->action_name, item->accelerators
    );
  }
}

GMenu *create_app_menu(GtkApplication *app) {
  g_action_map_add_action_entries(
    G_ACTION_MAP(app), app_entries, G_N_ELEMENTS(app_entries), app
  );

  GMenu *menu = g_menu_new();

  for (const struct menu_data *menu_data = menus;
       menu_data->label;
       menu_data++)
    populate_submenu(app, menu, menu_data);

  return menu;
}

static const GActionEntry win_entries[] = {
  {"about", activate_about},
  {"sim",   activate_sim}
};

void add_window_action_map(GtkWindow *w) {
  g_action_map_add_action_entries(
    G_ACTION_MAP(w), win_entries, G_N_ELEMENTS(win_entries), w
  );
}

static const GActionEntry load_save_entries[] = {
  {"load", activate_load},
  {"save", activate_save}
};

void add_load_save_action_map(struct alsa_card *card) {
  g_action_map_add_action_entries(
    G_ACTION_MAP(card->window_main),
    load_save_entries,
    G_N_ELEMENTS(load_save_entries),
    card
  );
}

static const GActionEntry startup_entry[] = {
  {"startup", activate_startup, NULL, "false"}
};

void add_startup_action_map(struct alsa_card *card) {
  g_action_map_add_action_entries(
    G_ACTION_MAP(card->window_main),
    startup_entry,
    G_N_ELEMENTS(startup_entry),
    card
  );
}

static const GActionEntry mixer_entries[] = {
  {"routing", activate_routing, NULL, "false"},
  {"mixer",   activate_mixer,   NULL, "false"}
};

static const GActionEntry levels_entries[] = {
  {"levels",  activate_levels,  NULL, "false"}
};

void add_mixer_action_map(struct alsa_card *card) {
  g_action_map_add_action_entries(
    G_ACTION_MAP(card->window_main),
    mixer_entries,
    G_N_ELEMENTS(mixer_entries),
    card
  );

  // Hide the levels menu item if there is no "Firmware Version"
  // control (working kernel support for level meters was added in the
  // same version as the "Firmware Version" control)
  if (get_elem_by_name(card->elems, "Firmware Version")) {
    g_action_map_add_action_entries(
      G_ACTION_MAP(card->window_main),
      levels_entries,
      G_N_ELEMENTS(levels_entries),
      card
    );
  }
}
