/***************************************************************************
 *   copyright           : (C) 2002,2003,2004,2005 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 "scmxx.h"
#include "helper.h"
#include "memtypes.h"
#include "atcommand.h"
#include "gtincl.h"
#include "w32compat.h"

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

inline char* get_revision (void) {
  at_command_send(AT_GEN_REVISION,NULL);
  return at_get_value(AT_GEN_REVISION);
}

inline char* get_phoneserial (void) {
  at_command_send(AT_GEN_IMSI,NULL);
  return at_get_value(AT_GEN_IMSI);
}

inline char* get_simserial (void) {
  at_command_send(AT_GEN_IMEI,NULL);
  return at_get_value(AT_GEN_IMEI);
}

int pbook_select_mem (char* mem) {
  char* command;
  char* temp;
  enum return_code status;

  if (strcasecmp(mem,VCARD_PHONEBOOK_NAME) == 0) return 1;

  command = at_sie_phonebook_select(mem);
  temp = at_read_line();
  status = at_line_type(temp,NULL,NULL);
  mem_realloc(temp,0);
  if (status == AT_RET_OK) return 1;
  else return 0;
}

int sms_select_mem (char* mem, struct slot_range* r,
		    int* current_fill)
{
  char* temp;
  char* ausgabe;
  int retval = 0;

  if (str_len(mem) == 0) {
    return 0;
  }

  at_gen_sms_mem(mem,mem,NULL);
  ausgabe = at_get_value(AT_GEN_SMS_MEM);
  //example: +CPMS: 10,112,10,112,8,12
  if (str_len(ausgabe) != 0) {
    if (r != NULL) {
      strncpy(r->name,mem,sizeof(r->name));
      temp = strchr(ausgabe,',');
      if (str_len(temp)) {
	r->min = 1;
	r->max = atoi(temp+1);
      }
    }
    if (current_fill != NULL && str_len(ausgabe)) {
      *current_fill = atoi(ausgabe);
    }
    retval = 1;
  }
  mem_realloc(ausgabe,0);
  return retval;
}

#include "ttyaccess.h"
#include <unistd.h>

void phone_reset (struct tty_access* hw) {
  print_verbose(0,"%s...",_("Sending reset frame"));
  /* sending a sync frame
   * the phone may not respond without it because it thinks that all
   * send commands are still data from a previous run
   */
  hw->write("\x1a\r",2);
  /* it might also happen that some phones are
   * in "transparent/OBEX mode" and must be switched to "RCCP/GIPSY mode"
   */
  usleep(1010*1000);
  hw->write("+++",3);
  usleep(1010*1000);
  hw->write("\r",1);
  usleep(1000*1000);

  /* flush all data because we are not interested in it */
  hw->flush();
  print_verbose(0,"%s\n",_("done"));
}

int phone_init (int reset, struct tty_access* hw) {
  int retval = 1;
  char* ack;

  if (reset) { /* big reset */
    phone_reset(hw);
  } else { /* small reset */
    hw->write("\r",1);
    usleep(1000*1000);
    hw->flush();
  }
  at_command_send(AT_GEN_INIT,NULL);
  ack = at_read_line();
  if (at_line_type(ack,NULL,NULL) != AT_RET_OK) {
    mem_realloc(ack,0);
    ack = at_read_line();
    if (at_line_type(ack,NULL,NULL) != AT_RET_OK) {
      retval = 0;
    }
  }
  mem_realloc(ack,0);
  return retval;;
}

int command_echo (int enable) {
  char* command;
  char* ack;
  enum return_code status;
  if (enable) {
    command = AT_GEN_ECHO_ENABLE;
  } else {
    command = AT_GEN_ECHO_DISABLE;
  }
  at_command_send(command,NULL);
  ack = at_read_line();
  status = at_line_type(ack,NULL,NULL);
  mem_realloc(ack,0);
  return (status == AT_RET_OK);
}

int verbose_errors (int enable) {
  char* parmlist;
  char* ack;
  enum return_code status;
  if (enable) {
    parmlist = "2";
  } else {
    parmlist = "0";
  }
  at_command_send(AT_GEN_ERROR,parmlist);
  ack = at_read_line();
  status = at_line_type(ack,NULL,NULL);
  mem_realloc(ack,0);
  return (status == AT_RET_OK);
}

char* get_charset (void) {
  char* ausgabe;
  char* temp;
    
  at_syntax_request(AT_GEN_CHARSET);
  ausgabe = at_get_value(AT_GEN_CHARSET);
  if (ausgabe != NULL) {
    if (*ausgabe == '"' && 
	(temp = strchr(ausgabe,'"')) != NULL) {
      temp = strn_dup(ausgabe+1,temp-ausgabe-1);
      mem_realloc(ausgabe,0);
      return temp;
    }
  }
  return NULL;
}

char** get_charsets (void) {
  char* ausgabe;

  at_command_send(AT_GEN_CHARSET,"?");
  ausgabe = at_get_value(AT_GEN_CHARSET);
  //example: +CSCS: ("GSM","UCS2")
  return at_parse_stringlist(ausgabe);
}

int set_charset(char *charset) {
  char *temp;
  int  retval = 0;

  if (str_len(charset) == 0) {
    errexit("%s\n",_("no charset specified."));
  }
  at_gen_charset(charset);
  temp = at_read_line();
  if (at_line_type(temp,NULL,NULL) == AT_RET_OK) {
    retval = 1;
  }
  mem_realloc(temp,0);
  return retval;
}

void set_smsc (char* number) {
  char* temp;
  char* error;
  enum return_code status;

  if (str_len(number) == 0) {
    errexit("%s\n",_("no number specified."));
  } else if (is_telephone_number(number) == 0) {
    errexit("%s\n",_("no valid number specified."));
  }
  at_gen_smsc(number);
  temp = at_read_line();
  status = at_line_type(temp,AT_GEN_SMSC,&error);
  if (status == AT_RET_OK) {
    print_verbose(0,_("SMS server number was set to %s\n"),number);
  } else {
    errexit("%s\n", (status == AT_RET_ERROR) ? _("unknown cause") : error);
  }
  mem_realloc(temp,0);
}

char* get_smsc (void) {
  char* ausgabe;
  char* temp;
    
  at_syntax_request(AT_GEN_SMSC);
  ausgabe = at_get_value(AT_GEN_SMSC);
  if (ausgabe != NULL && *ausgabe == '"') {
    if ((temp=strstr(ausgabe,"\",")) != NULL) {
      temp = strn_dup(ausgabe+1,temp-ausgabe-1);
      mem_realloc(ausgabe,0);
      return temp;
    }
  }
  return NULL;
}

#include "timeincl.h"

void set_time (void) {
  char* ack;
  time_t seconds;
  char timestr[20]; //6*2 + 7 + 1
	
  memset(timestr,0,sizeof(timestr));
	
  time(&seconds); //seconds since Unix
  //string formatting with conversion to local time
  strftime(timestr,sizeof(timestr),"\"%y/%m/%d,%H:%M:%S\"",localtime(&seconds));
  at_command_send(AT_GEN_TIME,timestr);
  ack = at_read_line();
  if (at_line_type(ack,NULL,NULL) == AT_RET_OK) {
    print_verbose(0,"%s\n",_("Time was synchronized"));
  } else {
    errexit("%s\n",_("Time could not be synchronized"));
  }
  mem_realloc(ack,0);
}

static int get_timezone_offset_quarters () {
  int h = 0;
  int m = 0;
  time_t temp = 0;
  struct tm* p = NULL;

  time(&temp);
  p = localtime(&temp);
  if (p != NULL) {
    h = p->tm_hour;
    m = p->tm_min;
    p = gmtime(&temp);
    if (p != NULL) {
      h -= p->tm_hour;
      m -= p->tm_min;
    } else h = m = 0;
  }
  return (h*4)+(m/15);
}

#include "smspdu.h"
struct tm* get_time (void) {
  char* ausgabe;
  struct sms_pdu_time t;
  struct tm* p = NULL;
  struct tm* ptemp = NULL;

  at_syntax_request(AT_GEN_TIME);
  ausgabe = at_get_value(AT_GEN_TIME);
  if (ausgabe != NULL) {
    //example: +CCLK: "04/06/10,02:30:37"
    sms_pdu_time_init(&t);
    sms_pdu_time_fill(&t,ausgabe,get_timezone_offset_quarters());
    p = mem_alloc(sizeof(*p),1);
    ptemp = localtime(&t.value);
    if (ptemp != NULL) memcpy(p,ptemp,sizeof(*p));
    return p;
  }
  return NULL;
}

char* get_simid (void) {
  char* ausgabe;

  ausgabe = at_get_vendor();
  if(ausgabe != NULL &&
     strcasecmp(ausgabe,"SIEMENS") == 0) {
    at_command_send(AT_SIE_CARD_ID,NULL);
    return at_get_value(AT_SIE_CARD_ID);
  }
  return NULL;
}

char* get_operator (void) {
  char* ausgabe;
  char* temp;

  at_syntax_request(AT_GEN_OPERATOR);
  ausgabe = at_get_value(AT_GEN_OPERATOR);
  if (ausgabe != NULL) {
    //example: +COPS: 0,0,"E-Plus"
    if ((temp=strchr(ausgabe,'"')) != NULL) {
      temp = strn_dup(temp+1,strlen(temp+1)-1);
      mem_realloc(ausgabe,0);
      return temp;
    }
    mem_realloc(ausgabe,0);
  }
  return NULL;
}

char* get_battery (void) {
  char* ausgabe;
  char* temp;
    
  at_command_send(AT_GEN_BATTERY,NULL);
  ausgabe = at_get_value(AT_GEN_BATTERY);
  if (ausgabe != NULL) {
    //example: +CBC: 0,80
    if ((temp=strchr(ausgabe+6,',')) != NULL) {
      temp=strn_dup(temp+1,strlen(temp+1));
      mem_realloc(ausgabe,0);
      return temp;
    }
    mem_realloc(ausgabe,0);
  }
  return NULL;
}

int get_signal (int* ber_p) {
  char* ausgabe;
  char* temp;
  int signal = -1;
  int b;
  

  if (ber_p != NULL) {
    *ber_p = -1;
  }    
  at_command_send(AT_GEN_SIGNAL,NULL);
  ausgabe = at_get_value(AT_GEN_SIGNAL);
  if (ausgabe != NULL) {
    //example: +CSQ: 28,99
    signal = atoi(ausgabe+6);
    if (signal == 99) {
      signal = -1;
    } else {
      if ((signal=111-((signal-1)*2)) < 0) {
	signal = -1;
      }
    }
    if (ber_p != NULL) {
      if ((temp=strchr(ausgabe,',')) != NULL) {
	b = atoi(temp+1);
	if (b == 7) {
	  *ber_p = 255;
	} else if (b >= 0 && b < 7) {
	  *ber_p = 2<<b;
	}
      }
    }
    mem_realloc(ausgabe,0);
  }
  return signal;
}

char* get_netstatus (unsigned int* areacode_p, unsigned int* cellid_p) {
  char* ausgabe;
  char* index1;
  char* index2;
  char buffer;

  if (areacode_p != NULL) *areacode_p=0;
  if (cellid_p != NULL) *cellid_p=0;

  at_command_send(AT_GEN_CSD_STATUS,"?");
  ausgabe = at_get_value(AT_GEN_CSD_STATUS);
  //example: +CREG: (0-2)
  if (ausgabe != NULL && strchr(ausgabe,'2') == NULL) {
    mem_realloc(ausgabe,0);
    at_syntax_request(AT_GEN_CSD_STATUS);
    ausgabe = at_get_value(AT_GEN_CSD_STATUS);
  } else {
    mem_realloc(ausgabe,0);
    /* Fix: firmware may forget to send LAC/IC (e.g. ME45 v23,S55)
     * Original was provided by Dominik Neubauer
     */
    at_command_send(AT_GEN_CSD_STATUS,"2");
    ausgabe = at_read_line();
    if (at_line_type(ausgabe,NULL,NULL) == AT_RET_OK) {
      mem_realloc(ausgabe,0);
      at_syntax_request(AT_GEN_CSD_STATUS);
      ausgabe = at_get_value(AT_GEN_CSD_STATUS);
      if (ausgabe != NULL) {
	at_command_send(AT_GEN_CSD_STATUS,"0");
	mem_realloc(at_read_line(),0);
      }
    } else {
      ausgabe = mem_realloc(ausgabe,0);
    }
  }
  //example: +CREG: 2,1,"0044","6E30"
  if (ausgabe != NULL) {
    index1 = strchr(ausgabe,'"');
    if (index1 != NULL) {
      index2 = strchr(++index1,'"');
      if (index2 != NULL) {
	if (areacode_p != NULL) {
	  *areacode_p = hexstr2int(index1,index2-index1);
	}
	index1 = strchr(index2+1,'"');
	if (index1 != NULL) {
	  index2 = strchr(++index1,'"');
	  if (index2 != NULL && cellid_p != NULL) {
	    *cellid_p=hexstr2int(index1,index2-index1);
	  }
	}
      }
    }
    if ((index1=strchr(ausgabe,',')) != NULL) {
      buffer = index1[1];
      mem_realloc(ausgabe,0);
      switch (buffer) {
      case '0':	return _("not checked in, not seeking");
      case '1':	return _("checked in");
      case '2':	return _("not checked in, but seeking a network");
      case '3':	return _("check-in denied by network");
      case '5':	return _("registered, roaming");
      default:	return _("unknown status");
      }
    }
  }
  return NULL;
}

char* get_gprs_status (unsigned int* areacode_p, unsigned int* cellid_p) {
  char* ausgabe;
  char* index1;
  char* index2;
  char buffer;

  if (areacode_p != NULL) *areacode_p=0;
  if (cellid_p != NULL) *cellid_p=0;

  at_syntax_request(AT_GEN_GPRS_STATUS);
  ausgabe = at_get_value(AT_GEN_GPRS_STATUS);
  if (ausgabe != NULL) {
    index1 = strchr(ausgabe,'"');
    if (index1 != NULL) {
      index2 = strchr(++index1,'"');
      if (index2!=NULL) {
	if (areacode_p!=NULL) {
	  *areacode_p = hexstr2int(index1,index2-index1);
	}
	index1 = strchr(index2+1,'"');
	if (index1 != NULL) {
	  index2 = strchr(++index1,'"');
	  if (index2 != NULL && cellid_p != NULL) {
	    *cellid_p = hexstr2int(index1,index2-index1);
	  }
	}
      }
    }
    if ((index1=strchr(ausgabe,',')) != NULL) {
      buffer = index1[1];
      mem_realloc(ausgabe,0);
      switch (buffer) {
      case '0':	return _("not registered, not searching");
      case '1':	return _("registered, home network");
      case '2':	return _("not registered, searching");
      case '3':	return _("registration denied by network");
      case '5':	return _("registered, roaming");
      default:	return _("unknown status");
      }
    }
  }
  return NULL;
}

int get_gprs_attach (void) {
  char* ausgabe;
  char buffer;

  at_syntax_request(AT_GEN_GPRS_ATTACH);
  ausgabe = at_get_value(AT_GEN_GPRS_ATTACH);
  if (ausgabe != NULL) {
    buffer = ausgabe[0];
    mem_realloc(ausgabe,0);
    switch (buffer) {
    case '0': return 0;
    case '1': return 1;
    }
  }
  return -1;
}

char* get_gprs_class (void) {
  char* ausgabe;
  char* temp;

  at_syntax_request(AT_GEN_GPRS_CLASS);
  ausgabe = at_get_value(AT_GEN_GPRS_CLASS);
  if (ausgabe != NULL) {
    //example: +CGCLASS: "B"
    if (ausgabe[0] == '"') {
      memmove(ausgabe,ausgabe+1,strlen(ausgabe)+1);
    }
    if ((temp=strchr(ausgabe,'"')) != NULL) {
      *temp = 0;
    }
    return ausgabe;
  }
  return NULL;
}

void info_misc (FILE* fd) {
  char *temp;
  int i = 0, j;
  unsigned int area = 0, cell = 0;
  struct tm* ltime;
  char** memlist;
  unsigned int fieldsize = 0;
  char* fields[19];
  fields[0] = _("Vendor");
  fields[1] = _("Model");
  fields[2] = _("Revision");
  fields[3] = "IMEI";
  fields[4] = _("Battery");
  fields[5] = _("Charsets");
  fields[6] = _("Time");
  fields[7] = "IMSI";
  fields[8] = _("card ID");
  fields[9] = _("Status");
  fields[10] = _("Area code");
  fields[11] = _("Cell ID");
  fields[12] = _("Operator");
  fields[13] = _("SMS server");
  fields[14] = _("Signal");
  fields[15] = "BER";
  fields[16] = _("GPRS class");
  fields[17] = _("GRPS status");
  fields[18] = NULL;

  while (fields[i] != NULL) {
    if (fieldsize < (size_t)str_width(fields[i])) fieldsize = str_width(fields[i]);
    ++i;
  }
  fieldsize += 2;

  fprintf(fd,"%s:\n",_("Phone related information"));
  fprintf(fd,"%s:%*s%s\n",fields[0],(int)(fieldsize-str_width(fields[0])),"",
	  temp=at_get_vendor());
  mem_realloc(temp,0);
  fflush(stdout);

  fprintf(fd,"%s:%*s%s\n",fields[1],(int)(fieldsize-str_width(fields[1])),"",
	  temp=at_get_model());
  mem_realloc(temp,0);
  fflush(stdout);

  fprintf(fd,"%s:%*s%s\n",fields[2],(int)(fieldsize-str_width(fields[2])),"",
	  temp=get_revision());
  mem_realloc(temp,0);
  fflush(stdout);

  fprintf(fd,"%s:%*s%s\n",fields[3],(int)(fieldsize-str_width(fields[3])),"",
	  temp=get_phoneserial());
  mem_realloc(temp,0);
  fflush(stdout);

  fprintf(fd,"%s:%*s%s%%\n",fields[4],(int)(fieldsize-str_width(fields[4])),"",
	  temp=get_battery());
  mem_realloc(temp,0);
  fflush(stdout);

  memlist=get_charsets();
  fprintf(fd,"%s:%*s",fields[5],(int)(fieldsize-str_width(fields[5])),"");
  if (memlist == NULL) {
    fprintf(fd,_("none"));
  } else {
    for (i = 0; memlist[i] != NULL; ++i) {
      if (i != 0) {
        fprintf(fd,", ");
      }
      fprintf(fd,"%s",memlist[i]);
    }
  }
  fprintf(fd,"\n");
  mem_realloc(*memlist,0);
  mem_realloc(memlist,0);
  fflush(stdout);

  temp=mem_alloc(81,1);
  ltime=get_time();
  if (ltime!=NULL && strftime(temp,80,"%c",ltime)) {
    fprintf(fd,"%s:%*s%s\n",fields[6],(int)(fieldsize-str_width(fields[6])),"",temp);
    mem_realloc(ltime,0);
  }
  mem_realloc(temp,0);
  fflush(stdout);



  fprintf(fd,"\n%s:\n",_("SIM card related information"));
  fprintf(fd,"%s:%*s%s\n",fields[7],(int)(fieldsize-str_width(fields[7])),"",
	  temp=get_simserial());
  mem_realloc(temp,0);
  fflush(stdout);

  if ((temp=get_simid()) != NULL) {
    fprintf(fd,"%s:%*s%s\n",fields[8],(int)(fieldsize-str_width(fields[8])),"",
	    temp);
  }
  mem_realloc(temp,0);
  fflush(stdout);



  fprintf(fd,"\n%s:\n",_("Network related information"));
  temp=get_netstatus(&area,&cell);
  if (temp!=NULL) {
    fprintf(fd,"%s:%*s%s\n",fields[9],(int)(fieldsize-str_width(fields[9])),"",
	    temp);
    if (area) {
      fprintf(fd,"%s:%*s%04X\n",fields[10],(int)(fieldsize-str_width(fields[10])),"",area);
      if (cell) fprintf(fd,"%s:%*s%04X\n",fields[11],(int)(fieldsize-str_width(fields[11])),"",cell);
    }
  }
  fflush(stdout);

  temp=get_operator();
  if (temp!=NULL) {
    fprintf(fd,"%s:%*s%s\n",fields[12],(int)(fieldsize-str_width(fields[12])),"",
	    temp);
  }
  mem_realloc(temp,0);
  fflush(stdout);

  temp=get_smsc();
  if (temp!=NULL) {
    fprintf(fd,"%s:%*s%s\n",fields[13],(int)(fieldsize-str_width(fields[13])),"",
	    temp);
  }
  mem_realloc(temp,0);
  fflush(stdout);

  if ((i=get_signal(&j)) >= 0) {
    fprintf(fd,"%s:%*s%d dBm\n",fields[14],(int)(fieldsize-str_width(fields[14])),"",-i);
    if (j >= 0) {
      if (j <= 128) {
	      fprintf(fd,"%s:%*s<=%d,%d%%\n",fields[15],(int)(fieldsize-str_width(fields[15])),"",j/10,j%10);
      } else {
	      fprintf(fd,"%s:%*s>12,8%%\n",fields[15],(int)(fieldsize-str_width(fields[15])),"");
      }
    }
  }
  fflush(stdout);

  temp=get_gprs_class();
  if (temp!=NULL) {
    fprintf(fd,"%s:%*s",fields[16],(int)(fieldsize-str_width(fields[16])),"");
    temp=(char *)strtok(temp,"()\",");
    while (temp!=0) {
      fprintf(fd,"%s",temp);
      temp=(char *)strtok(NULL,"()\",");
      if (temp!=0) {
	      fprintf(fd,", ");
      }
    }
    fprintf(fd,"\n");
  }
  mem_realloc(temp,0);
  fflush(stdout);

  temp=get_gprs_status(NULL,NULL);
  if (temp!=NULL) {
    fprintf(fd,"%s:%*s%s",fields[17],(int)(fieldsize-str_width(fields[17])),"",
	    temp);
    i=get_gprs_attach();
    if (i==0) {
      fprintf(fd,", %s\n",_("detached"));
    } else if (i==1) {
      fprintf(fd,", %s\n",_("attached"));
    }
  }
  fflush(stdout);
}

void info_mem (FILE* fd, int full) {
  unsigned int i = 0;
  unsigned int fieldsize = 0;
  char* fields[4];
  fields[0] = _("Binary files");
  fields[1] = _("Phonebooks");
  fields[2] = _("SMS storages");
  fields[3] = NULL;

  while (fields[i] != NULL) {
    if (fieldsize < (size_t)str_width(fields[i])) fieldsize = str_width(fields[i]);
    ++i;
  }
  fieldsize += 2;

  fprintf(fd,"%s:%*s",fields[0],(int)(fieldsize-str_width(fields[0])),"");
  file_print_memlist(fd,full);
  fprintf(fd,"%s:%*s",fields[1],(int)(fieldsize-str_width(fields[1])),"");
  pbook_print_memlist(fd,full);
  fprintf(fd,"%s:%*s",fields[2],(int)(fieldsize-str_width(fields[2])),"");
  sms_print_memlist(fd,full);
}

void info (char* file, int misc, int mem, int lock) {
  if (misc || mem || lock) {
    FILE* fd;
    if (!str_len(file) || !strcmp(file,"-")) {
      fd=stdout;
    } else {
      fd=fdopen(file_open_output(file),"w+");
    }
    if (misc) {
      info_misc(fd);
    }

    if (mem || misc) {
      if (misc || lock) fprintf(fd,"\n%s:\n",_("Available memories"));
      info_mem(fd,mem);
    }

    if (lock || misc) {
      if (misc || mem) fprintf(fd,"\n%s:\n",_("Settings"));
      lock_print_list(fd,lock);
    }

    if (str_len(file) && strcmp(file,"-")) {
      fclose(fd);
    }
  }
}

