/*
 *  public/uwav/number.c
 *
 *  Copyright (C) 2008  ADC, Inc
 *  Written by: Xinyu Yan <xinyu.yan@adc.com>
 *
 */
#include <string.h>
#include "./number.h"

/*
ITU:
	0-spare
	1-subscriber
	2-unknown
	3-NDD
	4-IDD
	
GSM 09.02:
-- bits 765: nature of address indicator
-- 000 unknown
-- 001 international number
-- 010 national significant number
-- 011 network specific number
-- 100 subscriber number
-- 101 reserved
-- 110 abbreviated number
-- 111 reserved for extension
*/

static const u8 NAI_GSM2ITU[8] = {0x02, 0x04, 0x03, 0x03, 0x01, 0x02, 0x02, 0x02};
static const u8 NAI_ITU2GSM[8] = {0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00};
extern void  AsciiToBcd (BYTE *bcd_buf, const char *ascii_buf, int len);
u8 nai_gsm_to_itu(u8 nai)
{
	return NAI_GSM2ITU[nai & 0x07];
}

u8 nai_itu_to_gsm(u8 nai)
{
	return NAI_ITU2GSM[nai & 0x07];
}

void num_itu_to_gsm(num32_t *gsm_num, const num32_t *itu_num)
{
	int i;

	gsm_num->num_of_digit = itu_num->num_of_digit;

	for(i = 0; i < itu_num->num_of_digit; i++)
	{
		if(itu_num->digit[i] == 0x0b)
			gsm_num->digit[i] = 0x0a;
		else if(itu_num->digit[i] == 0x0c)
			gsm_num->digit[i] = 0x0b;
		else
			gsm_num->digit[i] = itu_num->digit[i];
	}
}

u8 *msc_isdn_to_bcd(u8 *bcd, const isdn_t *isdn)
{
	int i;
	const num32_t *num = &isdn->num;
	u8 *bcdptr;
	
	bcd[0] = (num->num_of_digit+1)/2 + 1;
	bcd[1] = 0x81 | (nai_itu_to_gsm(isdn->nai) << 4);
	bcdptr = bcd + 2;
	for(i = 0; ((i < 32) && (i < num->num_of_digit)); i++)
	{
		if((i & 1) == 0)
			*bcdptr = (num->digit[i] << 4) | 0x0f;
		else
		{
			*bcdptr = (*bcdptr & 0xf0) + num->digit[i];
			bcdptr++;
		}
	}
	return bcd;
}

isdn_t *netbcd_to_isdn(isdn_t *isdn, const u8 *bcd)
{
	u8 bcd_digits;
	u8 digit;
	num32_t *num = &isdn->num;

	if(bcd[0] < 1)
	{
		num->num_of_digit = 0;
		return isdn;
	}
	bcd_digits = (bcd[0]-1) * 2;
	isdn->nai = nai_gsm_to_itu((bcd[1] >> 4) & 0x07);
	bcd += 2;
	for(num->num_of_digit = 0; (num->num_of_digit<32)&&(num->num_of_digit<bcd_digits); num->num_of_digit++)
	{
		if((num->num_of_digit & 1) == 0)
			digit = *bcd & 0x0f;
		else
			digit = *(bcd++) >> 4;
		if(is_dail_digit(digit))
			num->digit[num->num_of_digit] = digit;
		else
			break;
	}
	return isdn;
}

u8 *isdn_to_netbcd(u8 *bcd, const isdn_t *isdn)
{
	int i;
	const num32_t *num = &isdn->num;
	u8 *bcdptr;
	
	bcd[0] = (num->num_of_digit+1)/2 + 1;
	bcd[1] = 0x81 | (nai_itu_to_gsm(isdn->nai) << 4);
	bcdptr = bcd + 2;
	for(i = 0; ((i < 32) && (i < num->num_of_digit)); i++)
	{
		if((i & 1) == 0)
			*bcdptr = 0xf0 + (num->digit[i] & 0x0f);
		else
		{
			*bcdptr = (num->digit[i] << 4) | (*bcdptr & 0x0f);
			bcdptr++;
		}
	}
	return bcd;
}

/* TBCD: Telephony Binary Coded Decimal (GSM 09.02) */
int tbcd_to_string(char *str, const u8 *tbcd, u8 tbcd_len)
{
	int i;
	u8 max_digits;
	u8 digit;

	max_digits = tbcd_len * 2;
	for(i = 0; i < max_digits; i++)
	{
		if((i & 1) == 0)
			digit = *tbcd & 0x0f;
		else
			digit = *(tbcd++) >> 4;
		if(is_dail_digit(digit))
			digit_to_char(str[i], digit);
		else
			break;
	}
	str[i] = 0;
	return i;
}

isdn_t *tpaddr_to_isdn(isdn_t *isdn, const u8 *tpaddr)
{
	u8 bcd_digits;
	u8 digit;
	num32_t *num = &isdn->num;

	bcd_digits = tpaddr[0];
	isdn->nai = nai_gsm_to_itu((tpaddr[1] >> 4) & 0x07);
	tpaddr += 2;
	for(num->num_of_digit = 0; (num->num_of_digit<32)&&(num->num_of_digit<bcd_digits); num->num_of_digit++)
	{
		if((num->num_of_digit & 1) == 0)
			digit = *tpaddr & 0x0f;
		else
			digit = *(tpaddr++) >> 4;
		if(is_dail_digit(digit))
			num->digit[num->num_of_digit] = digit;
		else
			break;
	}
	return isdn;
}

int string_to_num32(num32_t *num, const char *str)
{
	u8 digit;
	
	for(num->num_of_digit = 0; num->num_of_digit < 32; num->num_of_digit++, str++)
	{
		if((*str >= '0') && (*str <= '9'))
			digit = *str - '0';
		else if(*str == '*')
			digit = 0x0b;
		else if(*str == '#')
			digit = 0x0c;
		else
			break;
		num->digit[num->num_of_digit] = digit;
	}
	return num->num_of_digit;
}

int num32_to_string(char *str, const num32_t *num)
{
	int i;
	
	for(i = 0; ((i < 32) && (i < num->num_of_digit)); i++)
	{
		if(num->digit[i] <= 9)
			str[i] = num->digit[i] + '0';
		else if(num->digit[i] == 0x0b)
			str[i] = '*';
		else if(num->digit[i] == 0x0c)
			str[i] = '#';
	}
	str[i] = 0;
	return i;
}

int string_to_num16(num16_t *num, const char *str)
{
	u8 digit;
	
	for(num->num_of_digit = 0; num->num_of_digit < 16; num->num_of_digit++, str++)
	{
		if((*str >= '0') && (*str <= '9'))
			digit = *str - '0';
		else if(*str == '*')
			digit = 0x0b;
		else if(*str == '#')
			digit = 0x0c;
		else
			break;
		num->digit[num->num_of_digit] = digit;
	}
	return num->num_of_digit;
}

int num16_to_string(char *str, const num16_t *num)
{
	int i;
	
	for(i = 0; ((i < 16) && (i < num->num_of_digit)); i++)
	{
		if(num->digit[i] <= 9)
			str[i] = num->digit[i] + '0';
		else if(num->digit[i] == 0x0b)
			str[i] = '*';
		else if(num->digit[i] == 0x0c)
			str[i] = '#';
	}
	str[i] = 0;
	return i;
}

int string_to_num8(num8_t *num, const char *str)
{
	u8 digit;
	
	for(num->num_of_digit = 0; num->num_of_digit < 8; num->num_of_digit++, str++)
	{
		if((*str >= '0') && (*str <= '9'))
			digit = *str - '0';
		else if(*str == '*')
			digit = 0x0b;
		else if(*str == '#')
			digit = 0x0c;
		else
			break;
		num->digit[num->num_of_digit] = digit;
	}
	return num->num_of_digit;
}

int num8_to_string(char *str, const num8_t *num)
{
	int i;
	
	for(i = 0; ((i < 8) && (i < num->num_of_digit)); i++)
	{
		if(num->digit[i] <= 9)
			str[i] = num->digit[i] + '0';
		else if(num->digit[i] == 0x0b)
			str[i] = '*';
		else if(num->digit[i] == 0x0c)
			str[i] = '#';
	}
	str[i] = 0;
	return i;
}

int digit2netbcd(u8 *netbcd, u8 *digit, u8 max_digit)
{
	int i;

	for(i = max_digit-1; i >= 0; i--)
	{
		if((i & 1) == 0)
		{
			*netbcd = (*netbcd & 0xf0) | (digit[i] & 0x0f);
			netbcd++;
		}
		else
		{
			*netbcd = (digit[i] << 4) + 0x0f;
		}
	}
	return (max_digit+1)/2;
}

int netbcd2digit(u8 *digit, u8 *netbcd, u8 max_digit)
{
	int i;
	u8 d;

	//for(i = 0; i < max_digit; i++)
	//{
	//	if((i & 1) == 0)
	//		d = *netbcd & 0x0f;
	//	else
	//		d = *(netbcd++) >> 4;
	//	
	//	digit[i] = d;
	//}
	for(i = 0; i < max_digit; i++)
	{
		if((i & 1) == 0)
			d = *netbcd >> 4;
		else
			d = *(netbcd++) & 0x0f;
		
		digit[max_digit-1-i] = d;
	}
	
	return i;
}

int digit_to_netbcd(u8 *netbcd, u8 *digit, u8 max_digit)
{
	int i;

	for(i = 0; i < max_digit; i++)
	{
		if((i & 1) == 0)
			*netbcd = 0xf0 + (digit[i] & 0x0f);
		else
		{
			*netbcd = (digit[i] << 4) | (*netbcd & 0x0f);
			netbcd++;
		}
	}
	return (max_digit+1)/2;
}

int netbcd_to_digit(u8 *digit, u8 *netbcd, u8 max_digit)
{
	int i;
	u8 d;

	for(i = 0; i < max_digit; i++)
	{
		if((i & 1) == 0)
			d = *netbcd & 0x0f;
		else
			d = *(netbcd++) >> 4;
		
		if(d != 0x0f)
			digit[i] = d;
		else
			break;
	}
	return i;
}

void msc_save_lai(u8 *lai, u8 *mcc, u8 *mnc, int lac)
{
	lai[0] = (mcc[1] << 4) + (mcc[0] & 0x0f);
	lai[1] = (mnc[2] << 4) | mcc[2]; 
	lai[2] = (mnc[1] << 4) + (mnc[0] & 0x0f);
	lai[3] = lac >> 8;
	lai[4] = lac;
}

void msc_load_lai(u8 *mcc, u8 *mnc, int *lac, u8 *lai)
{
	mcc[0] = lai[0] & 0x0f;
	mcc[1] = lai[0] >> 4;
	mcc[2] = lai[1] & 0x0f;
	mnc[0] = lai[2] & 0x0f;
	mnc[1] = lai[2] >> 4;
	mnc[2] = lai[1] >> 4;
	*lac = (lai[3] << 8) + lai[4];
}


/*	+++++++++++++++++++++++++++++++++++++++++++++++++	*/
/*	transfer ISDN format from ascii string to normal	*/
/*	"8675557127001" --> 918675557127001EEE				*/
/*	return value: 0--string has error; 1--success		*/
/*	+++++++++++++++++++++++++++++++++++++++++++++++++	*/
#define ISDN_LEN 9

u8 isdn_ston(u8 *normal_isdn,u8 *str_isdn)
{
	u8		ii;
	u8		len;

	len = strlen((char *)str_isdn);
	if (len > ISDN_LEN*2-2)
		return 0;
	if ((len % 2) == 1)		// odd number
	{
		AsciiToBcd(normal_isdn+1, (char *)str_isdn,len-1);
		ii = len/2 + 1;
		normal_isdn[ii] = (str_isdn[len-1] - '0') << 4;
		normal_isdn[ii] |= 0x0E;
	}
	else
	{
		AsciiToBcd(normal_isdn+1, (char *)str_isdn,len);
		ii = len/2;
	}
	memset(normal_isdn+ii+1,0xEE,ISDN_LEN-ii-1);
	normal_isdn[0] = 0x91;	// default value
	return 1;
}

void isdn_ntoa(u8 * anti_isdn, u8 * normal_isdn)
{
     u8   ii;
     u8   aa = 0, bb = 0;
     u8   len;

     anti_isdn[1] = normal_isdn[0];     // nature of address and numbering plan indicator
     len = 1;
     for(ii = 1; ii < ISDN_LEN; ii++)
     {
          if(normal_isdn[ii] == 0xee)
               break;
          else
          {
               aa = (normal_isdn[ii] & 0x0f0) >> 4;
               bb = normal_isdn[ii] & 0x0f;
               if(bb > 0x0c)
                    bb = 0x0f;
               anti_isdn[ii + 1] = (bb << 4) + aa;
               len++;
          }
     }
     anti_isdn[0] = len;
}

#if 0
/* return the digit counts of number */
static int bcd_to_num16(num16_t *num, const u8 *bcd)
{
	u8 digit;
	
	for(num->num_of_digit = 0; num->num_of_digit < 16; num->num_of_digit++)
	{
		if((num->num_of_digit & 1) == 0)
			digit = *bcd >> 4;
		else
			digit = *(bcd++) & 0x0f;
		if(is_dail_digit(digit))
			num->digit[num->num_of_digit] = digit;
		else
			break;
	}
	return num->num_of_digit ;
}

/* return the length of bcd */
static int num16_to_bcd(u8 *bcd, const num16_t *num)
{
	int i;
	
	for(i = 0; ((i < 16) && (i < num->num_of_digit)); i++)
	{
		if((i & 1) == 0)
			*bcd = (num->digit[i] << 4) | 0x0f;
		else
			*(bcd++) = (*bcd & 0xf0) + num->digit[i];
	}
	return (i+1)/2;
}
#endif

