/*
 * Copyright (C) 2025 The Phosh.mobi e.V.
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 *
 * Author: Guido Günther <agx@sigxcpu.org>
 */

#define G_LOG_DOMAIN "cbd-modem-cell-broadcast"

#include "cbd-config.h"

#include "cbd-channel-manager.h"
#include "cbd-modem-cell-broadcast.h"

#include "gmobile.h"

#include <libmm-glib/libmm-glib.h>

/**
 * CbdModemCellBroadcast:
 *
 * Object tracking ModemManagers's Cell Broadcast interface
 */

enum {
  PROP_0,
  PROP_MODEM_CELL_BROADCAST,
  PROP_LAST_PROP
};
static GParamSpec *props[PROP_LAST_PROP];

struct _CbdModemCellBroadcast {
  GObject       parent;

  GCancellable *cancel;
  MMModemCellBroadcast *modem_cell_broadcast;
};
G_DEFINE_TYPE (CbdModemCellBroadcast, cbd_modem_cell_broadcast, G_TYPE_OBJECT)


static void
cbd_modem_cell_broadcast_set_modem (CbdModemCellBroadcast *self, MMModemCellBroadcast *modem)
{
  /* Construct only */
  g_assert (self->modem_cell_broadcast == NULL);

  self->modem_cell_broadcast = g_object_ref (modem);
}


static void
cbd_modem_cell_broadcast_set_property (GObject      *object,
                                       guint         property_id,
                                       const GValue *value,
                                       GParamSpec   *pspec)
{
  CbdModemCellBroadcast *self = CBD_MODEM_CELL_BROADCAST (object);

  switch (property_id) {
  case PROP_MODEM_CELL_BROADCAST:
    cbd_modem_cell_broadcast_set_modem (self, g_value_get_object (value));
    break;
  default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
    break;
  }
}


static void
cbd_modem_cell_broadcast_get_property (GObject    *object,
                                       guint       property_id,
                                       GValue     *value,
                                       GParamSpec *pspec)
{
  CbdModemCellBroadcast *self = CBD_MODEM_CELL_BROADCAST (object);

  switch (property_id) {
  case PROP_MODEM_CELL_BROADCAST:
    g_value_set_object (value, self->modem_cell_broadcast);
    break;
  default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
    break;
  }
}



static void
cbd_modem_cell_broadcast_dispose (GObject *object)
{
  CbdModemCellBroadcast *self = CBD_MODEM_CELL_BROADCAST (object);

  g_cancellable_cancel (self->cancel);
  g_clear_object (&self->cancel);

  g_signal_handlers_disconnect_by_data (self->modem_cell_broadcast, self);
  g_clear_object (&self->modem_cell_broadcast);

  G_OBJECT_CLASS (cbd_modem_cell_broadcast_parent_class)->dispose (object);
}


static void
cbd_modem_cell_broadcast_class_init (CbdModemCellBroadcastClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->get_property = cbd_modem_cell_broadcast_get_property;
  object_class->set_property = cbd_modem_cell_broadcast_set_property;
  object_class->dispose = cbd_modem_cell_broadcast_dispose;

  props[PROP_MODEM_CELL_BROADCAST] =
    g_param_spec_object ("modem-cell-broadcast", "", "",
                         MM_TYPE_MODEM_CELL_BROADCAST,
                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);

  g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
}


static void
cbd_modem_cell_broadcast_init (CbdModemCellBroadcast *self)
{
  self->cancel = g_cancellable_new ();
}


CbdModemCellBroadcast *
cbd_modem_cell_broadcast_new (MMModemCellBroadcast *modem)
{
  return g_object_new (CBD_TYPE_MODEM_CELL_BROADCAST, "modem-cell-broadcast", modem, NULL);
}


gboolean
cbd_modem_cell_broadcast_match (CbdModemCellBroadcast *self,
                                MMModemCellBroadcast  *modem_cell_broadcast)
{
  g_assert (CBD_IS_MODEM_CELL_BROADCAST (self));

  return self->modem_cell_broadcast == modem_cell_broadcast;
}


void
cbd_modem_cell_broadcast_set_channels (CbdModemCellBroadcast *self, GArray *channels)
{
#ifdef CBD_MM_HAS_SET_CHANNELS
  g_autoptr (GError) err = NULL;
  g_autoptr (GArray) mm_channels = g_array_new (FALSE, FALSE, sizeof (MMCellBroadcastChannels));
  gboolean success;

  g_assert (CBD_IS_MODEM_CELL_BROADCAST (self));
  g_assert (channels);

  for (int i = 0; i < channels->len; i++) {
    CbdChannelsRange range;
    MMCellBroadcastChannels mm_range;

    range = g_array_index (channels, CbdChannelsRange, i);

    mm_range.start = range.start;
    mm_range.end = range.end;
    g_array_append_val (mm_channels, mm_range);
  }

  success = mm_modem_cell_broadcast_set_channels_sync (self->modem_cell_broadcast,
                                                       (MMCellBroadcastChannels*)mm_channels->data,
                                                       mm_channels->len,
                                                       self->cancel,
                                                       &err);
  if (!success) {
    g_critical ("Failed to set channel list in '%s': %s",
                mm_modem_cell_broadcast_get_path (self->modem_cell_broadcast), err->message);
  }
#else
  g_warning_once ("Cellbroadcast built without support for setting channels, "
                  "will use modem defaults");
#endif
}
