/* 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 <strings.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include "actionstack.h"

void init_actionstack(void);
void dest_actionstack(void);

static void actionstack_freefunction(void (*)(unsigned int, void *));
static void actionstack_push(unsigned int, void *);
static void * actionstack_get(unsigned int *);
static void actionstack_pop(void);
static void * actionstack_parent(unsigned int *);
static void actionstack_clear(void);


/* set functions */
void
init_actionstack(void)
{
  vg4data.lists.actionstack.stack = NULL;
  vg4data.lists.actionstack.freefn = NULL;
  vg4->actionstack->freefunction = actionstack_freefunction;
  vg4->actionstack->push = actionstack_push;
  vg4->actionstack->get = actionstack_get;
  vg4->actionstack->pop = actionstack_pop;
  vg4->actionstack->parent = actionstack_parent;
  vg4->actionstack->clear = actionstack_clear;
} /* Ende init_actionstack */

void
dest_actionstack(void)
{
  vg4->actionstack->clear();
  vg4data.lists.actionstack.freefn = NULL;
} /* Ende dest_actionstack */


/* actionstack_freefunction:
 * set function for freeing data of actionstack-element
 * @param freefn  free-function, or NULL = free()
 *                parameters:
 *                 - unsigned int name_id
 *                 - void *vdata (data to be freed)
 */
static void
actionstack_freefunction(void (*freefn)(unsigned int, void *))
{
  vg4data.lists.actionstack.freefn = freefn;
} /* Ende actionstack_freefunction */


/* actionstack_push:
 * push element onto actionstack
 * @param name_id  arbitrary positive ID, which defines this element
 * @param vdata    allocated data for this element
 */
static void
actionstack_push(unsigned int name_id, void *vdata)
{
  struct vgi_astack *astk;

  if (name_id == 0) { return; }

  astk = SML3_calloc(1, sizeof(*astk));
  astk->next = vg4data.lists.actionstack.stack;

  astk->aste.name_id = name_id;
  astk->aste.vdata = vdata;

  vg4data.lists.actionstack.stack = astk;
} /* Ende actionstack_push */


/* actionstack_get:
 * get upmost element from actionstack
 * @param name_id  for returning element-ID
 * @return  pointer to data of upmost element
 *
 * Returning 0 for name_id indicates an empty stack.
 */
static void *
actionstack_get(unsigned int *name_id)
{
  struct vgi_astack *astk = vg4data.lists.actionstack.stack;

  if (name_id != NULL) { *name_id = 0; }
  if (astk == NULL) { return NULL; }

  if (name_id != NULL) { *name_id = astk->aste.name_id; }

  return astk->aste.vdata;
} /* Ende actionstack_get */


/* actionstack_pop:
 * remove upmost element from actionstack
 */
static void
actionstack_pop(void)
{
  struct vgi_astack *astk;

  if (vg4data.lists.actionstack.stack == NULL) { return; }

  astk = vg4data.lists.actionstack.stack->next;

  if (vg4data.lists.actionstack.stack->aste.vdata != NULL) {
    if (vg4data.lists.actionstack.freefn != NULL) {
      vg4data.lists.actionstack.freefn(vg4data.lists.actionstack.stack->aste.name_id, vg4data.lists.actionstack.stack->aste.vdata);
    } else {
      free(vg4data.lists.actionstack.stack->aste.vdata);
    }
  }

  free(vg4data.lists.actionstack.stack);
  vg4data.lists.actionstack.stack = astk;
} /* Ende actionstack_pop */


/* actionstack_parent:
 * get parent of upmost element from actionstack
 * @param name_id  for returning element-ID
 * @return  pointer to data of parent of upmost element
 *
 * Returning 0 for name_id indicates that no parent exists.
 */
static void *
actionstack_parent(unsigned int *name_id)
{
  struct vgi_astack *astk = vg4data.lists.actionstack.stack;

  if (name_id != NULL) { *name_id = 0; }
  if (astk == NULL) { return NULL; }
  if (astk->next == NULL) { return NULL; }

  if (name_id != NULL) { *name_id = astk->next->aste.name_id; }

  return astk->next->aste.vdata;
} /* Ende actionstack_parent */


/* actionstack_clear:
 * clear actionstack
 */
static void
actionstack_clear(void)
{
  while (vg4data.lists.actionstack.stack != NULL) {
    vg4->actionstack->pop();
  }
} /* Ende actionstack_clear */
