/***************************************************************************
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

//own headers
#include <smspdu.h>

#include <helper.h>

//standard headers
#include <string.h>

void sms_pdu_message_status_init(struct sms_pdu_message_status* s)
{
  s->status = 0;
  sms_pdu_time_init(&s->time);
  s->message_parent = NULL;
  s->status_parent = NULL;
}

void sms_pdu_time_init (struct sms_pdu_time* this) {
  if (this != NULL) {
    this->format = SMS_PDU_TIME_NONE;
    this->value = 0;
  }
}

int sms_pdu_time_fill (struct sms_pdu_time* this, const char* s, int tz)
{
  struct tm* sct_tm;
  int status = 0;

  sct_tm = mem_alloc(sizeof(*sct_tm),1);
  /* mingw does not have strptime */
  status = sscanf(s,"\"%d/%d/%d,%d:%d:%d\"",
		  &sct_tm->tm_year,&sct_tm->tm_mon,&sct_tm->tm_mday,
		  &sct_tm->tm_hour,&sct_tm->tm_min,&sct_tm->tm_sec);
  if (status == 6)
  /*if (strptime(s,format,&sct_tm) != NULL)*/
  {
    if (sct_tm->tm_year < 70) sct_tm->tm_year += 100;
    sct_tm->tm_mon -= 1; //0 = january
    sct_tm->tm_yday = 0;
    sct_tm->tm_wday = 0;

    /* Not set by strptime(); tells mktime() to determine whether
     * daylight saving time is in effect
     */
    sct_tm->tm_isdst = -1;

    /* work-around for lazy *BSD implementation of strptime()
     * that does not set tm_yday in struct tm
     */
    tzset();
    this->value = mktime(sct_tm);
    mem_realloc(sct_tm,0);
    sct_tm = localtime(&this->value);
    
    /* mktime() does not work correctly for this, so we use the following from
     * http://www.opengroup.org/onlinepubs/007904975/basedefs/xbd_chap04.html#tag_04_14
     * (slightly modified)
     */
    this->value = sct_tm->tm_sec + sct_tm->tm_min*60 + sct_tm->tm_hour*3600;
    this->value += 86400 * (sct_tm->tm_yday
			    + (sct_tm->tm_year-70)*365
			    + (sct_tm->tm_year-69)/4
			    - (sct_tm->tm_year-1)/100
			    + (sct_tm->tm_year+299)/400);

    /* previous timezone is now GMT but value is still local time
     * apply timezone offset so that it really is GMT
     * NOTE: the phone's firmware does NOT do this
     *       and some SCTS are broken (unset TZ field)
     */
    if (tz <= 12*4) this->value -= (tz*15*60);

    this->format = SMS_PDU_TIME_ABSOLUTE;
    return 1;
  } else { //fallback for strptime failure
    mem_realloc(sct_tm,0);
    this->format = SMS_PDU_TIME_NONE;
    return 0;
  }
}

void sms_pdu_ud_header_init (struct sms_pdu_ud_header* s) {
  if (s == NULL) return;
  s->type = 0;
  s->len = 0;
  memset(s->data,0,sizeof(s->data));
}

void sms_pdu_ud_init (struct sms_pdu_ud* s) {
  if (s == NULL) return;
  s->header = NULL;
  s->text = NULL;
  s->ack = NULL;
}
void sms_pdu_ud_delete (struct sms_pdu_ud* s) {
  unsigned int i = 0;
  if (s == NULL) return;
  if (s->header != NULL) {
    while (s->header[i] != NULL) {
      mem_realloc(s->header[i],0);
      ++i;
    }
    mem_realloc(s->header,0);
  }
  mem_realloc(s->text,0);
}

void sms_pdu_options_init (struct sms_pdu_options* s) {
  s->type = SMS_TYPE_SUBMIT;
  s->udh_present = 0;
  s->u.submit.sr = 0;
  s->u.submit.rd = 0;
  s->u.submit.rp = 0;
}

void sms_pdu_dcs_init (struct sms_pdu_dcs* s) {
  s->type = SMS_DCS_TYPE_NONE;
  s->encoding = SMS_CHARSET_GSM;
}

void sms_pdu_init (struct sms_pdu* s) {
  if (s == NULL) return;
  sms_pdu_options_init(&s->options);
  gsm_number_init(&s->address);
  s->pid = 0;
  sms_pdu_dcs_init(&s->scheme);
  sms_pdu_time_init(&s->timedata);
  s->multipart_id = 0;
  s->partnum = 0;
  s->parts = 0;
  s->ud = NULL;
  s->extra.statusreport.status = NULL;
}
void sms_pdu_delete (struct sms_pdu* s) {
  unsigned int i = 0;
  if (s == NULL) return;
  gsm_number_delete(&s->address);
  if (s->ud != NULL) {
    while (i < s->parts) {
      sms_pdu_ud_delete(&s->ud[i++]);
    }
    mem_realloc(s->ud,0);
  }
  if (s->extra.statusreport.status != NULL &&
      (s->options.type == SMS_TYPE_STATUS_REPORT ||
       s->extra.statusreport.status->status_parent == NULL)) {
    mem_realloc(s->extra.statusreport.status,0);
  }
}

void sms_tpdu_init (struct sms_tpdu* s) {
  if (s == NULL) return;
  gsm_number_init(&s->sca);
  s->pdu = NULL;
}
void sms_tpdu_delete (struct sms_tpdu* s) {
  if (s == NULL) return;
  gsm_number_delete(&s->sca);
  if (s->pdu != NULL) {
    sms_pdu_delete(s->pdu);
    mem_realloc(s->pdu,0);
  }
}

void sms_pdu_raw_init (struct sms_pdu_raw* this) {
  if (this == NULL) return;
  this->size = 0;
  memset(this->data,0xff,sizeof(this->data));
}

void sms_pdu_raw_append (struct sms_pdu_raw* this,
			 uint8_t* data, size_t len) {
  if (this->size+len > sizeof(this->data)) len = sizeof(this->data)-this->size;
  memcpy(this->data+this->size,data,len);
  this->size += len;
}

void sms_pdu_raw_from_hexstring (struct sms_pdu_raw* this,
				 char* hexstring)
{
  size_t i = 0;

  if (this == NULL) return;
  this->size = str_len(hexstring)/2;
  if (this->size > sizeof(this->data)) this->size = sizeof(this->data);
  for (; i < this->size; ++i)
    this->data[i] = hexstr2int(hexstring+2*i,2);
}

char* sms_pdu_raw_to_hexstring (struct sms_pdu_raw* this) {
  char* retval;
  size_t i = 0;

  if (this == NULL) return NULL;
  if (this->size > sizeof(this->data)) this->size = sizeof(this->data);
  retval = mem_alloc(this->size*2+1,1);
  for (; i < this->size; ++i)
    sprintf(retval+2*i,"%02X",this->data[i]);
  return retval;
}

void sms_slot_data_init (struct sms_slot_data* this) {
  if (this == NULL) return;
  this->slot = SMS_SLOT_INVALID;
  this->dir = SMS_OUTGOING;
  sms_pdu_raw_init(&this->tpdu);
}

void sms_init (struct sms* s,
	       struct sms_slot_data* encoded,
	       struct sms_tpdu* decoded)
{
  unsigned int i;
  unsigned int p = 2;
  if (s == NULL || encoded == NULL ||
      decoded == NULL || decoded->pdu == NULL) {
    return;
  }
  if (decoded->pdu->parts > 0) p = decoded->pdu->parts+1;
  s->encoded = mem_alloc(p*sizeof(*(s->encoded)),0);
  for (i = 0; i < p; ++i) {
    s->encoded[i] = NULL;
  }
  s->encoded[decoded->pdu->partnum] = encoded;
  s->decoded = decoded;
}
void sms_delete (struct sms* s) {
  unsigned int i = 0;
  if (s == NULL) return;
  if (s->encoded != NULL) {
    while (s->encoded[i] != NULL) {
      mem_realloc(s->encoded[i],0);
      ++i;
    }
    mem_realloc(s->encoded,0);
  }
  if (s->decoded != NULL) {
    sms_tpdu_delete(s->decoded);
    mem_realloc(s->decoded,0);
  }
}
