/* Copyright 2022-2026 Kurt Nienhaus
 *
 * This file is part of VgaGames.
 * VgaGames 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.
 * VgaGames 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 VgaGames.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include "object.h"

void init_object_call(void);

static VG_BOOL object_call_run(void *);
static void object_call_draw(void *);
static int object_call_data(void *, unsigned int, void *);
static void object_call_signal(void *, unsigned int, int);
static void object_call_signal_objid(void *, const char *, int, int);
static void object_call_mgmt_run(void *);
static int object_call_mgmt_data(void *, const char *, void *);


/* set functions */
void
init_object_call(void)
{
  vg4->object->call_run = object_call_run;
  vg4->object->call_draw = object_call_draw;
  vg4->object->call_data = object_call_data;
  vg4->object->call_signal = object_call_signal;
  vg4->object->call_signal_objid = object_call_signal_objid;
  vg4->object->call_mgmt_run = object_call_mgmt_run;
  vg4->object->call_mgmt_data = object_call_mgmt_data;
} /* Ende init_object_call */


/* object_call_run:
 * call object-function f_run() for all object-instances
 * @param vgame  private structure of the game, or NULL
 * @return  VG_TRUE = OK,
 *          VG_FALSE = at least one f_run()-function returned VG_FALSE
 */
static VG_BOOL
object_call_run(void *vgame)
{
  unsigned int *idlist;
  int ianz, ipos;
  struct VG_Object *objp;
  VG_BOOL retw = VG_TRUE;

  ianz = vg4->object->list(NULL, 0, &idlist);
  for (ipos = 0; ipos < ianz; ipos++) {
    objp = vg4->object->instance_getobj(idlist[ipos]);
    if (objp != NULL) {
      if (objp->f_run != NULL) {
        if (!objp->f_run(vgame, objp)) { retw = VG_FALSE; }
      }
    }
  }
  if (idlist != NULL) { free(idlist); }

  return retw;
} /* Ende object_call_run */


/* object_call_draw:
 * call object-function f_draw() for all object-instances
 * @param vgame  private structure of the game, or NULL
 */
static void
object_call_draw(void *vgame)
{
  unsigned int *idlist;
  int ianz, ipos;
  struct VG_Object *objp;

  ianz = vg4->object->list(NULL, 0, &idlist);
  for (ipos = 0; ipos < ianz; ipos++) {
    objp = vg4->object->instance_getobj(idlist[ipos]);
    if (objp != NULL) {
      if (objp->f_draw != NULL) { objp->f_draw(vgame, objp); }
    }
  }
  if (idlist != NULL) { free(idlist); }
} /* Ende object_call_draw */


/* object_call_data:
 * call object-function f_data() for an object-instance
 * @param vgame       private structure of the game, or NULL
 * @param instanceid  instance-ID
 * @param vptr        arbitrary parameter
 * @return  return value of f_data() or -255 = could not call f_data()
 */
static int
object_call_data(void *vgame, unsigned int instanceid, void *vptr)
{
  struct VG_Object *objp;

  objp = vg4->object->instance_getobj(instanceid);
  if (objp == NULL) { pwarn("object->call_data: Instance-ID %u not found\n", instanceid); return -255; }
  if (objp->f_data == NULL) { pwarn("No f_data() for instance-ID %u (%s)\n", instanceid, objp->objid); return -255; }

  return objp->f_data(vgame, objp, vptr);
} /* Ende object_call_data */


/* object_call_signal:
 * call object-function f_signal() for an object-instance
 * @param vgame       private structure of the game, or NULL
 * @param instanceid  instance-ID
 * @param signo       arbitray integer interpreted as signal-number
 */
static void
object_call_signal(void *vgame, unsigned int instanceid, int signo)
{
  struct VG_Object *objp;

  objp = vg4->object->instance_getobj(instanceid);
  if (objp == NULL) { pwarn("object->call_signal: Instance-ID %u not found\n", instanceid); return; }
  if (objp->f_signal == NULL) { return; }

  objp->f_signal(vgame, objp, signo);
} /* Ende object_call_signal */


/* object_call_signal_objid:
 * call object-function f_signal() for all instances of an object
 * @param vgame  private structure of the game, or NULL
 * @param objid  object-ID
 * @param subid  sub-ID for object-ID, or 0 = no sub-ID
 * @param signo  arbitray integer interpreted as signal-number
 */
static void
object_call_signal_objid(void *vgame, const char *objid, int subid, int signo)
{
  int numberof, ipos;
  unsigned int *idlist;

  if (objid == NULL || *objid == '\0') { objid = VGI_OBJID_NONE; }

  numberof = vg4->object->list(objid, subid, &idlist);

  for (ipos = 0; ipos < numberof; ipos++) {
    vg4->object->call_signal(vgame, idlist[ipos], signo);
  }

  if (idlist != NULL) { free(idlist); }
} /* Ende object_call_signal_objid */


/* object_call_mgmt_run:
 * call object-management-function f_run() for all object-managements
 * @param vgame  private structure of the game, or NULL
 */
static void
object_call_mgmt_run(void *vgame)
{
  struct SML3_hash *hptr;
  struct SML3_hashelem *he1;
  struct VG_ObjMgmt *omgmt;

  hptr = vg4data.lists.object.hmgmt;

  for (he1 = SML3_hashlist(hptr, NULL, 0); he1 != NULL; he1 = SML3_hashlist(hptr, he1, 0)) {
    omgmt = (struct VG_ObjMgmt *)SML3_hashelem_valget(he1, NULL);
    if (omgmt->f_run != NULL) { omgmt->f_run(vgame); }
  }
} /* Ende object_call_mgmt_run */


/* object_call_mgmt_data:
 * call object-function f_data() for an object-management
 * @param vgame  private structure of the game, or NULL
 * @param objid  object-ID
 * @param vptr   arbitrary parameter
 * @return  return value of f_data() or -255 = could not call f_data()
 */
static int
object_call_mgmt_data(void *vgame, const char *objid, void *vptr)
{
  struct SML3_hash *hptr;
  struct SML3_hashelem *he1;
  struct VG_ObjMgmt *omgmt;

  if (objid == NULL || *objid == '\0') { objid = VGI_OBJID_NONE; }

  hptr = vg4data.lists.object.hmgmt;

  he1 = SML3_hashget(hptr, objid, strlen(objid) + 1);
  if (he1 == NULL) { pwarn("Object-management %s not found\n", objid); return -255; }
  omgmt = (struct VG_ObjMgmt *)SML3_hashelem_valget(he1, NULL);
  if (omgmt->f_data == NULL) { pwarn("No f_data() for object-management %s\n", objid); return -255; }

  return omgmt->f_data(vgame, vptr);
} /* Ende object_call_mgmt_data */
