/***************************************************************************
 *   copyright           : (C) 2004 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 "pbookphone.h"
#include "scmxx.h"
#include "helper.h"
#include "atcommand.h"
#include "gtincl.h"

#include <ctype.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>

void struct_pbook_phone_entry_init (struct pbook_phone_entry* p) {
  p->slot = 0;
  p->number = NULL;
  p->numtype = numtype(NULL);
  p->text = NULL;
}
void struct_pbook_phone_entry_delete (struct pbook_phone_entry* p) {
  mem_realloc(p->number,0);
  mem_realloc(p->text,0);
}

int pbook_phone_write_entry (struct pbook_phone_entry* entry) {
  char* temp;
  int retval = 1;

  at_gen_phonebook_write(entry->slot,entry->number,
			 entry->numtype,(char*)entry->text);
  temp = at_read_line();
  if (at_line_type(temp,NULL,NULL) != AT_RET_OK) retval = 0;
  mem_realloc(temp,0);
  return retval;
}

int pbook_phone_write (char* mem, struct pbook_phone_entry** entries) {
  unsigned int min,max;
  int nlen,tlen;
  unsigned long i = 0;
  unsigned long count = 0;
  char* text = NULL;

  if (!pbook_get_ranges(mem,1,&min,&max,&nlen,&tlen)) {
    errexit(_("getting limits of phonebook %s failed.\n"),mem);
  }

  for (i = 0; entries[i] != NULL; ++i) {
    if (min > entries[i]->slot || entries[i]->slot > max) {
      errexit (_("slot %d is out of range %d-%d.\n"),entries[i]->slot,min,max);
    }
  }
  count = i;
  if (count > max-min+1) {
    errexit(_("too many entries (max. %d).\n"),max-min+1);
  }

  if (count > 1) {
    print_verbose(0,_("Updating entries %s(%d-%d)...\n"),mem,min,max);
    text = mem_alloc(str_len(mem)+numlen(max)+3,1);
    for (i = 1; entries[i-1] != NULL; ++i) {
      if (pbook_phone_write_entry(entries[i-1]) == 0) return 0;
      sprintf(text,"%s(%u)",(mem)?mem:"",entries[i-1]->slot);
      console_print_status(STDERR_FILENO,text,str_len(text),&i,&count,1);
    }
    mem_realloc(text,0);
    console_print_status(STDERR_FILENO,_("done"),strlen(_("done")),&i,&count,1);
    if (count > 1) print_verbose(0,"%c",'\n');
  } else {
    print_verbose(0,_("Updating one entry in slot %d of memory %s..."),entries[0]->slot,mem);    
    if (pbook_phone_write_entry(entries[0]) == 0) return 0;
  }
  print_verbose(0,"%s\n",_("done"));
  return 1;
}

/* fill the pbook_phone_entry from a text line
 */
int pbook_phone_str2entry (char* line,
			   struct pbook_phone_entry* entry)
{
  //pointers in line
  char* slot;
  char* number;
  char* numtype;
  char* text;

  if (line == NULL || entry == NULL) return -1;

  /* Policy is to not trust in what the phone delivers
   * Thus, full checking must be done.
   * Format: slot,"number",numtype,"text"
   */
  slot = line;
  while(isdigit((int)*line) && *line != 0) ++line;
  if (*line != ',' || *line == 0) return 0;
  ++line;
  if (*line != '"' || *line == 0) return 0;
  number = ++line;
  while (*line != '"' && *line != 0) ++line;
  if (*line != '"' || *line == 0) return 0;
  ++line;
  if (*line != ',' || *line == 0) return 0;
  numtype = ++line;
  while(isdigit((int)*line) && *line != 0) ++line;
  if (*line != ',' || *line == 0) return 0;
  ++line;
  if (*line != '"' || *line == 0) return 0;
  text = ++line;
  if ((line=strrchr(text-1,'"')) <= text-1) return 0;
  ++line;
  if (*line != 0) return 0;

  entry->slot = atoi(slot);
  entry->number = strn_dup(number,strchr(number,'"')-number);
  entry->numtype = atoi(numtype);
  entry->text = (unsigned char*)strn_dup(text,strrchr(text,'"')-text);
  return 1;
}

/* slot_max-slot_min can be used to loop over a limited range
 * The returned pointer is malloc'd, NULL-terminated list,
 * each list enty is malloc'd, too.
 */
#include <unistd.h>
struct pbook_phone_entry**
pbook_phone_get_range (char* mem,
		       unsigned int slot_min,
		       unsigned int slot_max,
		       int print_counter)
{
  unsigned int i = 0;
  unsigned range = slot_max-slot_min+1;
  struct pbook_phone_entry** retval;
  struct pbook_phone_entry* buffer;
  struct pbook_phone_entry** vcfbuf;
  char* command;
  char* ausgabe;
  char* temp;
  enum return_code linestatus;
  int status;
  int enable_slot_range_test = 1;
  unsigned int j,k = 0;

  if (strcasecmp(mem,VCARD_PHONEBOOK_NAME) == 0)
    if (slot_min != slot_max) {
      retval = mem_alloc(((range*4)+1)*sizeof(*retval),0);
      for (i = slot_min; i <= slot_max; ++i) {
	//call this function recursively (one level)
	vcfbuf = pbook_phone_get_range(mem,i,i,print_counter);
	for (j = 0; vcfbuf[j] != NULL; ++j) {
	  retval[k++] = vcfbuf[j];
	}
	vcfbuf = mem_realloc(vcfbuf,0);
      }
      retval[k] = NULL;
      return retval;
    } else {
      command = at_sie_phonebook_vcf_read(slot_min, -1);
      enable_slot_range_test = 0;
      range = 4;
    }
  else
    if (strcasecmp(mem,"RD") == 0 || strcasecmp(mem,"CS") == 0)
      command = at_sie_phonebook_read_sorted(slot_min,slot_max); /* Get sorted pb */
    else
      command = at_gen_phonebook_read(slot_min,slot_max); /* Get pb */
  
  if (command != NULL) {
    ausgabe = at_read_line();
    linestatus = at_line_type(ausgabe,command,&temp);
  }
  if (command == NULL || linestatus&AT_RET_ERROR)
    errexit(_("reading from phonebook %s failed: %s\n"),mem,temp);

  retval = mem_alloc((range+1)*sizeof(*retval),0);
  while ((linestatus == AT_RET_ANSWER ||
	  linestatus == AT_RET_OTHER) &&
	 i <= i+range-1) {
    buffer = mem_alloc(sizeof(**retval),0);
    struct_pbook_phone_entry_init(buffer);
    status = pbook_phone_str2entry(temp,buffer);
    if (status <= 0) errexit(_("parsing entry failed: %s\n"), temp);

    if (enable_slot_range_test) {
      if (buffer->slot > slot_max) errexit(_("phone returned unexpected entry %u\n"),buffer->slot);
      for (; i+slot_min < buffer->slot; ++i) {
	retval[i] = mem_alloc(sizeof(**retval),0);
	struct_pbook_phone_entry_init(retval[i]);
	retval[i]->slot = i+slot_min;
	if (verbosity_get() < VERBOSE_LEVEL_NOTICE && print_counter)
	  print_verbose(0," %d",retval[i]->slot);
      }
    }
    retval[i] = buffer;
    if (strcasecmp(mem,VCARD_PHONEBOOK_NAME) == 0) retval[i]->slot = slot_min;
    if (verbosity_get() < VERBOSE_LEVEL_NOTICE && print_counter)
      print_verbose(0," %d",retval[i]->slot);

    mem_realloc(ausgabe,0);
    ausgabe = at_read_line();
    linestatus = at_line_type(ausgabe,command,&temp);
    if (linestatus&AT_RET_ERROR) errexit(_("reading from phonebook %s failed: %s\n"),mem,temp);
    ++i;
  }
  if (enable_slot_range_test) {
    for (; i+slot_min <= slot_max; ++i) {
      retval[i] = mem_alloc(sizeof(**retval),0);
      struct_pbook_phone_entry_init(retval[i]);
      retval[i]->slot = i+slot_min;
      if (verbosity_get() < VERBOSE_LEVEL_NOTICE && print_counter)
	print_verbose(0," %d",retval[i]->slot);
    }
  }
  retval[i] = NULL; //termination of list
  return retval;
}

// number of requested number during a search
#define search_delta 5

int pbook_phone_find_empty (char* mem, unsigned int startslot) {
  unsigned int min = -1, max = -1;
  unsigned int tempmax;
  int retval = -1;
  unsigned int i;
  struct pbook_phone_entry** temp;

  //reading available indexes
  if (!pbook_get_ranges(mem,0,&min,&max,NULL,NULL)) {
    errexit(_("getting limits of phonebook %s failed.\n"),mem);
  }
  if ((min == (unsigned int)0 && max == (unsigned int)0) ||
      startslot > max)
  {
    //emty phonebook
    return -1;
  }

  print_verbose(0,"%s\n",_("Trying to find an empty slot..."));
  if (startslot < min) startslot = min;
  for (;startslot <= max && retval < 0; startslot += search_delta ) {
    tempmax = startslot+search_delta-1;
    if (tempmax > max) tempmax = max;
    temp = pbook_phone_get_range(mem,startslot,tempmax,0);
    for (i=0; temp[i] != NULL; ++i) {
      if (str_len(temp[i]->number) == 0 &&
	  str_len((char*)temp[i]->text) == 0 &&
	  retval < 0) {
	retval = temp[i]->slot;
	print_verbose(0,_("Detected empty slot %d\n"),retval);
      }
      struct_pbook_phone_entry_delete(temp[i]);
    }
    mem_realloc(temp,0);
  }
  return retval;
}

/* number of requesting numbers
 * higher means a little bit more speed
 * lower helps slow phones/SIM-cards to not time out
 */
#define delta 5

struct pbook_phone_entry**
pbook_phone_get (char* mem) {
  unsigned cur_min, cur_max;
  unsigned int min = -1, max = -1;
  struct pbook_phone_entry** retval;
  struct pbook_phone_entry** temp;
  unsigned long i,j; //index in temp
  unsigned int k = 0; //index in retval
  char* text = NULL;

  //reading available indexes
  if (!pbook_get_ranges(mem,0,&min,&max,NULL,NULL)) {
    errexit(_("getting limits of phonebook %s failed.\n"),mem);
  }
  if (min == 0 && max == 0) {
    //emty phonebook
    return NULL;
  }
  text = mem_alloc(str_len(mem)+numlen(min)+numlen(max)+4,1);
  sprintf(text,"%s(%d-%d)",mem,min,max);
  print_verbose(0,"%s\n",_("Receiving phonebook entries..."));
  //4 times as big due to phonebook vcf
  retval = mem_alloc(((4*(max-min+1))+1)*sizeof(*retval),0);
  retval[0] = NULL;
  for (cur_min = min; cur_min <= (unsigned int)max; cur_min += delta) {
    cur_max = cur_min+delta-1;
    if (cur_max > (unsigned int)max) cur_max = max;
    i = cur_min-min;
    j = max-min+1;
    console_print_status(STDERR_FILENO,text,str_len(text),&i,&j,1);
    temp = pbook_phone_get_range(mem,cur_min,cur_max,0);
    i = 0;
    while (temp[i] != NULL) retval[k++] = temp[i++];
    retval[k] = NULL;    
  }
  i = max;
  console_print_status(STDERR_FILENO,text,str_len(text),&i,&i,1);
  mem_realloc(text,0);
  print_verbose(0,"%c",'\n');
  return retval;
}
