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

void init_dialog_network(void);

static VG_BOOL dialog_nw_clientname(char *, size_t, const char *, const struct VG_Position *, struct VG_Hash *);
static VG_BOOL dialog_nw_connect(VG_BOOL *, char *, size_t, const char *, const struct VG_Position *, struct VG_Hash *);
static VG_BOOL dialog_nw_server(const char *, const struct VG_Position *, struct VG_Hash *);


/* set functions */
void
init_dialog_network(void)
{
  vg4->dialog->nw_clientname = dialog_nw_clientname;
  vg4->dialog->nw_connect = dialog_nw_connect;
  vg4->dialog->nw_server = dialog_nw_server;
} /* Ende init_dialog_network */


/* dialog_nw_clientname:
 * open a canvas-dialog for typing in client-name for network
 * @param clname      passing default client-name if set, and for returning client-name
 * @param clsize      sizeof(clname)
 * @param cvasdir     directory to load canvas from, or NULL = system-canvas
 * @param posdst      canvas-position on window, or NULL = centered
 * @param hvar        hash with variable-values for text control-commands "var", or NULL
 *                      hash-format:
 *                       - key:   variable name
 *                       - value: variable value
 *                    (see below)
 * @return  VG_TRUE = OK or VG_FALSE = exit-request
 *
 * The canvas-name must be:
 *  - nw.clientname.cvas:
 *    - with items:
 *      - [CV-TEXT]   with name "title": text for title
 *      - [CV-INPUT]  with name "input": for typing in client-name
 *    - uses following variables:
 *      - key: "title", value: <title of canvas>
 *    - hvar should contain:
 *      - key: "clientname:title", value: <title of canvas>
 */
static VG_BOOL
dialog_nw_clientname(char *clname, size_t clsize, const char *cvasdir, const struct VG_Position *posdst, struct VG_Hash *hvar)
{
  const char *cvprefix = "network";
  const char *cvfile_clientname = "nw.clientname.cvas";
  const char *cvname_input = "input";
  struct VG_Hash *hvar_clientname;
  char cvaspath[512], hname[64];
  VG_BOOL retw;
  struct VG_Canvas *cvas_clientname;
  const char *selname;

  hvar_clientname = NULL;

  /* set variables and load canvas */

  /* clientname-canvas */
  hvar_clientname = vg4->hash->create();
  dialog_transfer_variables(hvar, hvar_clientname, "clientname");
  dialog_cvasfile(cvprefix, cvfile_clientname, cvasdir, cvaspath, sizeof(cvaspath));
  cvas_clientname = vg4->canvas->load(cvaspath, hvar_clientname);
  if (cvas_clientname == NULL) {
    outerr("%s [at cvasdir=%s] not found", cvfile_clientname, (cvasdir == NULL ? "(null)" : cvasdir));
    return VG_TRUE;
  }

  if (clname != NULL && clsize > 0 && *clname != '\0') {
    vg4->misc->strcpy(hname, sizeof(hname), clname);
  } else {
    if (gethostname(hname, sizeof(hname)) < 0) { snprintf(hname, sizeof(hname), "<nobody>"); }
  }
  vg4->canvas->input_set_text(cvas_clientname, cvname_input, hname);

  /* loop */

  retw = VG_TRUE;
  for (;;) {
    /* execute clientname-canvas */
    if (!vg4->canvas->exec(cvas_clientname, posdst, &selname)) { retw = VG_FALSE; break; }
    if (selname == NULL) { break; }  /* cancel */

    /* act according to selection */
    if (strcmp(selname, cvname_input) == 0) {
      const char *t_input = vg4->canvas->input_get_text(cvas_clientname, cvname_input);
      if (*t_input != '\0') {
        if (clname != NULL && clsize > 0) { vg4->misc->strcpy(clname, clsize, t_input); }
      }
      break;
    }
  }

  vg4->canvas->destroy(cvas_clientname);
  if (hvar_clientname != NULL) { vg4->hash->destroy(hvar_clientname); }

  return retw;
} /* Ende dialog_nw_clientname */


/* dialog_nw_connect:
 * open a canvas-dialog for specifying network-server and connect to it
 * @param do_connect  for returning clicked button
 *                     - VG_TRUE: OK, connect-button was clicked
 *                     - VG_FALSE: cancel-button was clicked
 * @param svname      passing default server-name if set, and for returning server-name
 *                     - if set after return, server-name was typed in
 *                     - if empty after return, server-name shall be searched via broadcast/multicast
 * @param svsize      sizeof(svname)
 * @param cvasdir     directory to load canvas from, or NULL = system-canvas
 * @param posdst      canvas-position on window, or NULL = centered
 * @param hvar        hash with variable-values for text control-commands "var", or NULL
 *                      hash-format:
 *                       - key:   variable name
 *                       - value: variable value
 *                    (see below)
 * @return  VG_TRUE = OK or VG_FALSE = exit-request
 *
 * The canvas-name must be:
 *  - nw.connect.cvas:
 *    - with items:
 *      - [CV-TEXT]   with name "title": text for title
 *      - [CV-TEXT]   with name "info": text for connection-type, e.g. "Search for network-server" and "Input network-server"
 *      - [CV-SWITCH] with name "switch": for setting whether server-name is being searched or typed in
 *      - [CV-INPUT]  with name "input": for typing in server-name
 *      - [CV-BUTTON] with name "connect": for being ready to connect
 *      - [CV-BUTTON] as a cancel-button
 *    - uses following variables:
 *      - key: "title", value: <title of canvas>
 *    - hvar should contain:
 *      - key: "connect:title", value: <title of canvas>
 *      - key: "connect:info-on", value: <text for info, when searching for network-server>
 *      - key: "connect:info-off", value: <text for info, when network-server must be typed in>
 */
static VG_BOOL
dialog_nw_connect(VG_BOOL *do_connect, char *svname, size_t svsize,
                 const char *cvasdir, const struct VG_Position *posdst, struct VG_Hash *hvar)
{
  const char *cvprefix = "network";
  const char *cvfile_connect = "nw.connect.cvas";
  const char *cvname_switch = "switch";
  const char *cvname_input = "input";
  const char *cvname_connect = "connect";
  const char *cvvar_info = "info";
  const char *cvparm_connect_info_on = "info-on";
  const char *cvparm_connect_info_off = "info-off";
  struct VG_Hash *hvar_connect;
  char cvaspath[512];
  VG_BOOL retw;
  struct VG_Canvas *cvas_connect;
  const char *selname, *info_on, *info_off;

  hvar_connect = NULL;

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

  /* set variables and load canvas */

  /* connect-canvas */
  hvar_connect = vg4->hash->create();
  dialog_transfer_variables(hvar, hvar_connect, "connect");
  dialog_cvasfile(cvprefix, cvfile_connect, cvasdir, cvaspath, sizeof(cvaspath));
  cvas_connect = vg4->canvas->load(cvaspath, hvar_connect);
  if (cvas_connect == NULL) {
    outerr("%s [at cvasdir=%s] not found", cvfile_connect, (cvasdir == NULL ? "(null)" : cvasdir));
    return VG_TRUE;
  }

  info_on = (const char *)vg4->hash->get(hvar_connect, cvparm_connect_info_on, NULL);
  if (info_on == NULL) { info_on = "Search for network-server"; }
  info_off = (const char *)vg4->hash->get(hvar_connect, cvparm_connect_info_off, NULL);
  if (info_off == NULL) { info_off = "Input network-server"; }

  if (svname != NULL && svsize > 0 && *svname != '\0') {
    vg4->canvas->switch_set(cvas_connect, cvname_switch, VG_FALSE);
    vg4->canvas->input_set_text(cvas_connect, cvname_input, svname);
  }

  if (vg4->canvas->switch_get(cvas_connect, cvname_switch)) {  /* search for nw-server */
    vg4->canvas->text_set(cvas_connect, cvvar_info, info_on);
    vg4->canvas->disable(cvas_connect, cvname_input, VG_TRUE);
  } else {  /* input nw-server */
    vg4->canvas->text_set(cvas_connect, cvvar_info, info_off);
    vg4->canvas->disable(cvas_connect, cvname_input, VG_FALSE);
    vg4->canvas->set_activated(cvas_connect, cvname_input);
  }

  /* loop */

  retw = VG_TRUE;
  for (;;) {
    /* execute connect-canvas */
    if (!vg4->canvas->exec(cvas_connect, posdst, &selname)) { retw = VG_FALSE; break; }
    if (selname == NULL) { break; }  /* cancel */

    /* act according to selection */
    if (strcmp(selname, cvname_switch) == 0) {
      if (vg4->canvas->switch_get(cvas_connect, cvname_switch)) {  /* search for nw-server */
        vg4->canvas->text_set(cvas_connect, cvvar_info, info_on);
        vg4->canvas->disable(cvas_connect, cvname_input, VG_TRUE);
      } else {  /* input nw-server */
        vg4->canvas->text_set(cvas_connect, cvvar_info, info_off);
        vg4->canvas->disable(cvas_connect, cvname_input, VG_FALSE);
        vg4->canvas->set_activated(cvas_connect, cvname_input);
      }

    } else if (strcmp(selname, cvname_connect) == 0) {
      VG_BOOL is_ok = VG_TRUE;
      if (!vg4->canvas->switch_get(cvas_connect, cvname_switch)) {
        const char *t_input = vg4->canvas->input_get_text(cvas_connect, cvname_input);
        if (*t_input != '\0') {
          if (svname != NULL && svsize > 0) { vg4->misc->strcpy(svname, svsize, t_input); }
        } else {
          is_ok = VG_FALSE;
        }
      }
      if (is_ok) {
        if (do_connect != NULL) { *do_connect = VG_TRUE; }
        break;
      }
    }
  }

  vg4->canvas->destroy(cvas_connect);
  if (hvar_connect != NULL) { vg4->hash->destroy(hvar_connect); }

  return retw;
} /* Ende dialog_nw_connect */


/* dialog_nw_server:
 * open a canvas-dialog for starting or stopping network-server locally
 * @param cvasdir     directory to load canvas from, or NULL = system-canvas
 * @param posdst      canvas-position on window, or NULL = centered
 * @param hvar        hash with variable-values for text control-commands "var", or NULL
 *                      hash-format:
 *                       - key:   variable name
 *                       - value: variable value
 *                    (see below)
 * @return  VG_TRUE = OK or VG_FALSE = exit-request
 *
 * The canvas-name must be:
 *  - nw.server.cvas:
 *    - with items:
 *      - [CV-TEXT]   with name "info": text for server-state
 *      - [CV-BUTTON] with name "button": starting or stopping the server locally
 *      - [CV-BUTTON] as a cancel-button
 *    - uses following variables:
 *      - key: "buttontxt", value: <button-text>
 *    - hvar should contain:
 *      - key: "server:info-ison-local", value: <text if server is running locally, e.g. "Server running locally">
 *      - key: "server:info-ison-remote", value: <text if server is running remotely, e.g. "Server running in LAN">
 *      - key: "server:info-isoff", value:<text if server is not running, e.g. "Server stopped">
 *      - key: "server:buttontxt-ison", value: <button-text if server is running, e.g. "Stop">
 *      - key: "server:buttontxt-isoff", value: <button-text if server is not running, e.g. "Start">
 */
static VG_BOOL
dialog_nw_server(const char *cvasdir, const struct VG_Position *posdst, struct VG_Hash *hvar)
{
  const char *cvprefix = "network";
  const char *cvfile_server = "nw.server.cvas";
  const char *cvname_button = "button";
  const char *cvvar_info = "info";
  const char *cvparm_server_info_ison_local = "info-ison-local";
  const char *cvparm_server_info_ison_remote = "info-ison-remote";
  const char *cvparm_server_info_isoff = "info-isoff";
  const char *cvparm_server_buttontxt_ison = "buttontxt-ison";
  const char *cvparm_server_buttontxt_isoff = "buttontxt-isoff";
  const char *cvparm_server_buttontxt = "buttontxt";
  struct VG_Hash *hvar_server;
  char cvaspath[512];
  VG_BOOL retw, nw_is_on, nw_islocal;
  struct VG_Canvas *cvas_server;
  const char *selname, *info_ison_local, *info_ison_remote, *info_isoff, *buttontxt_ison, *buttontxt_isoff;

  hvar_server = NULL;

  /* set variables and load canvas */

  /* server-canvas */
  hvar_server = vg4->hash->create();
  dialog_transfer_variables(hvar, hvar_server, "server");
  dialog_cvasfile(cvprefix, cvfile_server, cvasdir, cvaspath, sizeof(cvaspath));
  cvas_server = vg4->canvas->load(cvaspath, hvar_server);
  if (cvas_server == NULL) {
    outerr("%s [at cvasdir=%s] not found", cvfile_server, (cvasdir == NULL ? "(null)" : cvasdir));
    return VG_TRUE;
  }

  info_ison_local = (const char *)vg4->hash->get(hvar_server, cvparm_server_info_ison_local, NULL);
  if (info_ison_local == NULL) { info_ison_local = "Server running locally"; }
  info_ison_remote = (const char *)vg4->hash->get(hvar_server, cvparm_server_info_ison_remote, NULL);
  if (info_ison_remote == NULL) { info_ison_remote = "Server running in LAN"; }
  info_isoff = (const char *)vg4->hash->get(hvar_server, cvparm_server_info_isoff, NULL);
  if (info_isoff == NULL) { info_isoff = "Server stopped"; }
  buttontxt_ison = (const char *)vg4->hash->get(hvar_server, cvparm_server_buttontxt_ison, NULL);
  if (buttontxt_ison == NULL) { buttontxt_ison = "Stop"; }
  buttontxt_isoff = (const char *)vg4->hash->get(hvar_server, cvparm_server_buttontxt_isoff, NULL);
  if (buttontxt_isoff == NULL) { buttontxt_isoff = "Start"; }

  /* loop */

  retw = VG_TRUE;
  for (;;) {
    /* check for running network-server in the LAN */
    if (vg4->nw->check_server(&nw_islocal)) { nw_is_on = VG_TRUE; } else { nw_is_on = VG_FALSE; }

    if (nw_is_on) {
      vg4->hash->setstr(hvar_server, cvparm_server_buttontxt, buttontxt_ison);
    } else {
      vg4->hash->setstr(hvar_server, cvparm_server_buttontxt, buttontxt_isoff);
    }
    if (!vg4->canvas->reload(cvas_server, hvar_server)) { return VG_TRUE; }

    if (nw_is_on) {
      if (nw_islocal) {
        vg4->canvas->text_set(cvas_server, cvvar_info, info_ison_local);
        vg4->canvas->set_activated(cvas_server, cvname_button);
      } else {
        vg4->canvas->text_set(cvas_server, cvvar_info, info_ison_remote);
        vg4->canvas->disable(cvas_server, cvname_button, VG_TRUE);
      }
    } else {
      vg4->canvas->text_set(cvas_server, cvvar_info, info_isoff);
      vg4->canvas->set_activated(cvas_server, cvname_button);
    }

    /* execute server-canvas */
reexec:
    if (!vg4->canvas->exec(cvas_server, posdst, &selname)) { retw = VG_FALSE; break; }
    if (selname == NULL) { break; }  /* cancel */
    if (*selname == '\0') { goto reexec; }  /* don't want to do checking again now */

    /* act according to selection */
    if (strcmp(selname, cvname_button) == 0) {
      if (nw_is_on) {
        vg4->nw->server("stop");
      } else {
        if (!vg4->nw->check_server(NULL)) { vg4->nw->server("start"); }
      }
      break;
    }
  }

  vg4->canvas->destroy(cvas_server);
  if (hvar_server != NULL) { vg4->hash->destroy(hvar_server); }

  return retw;
} /* Ende dialog_nw_server */
