/***************************************************************************
 *   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 <helper.h>
#include <stdio.h>
#include <string.h>

uint8_t numtype (const char* number) {
  //see ETSI 23.040, Ch. 9.1.2.5
  if (str_len(number) == 0 ||
      *number != '+') {
    return 0x81;
  } else {
    return 0x91;
  }
}

void gsm_number_init (struct gsm_number* s) {
  if (s == NULL) return;
  s->toa.type = GSM_NUMBER_TYPE_UNKNOWN;
  s->toa.plan = GSM_NUMBER_PLAN_UNKNOWN;
  memset(s->digits,0,GSM_NUMBER_DIGITS_LEN);
  s->text = NULL;
}

void gsm_number_delete (struct gsm_number* s) {
  if (s == NULL) return;
  mem_realloc(s->text,0);
}

int gsm_number_compare (const struct gsm_number* s1, 
			const struct gsm_number* s2)
{
  if (s1->toa.type == s2->toa.type &&
      (str_len(s1->digits) != 0 ||
       str_len(s2->digits) != 0 ||
       strcmp(s1->digits,s2->digits) == 0) &&
      ucs4cmp(s1->text,s2->text) == 0) {
    return 1;
  } else {
    return 0;
  }
}

uint8_t gsm_number_get_toa (const struct gsm_number* this) {
  if (this == NULL) return 0x80;
  else  return (0x80+(this->toa.type<<4)+this->toa.plan);
}

void gsm_number_set_toa (struct gsm_number* this, uint8_t toa) {
  if (this == NULL) return;
  this->toa.type = ((toa>>4)&0x7);
  this->toa.plan = toa&0xF;
}

void gsm_number_set_semioctets (struct gsm_number* this,
				uint8_t toa,
				uint8_t* semioctets,
				size_t len)
{
  char* map = "0123456789*#+??\0";
  uint8_t i = 0;

  if (this == NULL || (semioctets == NULL && len)) return;
  gsm_number_set_toa(this,toa);
  if (2*len > GSM_NUMBER_DIGITS_LEN) len = GSM_NUMBER_DIGITS_LEN/2;
  for (; i < len; ++i) {
    this->digits[2*i] = map[semioctets[i]&0xF];
    if (((semioctets[i]>>4)&0xF) != 0xF)
      this->digits[2*i+1] = map[(semioctets[i]>>4)&0xF];
  }
}

size_t gsm_number_get_semioctets (struct gsm_number* this, uint8_t* out) {
  size_t len = strlen(this->digits);
  unsigned int i = 0;
  char* map = "0123456789*#+?";
  char* m;
  int off = 0;

  memset(out,0,strlen(this->digits));
  for (;i < len; ++i) {
    m = strchr(map,(int)this->digits[i]);
    if (m == NULL) {
      break;
    } else out[i/2] |= (m-map)<<off;
    /* toggle between upper and lower half-byte */
    off = 4 - off;
  }
  if (off) {
    out[i/2] |= 0xF<<off;
    ++len;
  }
  return len/2;
}

void gsm_number_set (struct gsm_number* this,
			   const char* number,
			   size_t len)
{
  size_t off = 0;

  if (this == NULL || number == NULL) return;
  if (len > 0) {
    if (number[len-1] == '"') --len;
    if (number[off] == '"') ++off;
    if (len < off) return;
    if (number[off] == '+') {
      this->toa.type = GSM_NUMBER_TYPE_INTERNAT;
      ++off;
    }
    len -= off;
    if (len > GSM_NUMBER_DIGITS_LEN) len = GSM_NUMBER_DIGITS_LEN;

    memset(this->digits,0,sizeof(this->digits));
    strncpy(this->digits,number+off,len);

    if (is_number(this->digits) == 0)
      memset(this->digits,0,sizeof(this->digits));
  }
}

char* gsm_number_get (const struct gsm_number* this) {
  char* retval = NULL;
  unsigned int length;

  if (this == NULL) return NULL;
  length = str_len(this->digits);
  if (length == 0) return NULL;
  if (this->toa.type == GSM_NUMBER_TYPE_TEXT) return NULL;
  if (this->toa.type == GSM_NUMBER_TYPE_INTERNAT) ++length;
  retval = mem_alloc(length+1,1);
  snprintf(retval,length+1,"%s%s",
	   (this->toa.type == GSM_NUMBER_TYPE_INTERNAT)? "+" : "",
	   this->digits);
  return retval;
}

