/* 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 "nw.h"

void init_nw_xdatalist(void);

static VG_BOOL nw_xdata_allclients_recv(struct VG_NwXdataList **, VG_BOOL, const char *, size_t, VG_BOOL *);
static void nw_xdata_allclients_free(struct VG_NwXdataList *);


/* set functions */
void
init_nw_xdatalist(void)
{
  vg4->nw->xdata_allclients_recv = nw_xdata_allclients_recv;
  vg4->nw->xdata_allclients_free = nw_xdata_allclients_free;
} /* Ende init_nw_xdatalist */


/* nw_xdata_allclients_recv: receive exchange-data from all clients
 * @param xdatalistp  for returning received exchange-data list,
 *                    value must be set to NULL at the first function-call,
 *                    the list must be freed with vg4->nw->xdata_allclients_free()
 * @param exclusive   whether exclusive: each client must have different data
 * @param mydata      if exclusive: data the local client has sent to the network-server
 * @param mysize      if exclusive: number of bytes in mydata
 * @param redo        if exclusive: for returning whether local client must reselect its data
 * @return  VG_TRUE = OK or VG_FALSE = got exit-request
 *
 * This function simplifies receiving exchange-data,
 * when all clients sent data which all clients shall receive,
 * e.g. each client selects a car for a race.
 *
 * When the selection needs not to be exclusive,
 * this function waits until all data from all clients is received
 * and returns it in xdatalistp.
 *
 * When the selection shall be exclusive,
 * this function waits until all data from all clients is received,
 * except when another client selected the same data the local client requested,
 * which causes the function to return with redo = VG_TRUE,
 * this means that the local client shall reselect its data
 * and call this function again.
 */
static VG_BOOL
nw_xdata_allclients_recv(struct VG_NwXdataList **xdatalistp, VG_BOOL exclusive, const char *mydata, size_t mysize, VG_BOOL *redo)
{
  char *ex_data;
  size_t ex_size;
  int ex_clnr, clmax, clnr, icl;
  struct VG_NwXdataList *xdptr, **xdlast;

  if (redo != NULL) { *redo = VG_FALSE; }

  clmax = vg4->nw->numberofclients(NULL);
  clnr = vg4->nw->local_clnr();
  if (clmax == 0 || clnr == 0 || !vg4->nw->is_connected(clnr)) { return VG_TRUE; }
  if (xdatalistp == NULL) { return VG_TRUE; }

  if (exclusive && (mydata == NULL || mysize == 0 || redo == NULL)) {
    outerr("Exchange-data shall be exclusive, but no local data passed.");
    return VG_TRUE;
  }

  /* receive data from network-server */
  for (;;) {
    if (!vg4->nw->xdata_recv(&ex_data, &ex_size, &ex_clnr)) { return VG_FALSE; }

    if (ex_size > 0) {  /* exchange-data received */
      /* search for client-number in list */
      for (xdlast = xdatalistp; *xdlast != NULL; xdlast = &(*xdlast)->next) {
        if ((*xdlast)->clnr == ex_clnr) { break; }
      }
      if (*xdlast == NULL) {  /* not found: set exchange-data */
        if (exclusive) {  /* selection is exclusive */
          /* check if any client already owns the received exchange-data */
          for (xdptr = *xdatalistp; xdptr != NULL; xdptr = xdptr->next) {
            if (xdptr->size == ex_size && memcmp(xdptr->data, ex_data, ex_size) == 0) { break; }
          }
          if (xdptr == NULL) {  /* no, set it */
            *xdlast = SML3_calloc(1, sizeof(**xdlast));
            (*xdlast)->next = NULL;
            (*xdlast)->data = ex_data;
            (*xdlast)->size = ex_size;
            (*xdlast)->clnr = ex_clnr;
            ex_data = NULL;
          }
        } else {  /* not exclusive */
          *xdlast = SML3_calloc(1, sizeof(**xdlast));
          (*xdlast)->next = NULL;
          (*xdlast)->data = ex_data;
          (*xdlast)->size = ex_size;
          (*xdlast)->clnr = ex_clnr;
          ex_data = NULL;
        }
      }
      if (ex_data != NULL) { free(ex_data); }

    } else {  /* no data received */
      /* check if all connected clients have their exchange-data */
      for (icl = 1; icl <= clmax; icl++) {
        for (xdptr = *xdatalistp; xdptr != NULL; xdptr = xdptr->next) {
          if (xdptr->clnr == icl) { break; }
        }
        if (xdptr == NULL && vg4->nw->is_connected(icl)) { break; }
      }
      if (icl > clmax) { break; }  /* yes, we got all information */

      if (exclusive) {  /* selection is exclusive */
        /* check if another client has already my data */
        for (xdptr = *xdatalistp; xdptr != NULL; xdptr = xdptr->next) {
          if (xdptr->clnr == clnr) { break; }
        }
        if (xdptr == NULL) {  /* my data is not yet set */
          for (xdptr = *xdatalistp; xdptr != NULL; xdptr = xdptr->next) {
            if (xdptr->size == mysize && memcmp(xdptr->data, mydata, mysize) == 0) { break; }
          }
          if (xdptr != NULL) {  /* yes, so reselect data */
            *redo = VG_TRUE;
            break;
          }
        }
      }

      /* window-flush and wait */
      vg4->window->flush();
      vg4->misc->wait_time(100);
    }
  }

  return VG_TRUE;
} /* Ende nw_xdata_allclients_recv */


/* nw_xdata_allclients_free: free exchange-data list
 * @param xdatalist  exchange-data list
 */
static void
nw_xdata_allclients_free(struct VG_NwXdataList *xdatalist)
{
  struct VG_NwXdataList *xdnext;

  for (; xdatalist != NULL; xdatalist = xdnext) {
    xdnext = xdatalist->next;
    if (xdatalist->data != NULL) { free(xdatalist->data); }
    free(xdatalist);
  }
} /* Ende nw_xdata_allclients_free */
