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

void init_misc_saveload(void);

static void misc_settings_load(const char *);
static char * misc_settings_save(int);


/* set functions */
void
init_misc_saveload(void)
{
  vg4->misc->settings_load = misc_settings_load;
  vg4->misc->settings_save = misc_settings_save;
} /* Ende init_misc_saveload */


/* misc_settings_load:
 * set settings from string
 * @param sstr  settings-string (in general loaded from file)
 */
static void
misc_settings_load(const char *sstr)
{
  struct vgi_misc *sptr;
  const char *kpt1, *kpt2;
  char *bcode, *pval;
  int setting, wscale;
  struct VG_GCList gclist;
  VG_BOOL do_reopen, scale_nearest, scale_integer;

  if (sstr == NULL || *sstr == '\0') { return; }

  sptr = vg4data.lists.misc.s;

  do_reopen = VG_FALSE;
  vg4->window->getparameters(NULL, &wscale);
  scale_nearest = vg4data.lists.window.scale_nearest;
  scale_integer = vg4data.lists.window.scale_integer;

  vg4->input->gclist(&gclist);

  for (kpt1 = sstr, kpt2 = strchr(kpt1, '$'); kpt2 != NULL; kpt1 = kpt2 + 1, kpt2 = strchr(kpt1, '$')) {
    if (kpt2 == kpt1) { continue; }
    SML3_base64_decode(&bcode, NULL, kpt1, (size_t)(kpt2 - kpt1), NULL);
    if ((pval = strchr(bcode, '=')) == NULL || pval[1] == '\0') { free(bcode); continue; }
    setting = atoi(bcode);
    pval++;

    if (setting == VG_SETTING_WINDOWSCALE) {
      char *px1, *px2;
      px1 = pval;
      if ((px2 = strchr(px1, ',')) != NULL) {
        if (px2 > px1) {
          int ws1 = atoi(px1);
          if (ws1 >= 0 && ws1 != wscale) { wscale = ws1; do_reopen = VG_TRUE; }
        }
        px1 = px2 + 1;
      }
      if ((px2 = strchr(px1, ',')) != NULL) {
        if (px2 > px1) {
          if (atoi(px1) > 0) {
            vg4data.lists.window.scale_nearest = VG_TRUE;
          } else if (atoi(px1) == 0) {
            vg4data.lists.window.scale_nearest = VG_FALSE;
          }
        }
        px1 = px2 + 1;
        if (vg4data.lists.window.scale_nearest != scale_nearest) { do_reopen = VG_TRUE; }
      }
      if ((px2 = strchr(px1, ',')) != NULL) {
        if (px2 > px1) {
          if (atoi(px1) > 0) {
            vg4data.lists.window.scale_integer = VG_TRUE;
          } else if (atoi(px1) == 0) {
            vg4data.lists.window.scale_integer = VG_FALSE;
          }
        }
        px1 = px2 + 1;
        if (vg4data.lists.window.scale_integer != scale_integer) { do_reopen = VG_TRUE; }
      }

    } else if (setting == VG_SETTING_WINDOWBRIGHTNESS) {
      int wbright = atoi(pval);
      if (wbright >= 0 && wbright <= VG_MAX_BRIGHTNESS) {
        vg4->window->setbrightness(wbright);
      }

    } else if (setting == VG_SETTING_VOLUME) {
      if (vg4->audio->is_open()) {
        int vol;
        char *px1, *px2;
        px1 = pval;
        if ((px2 = strchr(px1, ',')) != NULL) {
          VG_BOOL ismute = (atoi(px1) > 0 ? VG_TRUE : VG_FALSE);
          vg4->audio->mute(ismute);
          px1 = px2 + 1;
        }
        if ((px2 = strchr(px1, ',')) != NULL) {
          if ((vol = atoi(px1)) >= 0 && vol <= 255) { vg4->audio->volume(VG_AUDIO_VOLUME_ALL, vol); }
          px1 = px2 + 1;
        }
        if ((px2 = strchr(px1, ',')) != NULL) {
          if ((vol = atoi(px1)) >= 0 && vol <= 255) { vg4->audio->volume(VG_AUDIO_VOLUME_SOUND, vol); }
          px1 = px2 + 1;
        }
        if ((px2 = strchr(px1, ',')) != NULL) {
          if ((vol = atoi(px1)) >= 0 && vol <= 255) { vg4->audio->volume(VG_AUDIO_VOLUME_MUSIC, vol); }
          px1 = px2 + 1;
        }
        if ((px2 = strchr(px1, ',')) != NULL) {
          if ((vol = atoi(px1)) >= 0 && vol <= 255) { vg4->audio->volume(VG_AUDIO_VOLUME_SPEECH, vol); }
          px1 = px2 + 1;
        }
      }

    } else if (setting == VG_SETTING_KEYS) {
      char *pname, *px1, *px2, *px3;
      int gcid, gcx;

      if ((px1 = strchr(pval, '=')) != NULL) {
        pname = pval;
        pval = px1; *pval++ = '\0';

        for (gcx = -1, gcid = -1;;) {
          /* search for device-ID */
          if (*pname != '\0') {  /* gamecontroller */
            for (gcx++; gcx < gclist.max; gcx++) {  /* search for next one */
              if (strcmp(pname, gclist.gc[gcx].name) == 0 && gclist.gc[gcx].id > 0) { break; }
            }
            if (gcx == gclist.max) {  /* not found */
              if (gcid < 0) { vg4->hash->setstr(sptr->hset_gc, pname, pval); }  /* first try */
              break;
            }
            if (gcid < 0) {  /* first try */
              gcid = gclist.gc[gcx].id;
              gclist.gc[gcx].id = 0;
            } else {  /* another one with same name */
              gcid = gclist.gc[gcx].id;
            }
          } else {  /* keyboard */
            gcid = 0;
          }

          /* set keys */
          px1 = pval;
          while ((px2 = strchr(px1, ',')) != NULL) {
            *px2 = '\0';
            if ((px3 = strchr(px1, ':')) != NULL) {
              if (gcid == 0) {
                vg4->input->key_setkbd(atoi(px1), atoi(px3 + 1));
              } else {
                vg4->input->key_setgc(atoi(px1), gcid, atoi(px3 + 1));
              }
            }
            px1 = px2 + 1;
          }

          if (gcid == 0) { break; }  /* keyboard */
        }
      }
    }

    free(bcode);
  }

  /* reopen window? */
  if (do_reopen) {
    if (!vg4->window->reopen(-1, wscale)) { outerr("Failed to reopen window - exiting"); exit(1); }
  }
} /* Ende misc_settings_load */


/* misc_settings_save:
 * return actual settings as allocated string for saving to file
 * @param settings  which to include into return-string: VG_SETTINGS bitwise or'ed
 * @return allocated settings-string, (must be freed with free())
 */
static char *
misc_settings_save(int settings)
{
  struct SML3_gummi gm0 = SML3_GUM_INITIALIZER, gm1 = SML3_GUM_INITIALIZER;
  size_t gmpos0 = 0, gmpos1 = 0;
  char *bcode;
  struct vgi_misc *sptr;

  sptr = vg4data.lists.misc.s;

  if (settings & VG_SETTING_WINDOWSCALE) {
    int wscale;
    vg4->window->getparameters(NULL, &wscale);
    gmpos1 = SML3_gumprintf(&gm1, 0, "%d=%d,%d,%d,", VG_SETTING_WINDOWSCALE,
             wscale, vg4data.lists.window.scale_nearest, vg4data.lists.window.scale_integer);
    SML3_base64_encode(&bcode, NULL, SML3_gumgetval(&gm1), gmpos1, 0, NULL);
    gmpos0 += SML3_gumprintf(&gm0, gmpos0, "%s$", bcode);
    free(bcode);
  }

  if (settings & VG_SETTING_WINDOWBRIGHTNESS) {
    int wbright = vg4->window->getbrightness();
    gmpos1 = SML3_gumprintf(&gm1, 0, "%d=%d", VG_SETTING_WINDOWBRIGHTNESS, wbright);
    SML3_base64_encode(&bcode, NULL, SML3_gumgetval(&gm1), gmpos1, 0, NULL);
    gmpos0 += SML3_gumprintf(&gm0, gmpos0, "%s$", bcode);
    free(bcode);
  }

  if (settings & VG_SETTING_VOLUME) {
    if (vg4->audio->is_open()) {
      VG_BOOL ismute;
      int vol_main, vol_sound, vol_music, vol_speech;
      ismute = vg4->audio->is_mute();
      vol_main = vg4->audio->volume(VG_AUDIO_VOLUME_ALL, -1);
      vol_sound = vg4->audio->volume(VG_AUDIO_VOLUME_SOUND, -1);
      vol_music = vg4->audio->volume(VG_AUDIO_VOLUME_MUSIC, -1);
      vol_speech = vg4->audio->volume(VG_AUDIO_VOLUME_SPEECH, -1);
      gmpos1 = SML3_gumprintf(&gm1, 0, "%d=%d,%d,%d,%d,%d,", VG_SETTING_VOLUME,
                              (ismute ? 1 : 0), vol_main, vol_sound, vol_music, vol_speech);
      SML3_base64_encode(&bcode, NULL, SML3_gumgetval(&gm1), gmpos1, 0, NULL);
      gmpos0 += SML3_gumprintf(&gm0, gmpos0, "%s$", bcode);
      free(bcode);
    }
  }

  if (settings & VG_SETTING_KEYS) {
    struct VG_GCList gclist;
    struct VG_KeyList keylist;
    int i1, i2;

    /* keyboard */
    gmpos1 = SML3_gumprintf(&gm1, 0, "%d==", VG_SETTING_KEYS);
    vg4->input->keylist(&keylist, 0);
    for (i2 = 0; i2 < keylist.max; i2++) {
      gmpos1 += SML3_gumprintf(&gm1, gmpos1, "%d:%d,", keylist.key[i2].keyref, keylist.key[i2].code);
    }
    SML3_base64_encode(&bcode, NULL, SML3_gumgetval(&gm1), gmpos1, 0, NULL);
    gmpos0 += SML3_gumprintf(&gm0, gmpos0, "%s$", bcode);
    free(bcode);

    /* gamecontrollers */
    vg4->input->gclist(&gclist);
    for (i1 = 0; i1 < gclist.max; i1++) {
      gmpos1 = SML3_gumprintf(&gm1, 0, "%d=%s=", VG_SETTING_KEYS, gclist.gc[i1].name);
      vg4->input->keylist(&keylist, gclist.gc[i1].id);
      for (i2 = 0; i2 < keylist.max; i2++) {
        gmpos1 += SML3_gumprintf(&gm1, gmpos1, "%d:%d,", keylist.key[i2].keyref, keylist.key[i2].code);
      }
      SML3_base64_encode(&bcode, NULL, SML3_gumgetval(&gm1), gmpos1, 0, NULL);
      gmpos0 += SML3_gumprintf(&gm0, gmpos0, "%s$", bcode);
      free(bcode);
    }
    /* add previous found gamecontrollers */
    { void *vpos = NULL;
      const char *key, *val; 
      vpos = NULL;
      for (key = vg4->hash->list(sptr->hset_gc, &vpos); vpos != NULL; key = vg4->hash->list(sptr->hset_gc, &vpos)) {
        for (i1 = 0; i1 < gclist.max; i1++) {
          if (strcmp(key, gclist.gc[i1].name) == 0) { break; }
        }
        if (i1 == gclist.max) {
          val = (const char *)vg4->hash->get(sptr->hset_gc, key, NULL);
          gmpos1 = SML3_gumprintf(&gm1, 0, "%d=%s=%s", VG_SETTING_KEYS, key, val);
          SML3_base64_encode(&bcode, NULL, SML3_gumgetval(&gm1), gmpos1, 0, NULL);
          gmpos0 += SML3_gumprintf(&gm0, gmpos0, "%s$", bcode);
          free(bcode);
        }
      }
    }
  }

  SML3_gumdest(&gm1);

  return SML3_gumswapstr(&gm0, NULL, 0);
} /* Ende misc_settings_save */
