/***************************************************************************
 *   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 <atcommand.h>
#include <helper.h>
#include <gtincl.h>
#include <pinfile.h>
#include <w32compat.h>

#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <strings.h>
#include <errno.h>

static char* at_last_command;
static char* at_last_parameter;
static int   at_last_was_syntax_request;
static char* PIN;
static struct at_hw_access at_tty;

void at_init (struct at_hw_access* tty, char* pin) {
  at_last_command = NULL;
  at_last_parameter = NULL;
  at_last_was_syntax_request = 0;
  PIN = str_dup(pin);
  at_set_tty(tty);
}

void at_set_tty(struct at_hw_access* tty) {
  at_tty.read = tty->read;
  at_tty.write = tty->write;
}

int at_check_line_end (const char* input, const size_t len) {
  return ((len >= 2 && input[len-2] == '\r' && input[len-1] == '\n') ||
	  (input[0] == '>' && len > 1));
}

char* at_read_line_raw () {
  char* value;
  char* temp;
  int repeat;
  int estatus;

  value = NULL;
  do {
    repeat = 0;
    value = at_tty.read(at_check_line_end);
    if (str_len(value) == 0) {
      if (value == NULL) {
        estatus = errno;
      } else {
        estatus = ETIMEDOUT;
      }
      errexit("%s: %s\n",_("Cannot read from device"),strerror(estatus));
    } else {
      if ((temp=strchr(value,'\r')) != NULL ||
	  (temp=strchr(value,'\n')) != NULL) {	
	//we do not want values 13=<CR>='\r' and 10=<LF>='\n' in our strings
	memset(temp,0,1);
	//we do not want to return empty strings
	//we do not accept the at_command_p as answer
	if (strlen(value) == 0 ||
	    (strncmp(value,AT_PREFIX,sizeof(AT_PREFIX)-1) == 0 &&
	     strncmp(value+sizeof(AT_PREFIX)-1,
		     at_last_command,
		     str_len(at_last_command)) == 0)) {
	  value = mem_realloc(value,0);
	  repeat = 1;
	}
      }
    }
  } while (repeat);
  print_message(2,_("Received"),"%s\n",value);
  return value;
}

char* at_read_line () {
  char* value;
  char* temp;
  char* ctemp;
  char* ptemp;
  char* pintype;
  int srtemp;
  
  value = at_read_line_raw();
  
  //PIN handling for all function is done transparently here
  if (at_line_type(value,NULL,&temp) == AT_RET_ERROR_CME) {
    if (strstr(temp,"PIN") != NULL ||
	strstr(temp,"PUK") != NULL) {
      //save previous command
      ctemp = str_dup(at_last_command);
      ptemp = str_dup(at_last_parameter);
      srtemp = at_last_was_syntax_request;
      
      /* On first PIN usage, the command line option overrides
       * pin file usage, subsequent request are always answered
       * by pin file.
       */
      if (str_len(PIN) == 0) {
        at_syntax_request(AT_GEN_PIN);
	pintype = at_read_line_raw();
	if (at_line_type(pintype,AT_GEN_PIN,&temp) == AT_RET_ANSWER)
	  mem_realloc(at_read_line_raw(),0);	
	  PIN = pinfile_get(temp);
	mem_realloc(pintype,0);
      }
      if (str_len(PIN) == 0) {
	//fail if pin file lookup failed and nothing was specified at command line
	ctemp = mem_realloc(ctemp,0);
	ptemp = mem_realloc(ptemp,0);
	if (PIN != NULL) mem_realloc(PIN,0);
	at_line_type(value,NULL,&temp);
	errexit("%s\n%s\n",temp,_("Use the --pin parameter or edit the pin file."));
      } else {
        //resend command to be sure to be allowed to enter the pin
	if (srtemp) at_syntax_request(ctemp);
	else at_command_send(ctemp,ptemp);
	mem_realloc(at_read_line_raw(),0);

	//enter the pin
	at_gen_pin(PIN);
	mem_realloc(value,0);
	value = at_read_line_raw();
	if (at_line_type(value,AT_GEN_PIN,&temp) != AT_RET_OK)
	  errexit("%s: %s\n",_("Using PIN failed"),temp);
	mem_realloc(value,0);

	//prevent a loop, use pin file for multiple usages
	PIN = mem_realloc(PIN,0);

	//resend previous command
	if (srtemp) at_syntax_request(ctemp);
	else at_command_send(ctemp,ptemp);
	ctemp = mem_realloc(ctemp,0);
	ptemp = mem_realloc(ptemp,0);
	value = at_read_line_raw();
      }
    }
  } else if (strncmp(value,"RING",4) == 0 ||
	     strncmp(value,"NO CARRIER",10) == 0) {
    mem_realloc(value,0);
    value = at_read_line();
  }
  return mem_realloc(value,strlen(value)+1);
}

void at_command_send (const char* command, const char* parmlist) {
  if (command == NULL) return;

  print_message(2,_("\nSending command"),"%s%s",AT_PREFIX,command);
  if (str_len(parmlist) > 0) {
    print_verbose(2,"%s%s\n",AT_WRITE_DELIM,parmlist);
  } else {
    print_verbose(2,"%s","\n");
  }

  if (at_tty.write(AT_PREFIX,sizeof(AT_PREFIX)-1) == 0 ||
      at_tty.write(command,strlen(command)) == 0 ||
      (str_len(parmlist) != 0 && 
       (at_tty.write(AT_WRITE_DELIM,sizeof(AT_WRITE_DELIM)-1) == 0 ||
	at_tty.write(parmlist,strlen(parmlist)) == 0)) ||
      at_tty.write("\r",1) == 0) {
    errexit("%s: %s\n",_("Cannot send at command"),strerror(errno));
  } else {
    at_last_command = mem_realloc(at_last_command,0);
    at_last_command = str_dup(command);
    at_last_parameter = mem_realloc(at_last_parameter,0);
    at_last_parameter = str_dup(parmlist);
    at_last_was_syntax_request = 0;
  }
}

void at_syntax_request (const char* command) {
  if (command == NULL) return;

  print_message(2,_("\nSending command"),"%s%s%s\n",
	   AT_PREFIX, command, AT_READ_SUFFIX);
  if (at_tty.write(AT_PREFIX,sizeof(AT_PREFIX)-1) == 0 ||
      at_tty.write(command,strlen(command)) == 0 ||
      at_tty.write(AT_READ_SUFFIX,strlen(AT_READ_SUFFIX)) == 0 ||
      at_tty.write("\r",1) == 0) {
    errexit("%s: %s\n",_("Cannot send at command"),strerror(errno));
  } else {
    at_last_command = mem_realloc(at_last_command,0);
    at_last_command = str_dup(command);
    at_last_parameter = mem_realloc(at_last_parameter,0);
    at_last_was_syntax_request = 1;
  }
}

void at_data_send (const char* data, size_t size) {
  /* 0x1a is Ctrl-Z (represents end of data stream) */
  if (at_tty.write(data,size))
    at_tty.write("\x1a",1);
}


char* at_get_value (const char* command) {
  char* temp;
  char* temp2;

  temp = at_read_line();
  if (temp == NULL) return NULL;
  switch (at_line_type(temp,command,&temp2)) {
  case AT_RET_ANSWER:
    memmove(temp,temp2,strlen(temp2)+1);
    //no break
  case AT_RET_OTHER:
    temp2 = at_read_line();
    if (at_line_type(temp2,NULL,NULL) == AT_RET_OK) {
      mem_realloc(temp2,0);
      return temp;
    }
    mem_realloc(temp2,0);
    //no break
  default:
    mem_realloc(temp,0);
    return NULL;
  }
}


char** at_parse_stringlist (char* stringlist) {
  char* temp = stringlist;

  if (*temp == '(') ++temp;
  if (*temp == '"') ++temp;
  memmove(stringlist,temp,strlen(temp)+1);
  temp = stringlist+strlen(stringlist)-1;
  if (*temp == ')') --temp;
  if (*temp != '"') ++temp;
  memset(temp,0,1);
  return str_split(stringlist,"\",\"",0);
}

enum return_code at_line_type (const char* response,
			       const char* command,
			       char** content)
{
  char* temp = NULL;
  enum return_code retval = AT_RET_OTHER;

  if (content != NULL) *content = (char*)response;
  if (response == NULL) return retval;
  if (strcasecmp(response,AT_OK) == 0) {
    temp = "OK";
    retval = AT_RET_OK;
  } else if (strcasecmp(response,AT_ERROR) == 0) {
    temp = "ERROR";
    retval = AT_RET_ERROR;
  } else if (strncasecmp(response,AT_ERROR_CME,
			 str_len(AT_ERROR_CME)) == 0) {
    temp = "CME ERROR";
    if (content != NULL) *content = (char*)response + str_len(AT_ERROR_CME);
    retval =  AT_RET_ERROR_CME;
  } else if (strncasecmp(response,AT_ERROR_CMS,
			 str_len(AT_ERROR_CMS)) == 0) {
     temp = "CMS ERROR";
    if (content != NULL) *content = (char*)response + str_len(AT_ERROR_CMS);
    retval = AT_RET_ERROR_CMS;
  } else if (command != NULL &&
	     strncasecmp(response,command,strlen(command)) == 0 &&
	     strncasecmp(response+str_len(command),
			 AT_REPLY_SEPERATOR,
			 str_len(AT_REPLY_SEPERATOR)) == 0) {
    temp = _("command return value");
    if (content != NULL) {
      *content = (char*)response + str_len(command) + str_len(AT_REPLY_SEPERATOR);
    }
    retval = AT_RET_ANSWER;
  } else {
    temp = _("nothing");
    retval =  AT_RET_OTHER;
  }
  print_verbose(3,_("Value matched %s.\n"),temp);
  return retval;
}
