/*
 * Copyright (C) 2019 Purism SPC
 * SPDX-License-Identifier: GPL-3.0+
 * Author: Guido Günther <agx@sigxcpu.org>
 */

#include "gthd.h"
#include "gthd-data.h"

typedef struct _GthdData {
  GObject parent;

  int nr_tzone;
  int nr_cdev;
  /* keep track of instance ids since there might be gaps */
  int tzone_max;
  int cdev_max;

  GPtrArray *cdevs;
  GPtrArray *tzones;
} GthdData;

G_DEFINE_TYPE (GthdData, gthd_data, G_TYPE_OBJECT);

static void
gthd_data_constructed (GObject *object)
{
  GthdData *self = GTHD_DATA (object);
  g_autoptr(GDir) dir = NULL;
  g_autoptr(GError) err = NULL;
  const gchar *filename;
  guint id;

  dir = g_dir_open(GTHD_THERMAL_SYSFS, 0, &err);
  if (!dir) {
    g_warning ("Failed to read %s: %s", GTHD_THERMAL_SYSFS, err->message);
    return;
  }

  while ((filename = g_dir_read_name(dir))) {
    if (g_str_has_prefix (filename, GTHD_COOLING_DEVICE_SYSFS)) {
      GthdCoolingDevice *cdev;
      g_autoptr(GError) err = NULL;

      cdev = gthd_cooling_device_new (filename, &err);
      if (!cdev) {
	g_warning ("Failed to get cooling device %s: %s", filename, err->message);
	continue;
      }
      id = gthd_cooling_device_get_id (cdev);
      self->cdev_max = id > self->cdev_max ? id : self->cdev_max;

      g_ptr_array_add (self->cdevs, cdev);
      self->nr_cdev++;
    } else if (g_str_has_prefix (filename, GTHD_THERMAL_ZONE_SYSFS)) {
      GthdThermalZone *tzone;
      g_autoptr(GError) err = NULL;

      tzone = gthd_thermal_zone_new (filename, &err);
      if (!tzone) {
	g_warning ("Failed to get thermal zone %s: %s", filename, err->message);
	continue;
      }
      id = gthd_thermal_zone_get_id (tzone);
      self->tzone_max = id > self->tzone_max ? id : self->tzone_max;

      g_ptr_array_add (self->tzones, tzone);
      self->nr_tzone++;
    }
  }
  g_debug ("Found %d thermal zones and %d cooling devices",
	   self->nr_tzone, self->nr_cdev);

  G_OBJECT_CLASS (gthd_data_parent_class)->constructed (object);
}


static void
gthd_data_dispose (GObject *object)
{
  GthdData *self = GTHD_DATA (object);

  if (self->cdevs) {
    g_ptr_array_free (self->cdevs, TRUE);
    self->cdevs = NULL;
  }

  if (self->tzones) {
    g_ptr_array_free (self->tzones, TRUE);
    self->tzones = NULL;
  }

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


static void
gthd_data_class_init (GthdDataClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->constructed = gthd_data_constructed;
  object_class->dispose = gthd_data_dispose;
}


static void
gthd_data_init (GthdData *self)
{
  self->cdevs = g_ptr_array_new ();
  self->tzones = g_ptr_array_new ();
}


GthdData*
gthd_data_get_default (void)
{
  static GthdData *instance;

  if (instance == NULL) {
    instance = g_object_new (GTHD_TYPE_DATA, NULL);
    g_object_add_weak_pointer (G_OBJECT (instance), (gpointer *)&instance);
  }
  return instance;
}


const GPtrArray *
gthd_data_get_cooling_devs (GthdData *self)
{
  g_return_val_if_fail (GTHD_IS_DATA (self), NULL);

  return self->cdevs;
}

GthdCoolingDevice *
gthd_data_get_cooling_device (GthdData *self, const char *name)
{
  g_return_val_if_fail (GTHD_IS_DATA (self), NULL);
  g_return_val_if_fail (name, NULL);

  for (int i = 0; i < self->cdevs->len; i++) {
    GthdCoolingDevice *cdev = g_ptr_array_index(self->cdevs, i);
    if (strcmp (gthd_cooling_device_get_name (cdev), name) == 0)
      return cdev;
  }
  return NULL;
}

const GPtrArray *
gthd_data_get_thermal_zones (GthdData *self)
{
  g_return_val_if_fail (GTHD_IS_DATA (self), NULL);

  return self->tzones;
}

GthdThermalZone *
gthd_data_get_thermal_zone (GthdData *self, const char *name)
{
  g_return_val_if_fail (GTHD_IS_DATA (self), NULL);
  g_return_val_if_fail (name, NULL);

  for (int i = 0; i < self->tzones->len; i++) {
    GthdThermalZone *tzone = g_ptr_array_index(self->tzones, i);
    if (strcmp (gthd_thermal_zone_get_name (tzone), name) == 0)
      return tzone;
  }
  return NULL;
}
