/***************************************************************************
 *   copyright           : (C) 2002 by Hendrik Sattler                     *
 *   mail                : post@hendrik-sattler.de                         *
 *                                                                         *
 *   This program 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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "memtypes.h"	 
#include "scmxx.h"
#include "options.h"
#include "helper.h"
#include "atcommand.h"
#include "gtincl.h"

#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <stdarg.h>

static unsigned int roundup2 (unsigned int d) {
  return ((d/2) + (d%2));
}

static char* file_types_get_string (int action) {
  char* ausgabe = at_get_vendor();
  char* command;

  if (ausgabe == NULL || strcasecmp(ausgabe,"SIEMENS") != 0) return NULL;

  ausgabe = mem_realloc(ausgabe,0);
  if (action == SCMXX_ACTION_GET) command = AT_SIE_BIN_READ;
  else command = AT_SIE_BIN_WRITE;

  at_command_send(command,"?");
  return at_get_value(command);
}

static unsigned int file_type_count (const char* str) {
  int count = 0;
  char* start;
  char* end;

  if (str == NULL) return 0;
  //count the number of entries
  while ((start = strchr(str,(int)'"')) != NULL &&
	 (end = strchr(start+1,(int)'"')) != NULL) {
    ++count;
    str = end+1;
  }
  return count;
}

static struct slot_range** file_type_filter (struct slot_range** input, int action) {
  char* vendor = at_get_vendor();
  char* model = at_get_model();
  unsigned int i = 0;

  if (strcasecmp(vendor,"SIEMENS") == 0) {
    if (strcasecmp(model,"ME45") == 0 ||
	strcasecmp(model,"S45") == 0 ||
	strcasecmp(model,"S45i") == 0) {
      for (; input[i] != NULL; ++i) {
	/* actually, you should not access midi slot 4 with S45 or ME45 because
	 * of a firmware bug:
	 * no data loss occurs but the phone thinks, a new music file arrived
	 * although this is not true.
	 */
	if (action != SCMXX_ACTION_SEND &&
	    strcmp(input[i]->name,"mid") == 0 &&
	    input[i]->max >= 4)
	  input[i]->max = 3;
      }
    } else if (strcasecmp(model,"S55") == 0) {
      for (; input[i] != NULL; ++i) {
	/* S55 stalls on bmp slots 3 and 4, obviously not present or very buggy
	 */
	if (action != SCMXX_ACTION_GET &&
	    strcmp(input[i]->name,"bmp") == 0 &&
	    input[i]->max >= 2)
	  input[i]->max = 2;
      }
    }
  }
  return input;
}

struct slot_range** file_types_get (int action) {
  struct slot_range** retval;
  char* ausgabe;
  char* temp;
  char* start;
  char* end;
  int count = 0;
  int status;

  ausgabe = file_types_get_string(action);
  /* ^SBNR: ("bmp",(0-2)),("mid",(0-10)),("vcf",(0-500)),("vcs",(1-500))
   * ^SBNW: ("bmp",(0-4)),("mid",(0-10)),("vcf",(0-500)),("vcs",(0-500)),("t9d",(0))
   */

  if (ausgabe == NULL) return NULL;
  retval = mem_alloc(sizeof(*retval)*(file_type_count(ausgabe)+1),0);
      
  //assign the entries;
  temp = ausgabe;
  count = 0;
  while ((start = strchr(temp,(int)'"')) != NULL &&
	 (end = strchr(start+1,(int)'"')) != NULL)
  {
    temp = end+1;

    retval[count] = mem_alloc(sizeof(**retval),0);
    status = sscanf(start,"\"%4[^\"]\",(%d-%d)",retval[count]->name,
		    &retval[count]->min,&retval[count]->max);
    if (status == 3) {
      ++count;
    } else {
      status = sscanf(start,"\"%4[^\"]\",(%d)",retval[count]->name,
		      &retval[count]->min);
      if (status == 2)
      {
	retval[count]->max = retval[count]->min;
	++count;
      }
    }
  }
  retval[count] = NULL;
  mem_realloc(ausgabe,0);
  return file_type_filter(retval,action);
}

struct file_mem_list {
  char* name; //pointer in r or w
  char* longname;
  struct slot_range* r;
  struct slot_range* w;
};
struct file_mem_list** struct_file_mem_list_init (struct slot_range** r,
						  struct slot_range** w)
{
  struct file_mem_list** retval;
  unsigned int rc = 0, wc = 0; //counters for r and w
  unsigned int c = 0;

  if (r != NULL) while (r[rc] != NULL) ++rc;
  if (w != NULL) while (w[wc] != NULL) ++wc;

  retval = mem_alloc((rc+wc+1)*sizeof(*retval),0);
  if (r != NULL) {
    for (rc = 0; r[rc] != NULL; ++rc) {
      retval[c] = mem_alloc(sizeof(**retval),0);
      retval[c]->name = r[rc]->name;
      retval[c]->r = r[rc];
      retval[c]->w = NULL;
      ++c;
    }
  }
  if (w != NULL) {
    for (wc = 0; w[wc] != NULL; ++wc) {
      for (rc = 0; r[rc] != NULL; ++rc) {
	if (strcmp(r[rc]->name,w[wc]->name) == 0) break;
      }
      if (r[rc] == NULL) {
	retval[c] = mem_alloc(sizeof(**retval),0);
	retval[c]->name = w[wc]->name;
	retval[c]->r = NULL;
	retval[c]->w = w[wc];
	++c;
      } else {
	retval[rc]->w = w[wc];
      }
    }
  }
  retval[c] = NULL;
  for (c = 0; retval[c] != NULL; ++c) {
    if (strcmp(retval[c]->name,"bmp") == 0) {
      retval[c]->longname = _("bitmap");
    } else if (strcmp(retval[c]->name,"mid") == 0) {
      retval[c]->longname = _("midi");
    } else if (strcmp(retval[c]->name,"vcs") == 0) {
      retval[c]->longname = _("vCalendar");
    } else if (strcmp(retval[c]->name,"vcf") == 0) {
      retval[c]->longname = _("vCard (address book)");
    } else if (strcmp(retval[c]->name,"bmx") == 0) {
      retval[c]->longname = _("EMS animation");
    } else if (strcmp(retval[c]->name,"t9d") == 0) {
      retval[c]->longname = _("T9 database");
    } else if (strcmp(retval[c]->name,"dir") == 0) {
      retval[c]->longname = _("UCS-2 encoded dir name");
    } else {
      retval[c]->longname = NULL;
    }
  }
  return retval;
}

void file_print_memlist (FILE* fd, unsigned int full) {
  struct slot_range** rrange;
  struct slot_range** wrange;
  struct file_mem_list** memlist;
  unsigned int m = 0;

  const unsigned int columns = 4;
  struct table_column {
    char* name;
    unsigned int width;
  } table[columns];
  char* entry[columns];
  unsigned int i;
  
  if (fd == NULL) {
    fd = stdout;
  }

  rrange = file_types_get(SCMXX_ACTION_GET);
  wrange = file_types_get(SCMXX_ACTION_SEND);
  memlist = struct_file_mem_list_init(rrange,wrange);
  mem_realloc(rrange,0);
  mem_realloc(wrange,0);

  if (!full) {
    for (; memlist[m] != NULL; ++m) {
      if (m != 0) fprintf(fd,", ");
      fprintf(fd,"%s",memlist[m]->name);
      mem_realloc(memlist[m]->r,0);
      mem_realloc(memlist[m]->w,0);
      mem_realloc(memlist[m],0);
    }    
  } else {
    table[0].name = _("mem");
    table[1].name = _("readable");
    table[2].name = _("writable");
    table[3].name = _("description");
    for (i = 0; i < columns; ++i)
      table[i].width = strlen(table[i].name);
    
    //maybe the column width need to be bigger
    for (m = 0; memlist[m] != NULL; ++m) {
      if (table[0].width < strlen(memlist[m]->name))
	table[0].width = strlen(memlist[m]->name);
      if (memlist[m]->r != NULL)
	if (table[1].width < numlen(memlist[m]->r->min)+1+numlen(memlist[m]->r->max))
	  table[1].width = numlen(memlist[m]->r->min)+1+numlen(memlist[m]->r->max);
      if (memlist[m]->w != NULL)
	if (table[2].width < numlen(memlist[m]->w->min)+1+numlen(memlist[m]->w->max))
	  table[2].width = numlen(memlist[m]->w->min)+1+numlen(memlist[m]->w->max);
    }
    for (i = 0; i < columns; ++i)
      entry[i] = mem_alloc((table[i].width+1)*sizeof(char),1);

    fprintf(fd,"\n");
    //print table header
    for (i = 0; i < columns; ++i)
      fprintf(fd,"%-*s  ",table[i].width,table[i].name);
    fprintf(fd,"\n");
    for (i = 0; i < columns; ++i) {
      memset(entry[i],'-',table[i].width);
      fprintf(fd,"%s  ",entry[i]);
    }
    fprintf(fd,"\n");
    mem_realloc(entry[0],0);
    mem_realloc(entry[columns-1],0);

    for (m = 0; memlist[m] != NULL; ++m) {
      //1st column
      entry[0] = memlist[m]->name;

      //2nd column
      if (memlist[m]->r) {
	if (memlist[m]->r->min != memlist[m]->r->max)
	  sprintf(entry[1],"%*s%d-%d",
		  roundup2(table[1].width-numlen(memlist[m]->r->min)-1-numlen(memlist[m]->r->max)),
		  "",
		  memlist[m]->r->min,
		  memlist[m]->r->max);
	else
	  sprintf(entry[1],"%*s%d",
		  roundup2(table[1].width-numlen(memlist[m]->r->min)),
		  "",
		  memlist[m]->r->min);
      } else sprintf(entry[1],"%*s%s",roundup2(table[1].width-4),"","none");

      //3rd column
      if (memlist[m]->w) {
	if (memlist[m]->w->min != memlist[m]->w->max)
	  sprintf(entry[2],"%*s%d-%d",
		  roundup2(table[2].width-numlen(memlist[m]->w->min)-1-numlen(memlist[m]->w->max)),
		  "",
		  memlist[m]->w->min,
		  memlist[m]->w->max);
	else
	  sprintf(entry[2],"%*s%d",
		  roundup2(table[2].width-numlen(memlist[m]->w->min)),
		  "",
		  memlist[m]->w->min);
      } else sprintf(entry[2],"%*s%s",roundup2(table[2].width-4),"","none");

      //print columns 1-3
      for (i = 0; i < columns-1; ++i) {
	fprintf(fd,"%-*s  ",table[i].width,entry[i]);
      }

      //print 4th column if present
      if (memlist[m]->longname != NULL) {
	fprintf(fd,"%s",memlist[m]->longname);
      }
      fprintf(fd,"\n");
      
      mem_realloc(memlist[m]->r,0);
      mem_realloc(memlist[m]->w,0);
      mem_realloc(memlist[m],0);
    }
    mem_realloc(entry[1],0);
    mem_realloc(entry[2],0);
  }
  mem_realloc(memlist,0);
  fprintf(fd,"\n");
}
char** pb_get_types (void) {

  char** retval;
  char* ausgabe;
  char* command;
  unsigned int i = 0;
  int has_mem_vcf = 0;

  ausgabe = at_get_vendor();
  if(ausgabe != NULL &&
     strcasecmp(ausgabe,"SIEMENS") == 0) {
    command = AT_SIE_PB_SELECT;
    mem_realloc(ausgabe,0);
    ausgabe = at_get_model();
    if (strcmp(ausgabe,"A65") != 0) { /* A65 stalls on this command, work-around */
      at_command_send(AT_SIE_PB_VCF_READ,AT_READ_SUFFIX);
      mem_realloc(ausgabe,0);
      ausgabe = at_get_value(AT_SIE_PB_VCF_READ);
      if (ausgabe != NULL) has_mem_vcf = 1;
    }
  } else {
    command = AT_GEN_PB_SELECT;
  }
  mem_realloc(ausgabe,0);
  at_command_send(command,AT_READ_SUFFIX);
  ausgabe = at_get_value(command);
  /* ^SPBS: ("FD","SM","ON","LD","MC","RC","OW","MS","CD","BL","RD","CS")
   * +CPBS: ("FD","SM","ON","LD","MC","RC")
   */
  if (ausgabe != NULL) {
    retval = at_parse_stringlist(ausgabe);
    if (has_mem_vcf) {
      while (retval[i] != NULL) ++i;
      retval = mem_realloc(retval,(i+2)*sizeof(*retval));
      retval[i] = str_dup(VCARD_PHONEBOOK_NAME);
      retval[i+1] = NULL;
    }
  } else {
    retval = NULL;
  }
  return retval;
}

void pbook_print_memlist (FILE* fd, unsigned int full) {
  char** memlist;
  unsigned int m;
  const unsigned int columns = 6;
  struct table_column {
    char* name;
    unsigned int width;
  } table[columns];
  char* entry[columns];

  unsigned int i = 0;
  unsigned int min;
  unsigned int max;
  int nrlen;
  int txtlen;
  int writable;
  
  if (fd == NULL) {
    fd = stdout;
  }

  memlist = pb_get_types();
  if (memlist == NULL) {
      fprintf(fd,"\n");
      return;
  }
  if (!full) {
    if (memlist != NULL) {
      for (m = 0; memlist[m] != NULL; ++m) {
	if (m != 0) fprintf(fd,", ");
	fprintf(fd,"%s",memlist[m]);
      }
    }
  } else {
    table[0].name = _("mem");
    table[1].name = _("slots");
    table[2].name = _("writable");
    table[3].name = _("digits");
    table[4].name = _("chars");
    table[5].name = _("description");
    for (i = 0; i < columns; ++i)
      table[i].width = strlen(table[i].name);

    if (table[1].width < 6)
      table[1].width = 6; //in case of 1-1000
    if (table[1].width < strlen(_("none")))
      table[1].width = strlen(_("none"));
    if (table[2].width < strlen(_("yes")))
      table[2].width = strlen(_("yes"));
    if (table[2].width < strlen(_("no")))
      table[2].width = strlen(_("no"));

    for (i = 0; i < columns; ++i)
      entry[i] = mem_alloc((table[i].width+1)*sizeof(char),1);

    fprintf(fd,"\n");
    //print table header
    for (i = 0; i < columns; ++i)
      fprintf(fd,"%-*s  ",table[i].width,table[i].name);
    fprintf(fd,"\n");
    for (i = 0; i < columns; ++i) {
      memset(entry[i],'-',table[i].width);
      fprintf(fd,"%s  ",entry[i]);
    }
    fprintf(fd,"\n");
    mem_realloc(entry[0],0);
    mem_realloc(entry[columns-1],0);

    for (m = 0; memlist[m] != NULL; ++m) {
      //getting data for current row
      pbook_select_mem(memlist[m]);
      writable = pbook_get_ranges(memlist[m],1,&min,&max,&nrlen,&txtlen);
      if (writable == 0) {
	if(!pbook_get_ranges(memlist[m],0,&min,&max,&nrlen,&txtlen)) {
	  print_warning(_("could not get limits of phonebook %s.\n"),memlist[m]);
	  ++m;
	  continue;
	}
      }
      
      //1st column
      entry[0] = memlist[m];

      //2nd column
      if (min != max) {
	sprintf(entry[1],"%*s%d-%d",
		roundup2(table[1].width-numlen(min)-1-numlen(max)),
		"",
		min,max);
      } else {
	if (min == 0) {
	  sprintf(entry[1],"%*s%s",
		  roundup2(table[1].width-strlen(_("none"))),
		  "",
		  _("none"));
	} else {
	  sprintf(entry[1],"%*s%d",
		  roundup2(table[1].width-numlen(min)),
		  "",
		  min);
	}
      }

      //3rd column
      if (writable) {
	sprintf(entry[2],"%*s%s",
		roundup2(table[2].width-strlen(_("yes"))),
		"",
		_("yes"));
      } else {
	sprintf(entry[2],"%*s%s",
		roundup2(table[2].width-strlen(_("no"))),
		"",
		_("no"));
      }

      //4th column
      if (nrlen >= 0) {
	sprintf(entry[3],"%*s%d",
		roundup2(table[3].width-numlen(nrlen)),
		"",
		nrlen);
      } else {
	memset(entry[3],0,1);
      }

      //5th column
      if (nrlen >= 0) {
	sprintf(entry[4],"%*s%d",
		roundup2(table[4].width-numlen(txtlen)),
		"",
		txtlen);
      } else {
	memset(entry[4],0,1);
      }

      //6th column
      if (strcasecmp(memlist[m],"FD") == 0) {
	entry[5] = _("SIM fix-dialing phonebook");
      } else if (strcasecmp(memlist[m],"SM") == 0) {
	entry[5] = _("SIM phonebook");
      } else if (strcasecmp(memlist[m],"DC") == 0 ||
		 strcasecmp(memlist[m],"MD") == 0) {
	entry[5] = _("last calls (mobile)");
      } else if (strcasecmp(memlist[m],"LD") == 0) {
	entry[5] = _("last calls (SIM)");
      } else if (strcasecmp(memlist[m],"ON") == 0 ||
		 strcasecmp(memlist[m],"OW") == 0) {
	entry[5] = _("own numbers");
      } else if (strcasecmp(memlist[m],"ME") == 0) {
	entry[5] = _("mobile equipment phonebook");
      } else if (strcasecmp(memlist[m],"BD") == 0) {
	entry[5] = _("barred numbers");
      } else if (strcasecmp(memlist[m],"SD") == 0) {
	entry[5] = _("service numbers");
      } else if (strcasecmp(memlist[m],"MC") == 0 ||
		 strcasecmp(memlist[m],"MS") == 0) {
	entry[5] = _("missed calls");
      } else if (strcasecmp(memlist[m],"RC") == 0 ||
		 strcasecmp(memlist[m],"CD") == 0) {
	entry[5] = _("callback numbers");
      } else if (strcasecmp(memlist[m],"BL") == 0) {
	entry[5] = _("blacklist numbers");
      } else if (strcasecmp(memlist[m],"MB") == 0) {
	entry[5] = _("mailbox numbers");
      } else if (strcasecmp(memlist[m],"RD") == 0) {
	entry[5] = _("red book (VIP in CS)");
      } else if (strcasecmp(memlist[m],"CS") == 0) {
	entry[5] = _("common sortable (FD+SM+ME)");
      } else if (strcasecmp(memlist[m],VCARD_PHONEBOOK_NAME) == 0) {
	entry[5] = _("address book numbers");
      } else {
	entry[5] = NULL;
      }

      //print columns 1-5
      for (i = 0; i < columns-1; ++i) {
	fprintf(fd,"%-*s  ",table[i].width,entry[i]);
      }
      //print column 6 if present
      if (entry[5] != NULL) {
	fprintf(fd,"%s",entry[5]);	
      }
      fprintf(fd,"\n");
    }
    for (i = 1; i < columns-1; ++i)
      mem_realloc(entry[i],0);
  }
  fprintf(fd,"\n");
  mem_realloc(*memlist,0);
  mem_realloc(memlist,0);
}

char** sms_get_types (void) {
  char** retval = NULL;
  unsigned int count = 0;
  unsigned int i = 0, k = 0;
  char* ausgabe;
  char** list;
  char** list2;

  at_command_send(AT_GEN_SMS_MEM,"?");
  ausgabe = at_get_value(AT_GEN_SMS_MEM);
  /* +CPMS: ("MT","SM","ME"),("MT","SM","ME"),("MT","SM","ME")
   */
  if (ausgabe != NULL) {
    list = str_split(ausgabe,"),(",0);
    if (list != NULL) {
      retval = mem_alloc(sizeof(*retval),0);
      retval[0] = NULL;
      for (i = 0; list[i] != NULL; ++i) {
	list2 = at_parse_stringlist(list[i]);
	k = 0;
	while (list2[k] != NULL) ++k;
	retval = str_array_append(retval,count,list2,k);
	count += k;
	mem_realloc(list2,0);
      }
      str_array_uniq(retval);
    }
    mem_realloc(list,0);
  }
  return retval;
}

void sms_print_memlist (FILE* fd, unsigned int full) {
  char** memlist;
  unsigned int m;
  const unsigned int columns = 4;
  struct table_column {
    char* name;
    unsigned int width;
  } table[columns];
  char* entry[columns];

  unsigned int i = 0;
  struct slot_range range;
  int current;

  if (fd == NULL) {
    fd = stdout;
  }

  memlist = sms_get_types();
  if (!full) {
    if (memlist != NULL) {
      for (m = 0; memlist[m] != NULL; ++m) {
	if (m != 0) fprintf(fd,", ");
	fprintf(fd,"%s",memlist[m]);
      }
    }
  } else {
    table[0].name = _("mem");
    table[1].name = _("slots");
    table[2].name = _("used");
    table[3].name = _("description");
    for (i = 0; i < columns; ++i)
      table[i].width = strlen(table[i].name);

    if (table[1].width < 6)
      table[1].width = 6; //in case of 1-1000
    if (table[2].width < 9)
      table[2].width = 9; //in case of 1000/1000

    for (i = 0; i < columns; ++i)
      entry[i] = mem_alloc((table[i].width+1)*sizeof(char),1);

    fprintf(fd,"\n");
    //print table header
    for (i = 0; i < columns; ++i) {
      sprintf(entry[i],"%*s%s",
	      roundup2(table[i].width-strlen(table[i].name)),
	      "",table[i].name);
      fprintf(fd,"%-*s  ",table[i].width,entry[i]);
    }
    fprintf(fd,"\n");
    for (i = 0; i < columns; ++i) {
      memset(entry[i],'-',table[i].width);
      fprintf(fd,"%s  ",entry[i]);
    }
    fprintf(fd,"\n");
    mem_realloc(entry[0],0);
    mem_realloc(entry[columns-1],0);

    for (m = 0; memlist[m] != NULL; ++m) {
      //getting data for current row
      sms_select_mem(memlist[m],&range,&current);

      //1st column
      entry[0] = memlist[m];
      
      //2nd column
      if (range.min != range.max) {
	sprintf(entry[1],"%*s%d-%d",
		roundup2(table[1].width-numlen(range.min)-1-numlen(range.max)),
		"",range.min,range.max);
      } else {
	if (range.min == 0) {
	  sprintf(entry[1],"%*s%s",
		  roundup2(table[1].width-strlen(_("none"))),
		  "",_("none"));
	} else {
	  sprintf(entry[1],"%*s%d",
		  roundup2(table[1].width-numlen(range.min)),
		  "",range.min);
	}
      }

      //3rd column
      sprintf(entry[2],"%*s%d/%d",
	      roundup2(table[2].width-numlen(range.max-range.min+1)-1-numlen(current)),
	      "",current,range.max-range.min+1);

      //4th column
      if (strcmp(range.name,"SM") == 0) {
	entry[3] = _("SIM memory");
      } else if (strcmp(range.name,"ME") == 0) {
	entry[3] = _("mobile equipment memory");
      } else if (strcmp(range.name,"MT") == 0) {
	entry[3] = "ME + SM";
      } else { 
	entry[3] = NULL;
      }

      //print columns 1-3
      for (i = 0; i < columns-1; ++i) {
	fprintf(fd,"%-*s  ",table[i].width,entry[i]);
      }
      //print column 4 if present
      if (entry[3] != NULL) {
	fprintf(fd,"%s",entry[3]);
      }
      fprintf(fd,"\n");
    }
    mem_realloc(entry[1],0);
    mem_realloc(entry[2],0);
  }
  fprintf(fd,"\n");
  mem_realloc(*memlist,0);
  mem_realloc(memlist,0);
}
