#include "mmix.h"      /* anything, we may need */
#include "defaults.h"  /* extern default variables */

/* in the configuration, the on/off settings are
 * written as a describing character ...
 * ==> R_NO->0 || R_OFF->1 || R_ON->2
 */
#define CFG_MUTE_ON   'M' /* (2) muted resource */
#define CFG_MUTE_OFF  'm' /* (1) unmuted resource */

#define CFG_REC_ON    'R' /* (2) recording is on */
#define CFG_REC_OFF   'r' /* (1) recording is off */
#define CFG_REC_NO    '-' /* (0) recording isn't aviable */

#define CFG_ENABLED   'E' /* (1) resource is on, is seen in mmix */
#define CFG_DISABLED  'D' /* (0) resource is off, isn't printed in mmix */

#define CFG_MAXSIZE 10000
#define CFG_HEADER  \
"# .mmixrc / created with version " VERSION "\n#\n" \
"# set \"nameXY\" and \"cfgnXY\" how you want it :)\n\n"
#define CFG_ENTRY 15 /* "left:right:m:r;" */

static void defaults_string_append(unsigned char **str, unsigned char *msg);
static void defaults_config_create(struct mmix *first, int fd);
static unsigned char *defaults_config_newsection(struct mmix *m, int cmix, bool new);
static bool defaults_load_section(struct mmix *m, unsigned char **lines, size_t *b);
static bool init_done=0;

/* append new stuff to a str + free old one */
static void defaults_string_append(unsigned char **str, unsigned char *msg) {
 str_add(str, msg); xfree(msg); /* msg is set! */
}

/* create a new section, return it as a string */
static unsigned char *defaults_config_newsection(struct mmix *m, int cmix, bool new) {
 char *names[] = SOUND_DEVICE_NAMES;
 unsigned char *s, *ret=0;
 int i;

 s=str_concat("\n[", m->mname, "]", NULL); /* begin section */
 defaults_string_append(&ret, s);

 /* the names, which can be edited by the user */
 for (i=0;i<SOUND_MIXER_NRDEVICES;i++)
 if ((m->devmask&(1<<i)) == (1<<i)) {
  char *n=mallocn(char, 3);
  xsnprintf(n, 2, "%02d", i);
  s=str_concat("\nname", n, "=", m->rname[i]?m->rname[i]:names[i], NULL);
  defaults_string_append(&ret, s);
  xfree(n);
 }

 /* 9 diff. configuration names */
 for (i=1;i<10;i++) {
  s=mallocn(unsigned char, 101);
  if (str_len((unsigned char*)m->cname[i])!=0) {
   xsprintf((char*)s, "\ncfgn%02d=%s", i, m->cname[i]); /* old value */
  } else xsprintf((char*)s, "\ncfgn%02d=config%02d mixer%d", i, i, cmix+1);
  defaults_string_append(&ret, s);
 }

 /* settings: conf01=left:right:m:r;left2:right2:m2:r2;... */
 for (i=1;i<10;i++) {
  int r=0, cfg;
  if (new) { cfg=0; } else { cfg=i; } /* new or old values */
  s=mallocn(unsigned char, 15);
  xsprintf((char*)s, "\nconf%02d=", i);
  defaults_string_append(&ret, s);
  for (;r<SOUND_MIXER_NRDEVICES;r++)
  if ((m->devmask&(1<<r)) == (1<<r)) {
   s=mallocn(unsigned char, CFG_ENTRY+1);
   xsprintf((char*)s, "%03d:%03d:%d:%d;",
    m->v[cfg][r].left, m->v[cfg][r].right,
    m->v[cfg][r].m, m->v[cfg][r].r);
   defaults_string_append(&ret, s);
  }
 } /* devmask/settings */
 str_add(&ret, (unsigned char*)"\nend;");
 return ret;
}

/* just write a complete new configuration */
static void defaults_config_create(struct mmix *first, int fd) {
 unsigned char *s=0;
 struct mmix *m;
 int cmix;
 write(fd, CFG_HEADER, sizeof(CFG_HEADER)-1);
 for (cmix=0,m=first;m!=NULL;m=m->next,cmix++) { /* for all mixer */
  s=defaults_config_newsection(m, cmix, 1); /* create new section */
  write(fd, s, str_len(s)); xfree(s);
  write(fd, "\n", 1);
 } /* all mixers */
}

static bool defaults_load_section(struct mmix *m, unsigned char **lines, size_t *b) {
 int l, cfg=0;
 char ns[2];
 for (l=*b;lines[l];l++) {
  if (!str_ncmp(lines[l], (unsigned char*)"end;", 4)) {
   m->cfg_okay=TRUE;
   *b=l; return OK;
  }
  ns[0]=ns[1]=0;

  /* name##=xy */
  if (!str_ncmp(lines[l], (unsigned char*)"name", 4)) {
   if (is_digit(lines[l][4])) ns[0]=lines[l][4];
   if (is_digit(lines[l][5])) ns[1]=lines[l][5];
   cfg=atoi(ns);
   if (init_done) xfree(m->rname[cfg]);
   m->rname[cfg]=(char*)str_dup(lines[l]+7);
   continue;
  }

  /* cfgn##=xy */
  if (!str_ncmp(lines[l], (unsigned char*)"cfgn", 4)) {
   if (is_digit(lines[l][4])) ns[0]=lines[l][4];
   if (is_digit(lines[l][5])) ns[1]=lines[l][5];
   cfg=atoi(ns);
   if (init_done) xfree(m->cname[cfg]);
   m->cname[cfg]=(char*)str_dup(lines[l]+7);
   continue;
  }

  /* conf01=left:right:m:s:r;left2:right2:m2:s2:r2 */
  if (!str_ncmp(lines[l], (unsigned char*)"conf", 4)) {
   unsigned char **r, **e;
   size_t count, n, n2;
   if (is_digit(lines[l][4])) ns[0]=lines[l][4];
   if (is_digit(lines[l][5])) ns[1]=lines[l][5];
   cfg=atoi(ns);
   r=str_split(lines[l]+7, ';', &count);
   if (count-1!=m->nrdevs) return ERR;
   for (n=0; n<count-1; n++) {
    int rs=mixer_realres(m, n+1);
    e=str_split(r[n], ':', &n2);
    /* we should have always 4 entry's now */
    if (n2!=4) return ERR;
    m->v[cfg][rs].left=atoi((const char*)e[0]);
    m->v[cfg][rs].right=atoi((const char*)e[1]);
    m->v[cfg][rs].m=atoi((const char*)e[2]);
    m->v[cfg][rs].r=atoi((const char*)e[3]);
   }
  } /* conf##=left:... */
 } /* while ! "end;" */
 return ERR; /* there was no "end;" :( */
}


/* extern */

bool defaults_loadconfig(struct mmix *first) {
 unsigned char *str, **lines=0;
 struct mmix *m=first;
 size_t c,l,cl;

 readit:
 str=open_readclose(mmix_config, &l, CFG_MAXSIZE); /* open existing file */
 if (!str) { /* if no file is there ... */
  l=open_trunc(mmix_config); /* try to create a new one */
  if ((int)l<0) { return ERR; } /* failed -> error */
  defaults_config_create(&first[0], l); /* write initial values ... */
  close(l); /* ... and close it */
  xfree(str);sync();
  goto readit; /* read it */
 }
 lines=str_split(str, '\n', &l);
 if (!lines) return ERR;

 /* for all lines, begin with 0 */
 for (cl=0; cl<l; cl++) {
  for (m=first;m!=NULL;m=m->next) {
   unsigned char *s=str_concat("[", m->mname, "]", NULL);
   if (!str_cmp(s, lines[cl])) { /* section found, load it */
    if (defaults_load_section(&m[0], lines, &cl)!=OK) {
     draw("error in section from line %d ...\n", cl+1);
     return ERR;
   }}
   xfree(s);
  }
 } /* EOF config */
 xfree(str);

 /* add new devices, if any */
 for (c=0,m=first;m!=NULL;m=m->next,c++) if (m->cfg_okay!=TRUE) {
  unsigned char *s;
  draw("adding new section(%d) to %s config  (%d)...", c, m->mname, m->cfg_okay);
  s=defaults_config_newsection(&m[0],c,1);
  if (open_appendclose(mmix_config, s)!=str_len(s)) {
   draw(" failed!"); return ERR;
  }
  xfree(s);
 }
 init_done=1;
 return OK;
}

bool defaults_saveconfig(struct mmix *first) {
 unsigned char *str, **lines=0, *section;
 struct mmix *m;
 size_t l, cl;
 bool found=0;
 int fd;

 str=open_readclose(mmix_config, &l, CFG_MAXSIZE); /* read old config */
 if ((fd=open_trunc(mmix_config))<0) return ERR; /* truncate + begin writing */
 lines=str_split(str, '\n', &l);
 if (!lines) return ERR;

 for (cl=0; cl<l; cl++, found=0) { /* for all lines */
  for (m=first;m!=NULL;m=m->next) { /* for all mixers */
   section=str_concat("[", m->mname, "]", NULL);
   if (!str_cmp(section, lines[cl])) { /* section found */
    unsigned char *n=defaults_config_newsection(&m[0],0,0);
    while (cl++<l) if (!str_cmp(lines[cl], (unsigned char*)"end;")) break;
    write(fd, n, str_len(n));
    xfree(n); found=1;
   }
   xfree(section);
  }
  if (!found) {
   if (cl) write(fd, "\n", 1); /* old line */
   write(fd, lines[cl], str_len(lines[cl])); /* old line */
  }
 }
 close(l); /* close config */
 xfree(str); /* old one */
 return OK;
}
