#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>

#include "./include/pstn_client.h"
#include "../tcp/src/include/tcp_if.h"

#define  CLIENT_CFG	"./conf/pstn_client.cfg"
#define  DSTN_CFG	"./conf/dstn_entry.cfg"


_pstn_server_param_t  pstn_cfg;
static int sw_tcp_sap_id, msan_tcp_sap_id;
static _pstn_procs_t  pstn_procs;
static 	int   dstn_entry = 14;

int pstn_save_msan_slot_info(char *record_ptr)
{
	const  char		delimiter[]=",";
	char			buf[32]={""},*token;
	int index=0;
	int pair_group_id, msan_id, slot_id;
	_pair_2_slot_param_t *pair_ptr=NULL;

   	token=buf;

	while(index<3)
	{
		token = strsep((char **)&(record_ptr),delimiter);
		if(token == NULL)
		{
			index++;
			continue;
		}
		strcpy(buf,token);
		switch(index)
		{
			case 0x00: 
				pair_group_id = atoi(buf);
			break;
			case 0x01:
				msan_id = atoi(buf)-1;
			break;
			case 0x02:
				slot_id = atoi(buf);
			break;						
		}
		index++;
	}

	if(pair_group_id>=MAX_SLOTS || msan_id>=2 || slot_id>=MAX_SLOTS)
		return 0;
	pair_ptr = &pstn_cfg.pair_slot_info[pair_group_id];
	pair_ptr->used = 1;
	pair_ptr->pair_group = pair_group_id;
	pair_ptr->msan_id = msan_id;
	pair_ptr->slot_id = slot_id;	

	return 1;
}

int pstn_client_read_conf()
{
	char s[80],s1[80];
	int  len;
	unsigned char conf_state=0xFF;
	_msan_param_t *msan_ptr=NULL;
	_pair_2_slot_param_t *pair_ptr=NULL;
  	FILE *fpConf;


	
	read_dstn_entry();
	
    	memset(&pstn_cfg, 0x00, sizeof(_pstn_server_param_t));
 	pstn_cfg.krone_pair_id_in_crm = 1046;
	pstn_cfg.ss_entry_id_in_crm = 1049;

	fpConf = fopen(CLIENT_CFG,"rb");
	
	if(fpConf == NULL)
	{
		return 0;
	}

	strcpy(s,"");
	strcpy(s1,"");
	while(fgets(s,1024,fpConf) !=(char *)0)
	{
		if( (int *)strchr(s,'#') !=NULL) continue;
		if( !strlen(s) ) continue;      
		len = strlen(s);
		if(len < 2) continue;
		
		if(s[len-1] == 0x0d ||s[len-1] == 0x0a)
		{
			if(s[len-2] == 0x0d ||s[len-2] == 0x0a)
			{
				s[len-2] = 0;
				len -= 2;			
			}
			else
			{
				s[len-1] = 0;
				len -= 1;
			}
		}

		if(!strncasecmp(s,"[server]",8))
		{
			conf_state = 1;
			continue;
		}
		if(!strncasecmp(s,"[MSAN-0]",8))
		{
			conf_state = 2;
			msan_ptr = &pstn_cfg.msan[0];
			msan_ptr->server_enable = 1;
			continue;
		}
		if(!strncasecmp(s,"[MSAN-1]",8))
		{
			conf_state = 3;
			msan_ptr = &pstn_cfg.msan[1];
			msan_ptr->server_enable = 1;
			continue;
		}
		if(!strncasecmp(s,"[Pairs-Slot]",12))
		{
			conf_state = 4;
			continue;
		}
		if(!strncasecmp(s,"[resources]",11))
		{
			conf_state = 5;
			continue;
		}

		switch(conf_state)
		{	
			case 1:
				if(strncasecmp(s,"local_ip=",9)==0)
	        		{
	        			strcpy(pstn_cfg.local_ip, &s[9]); 
	        		}
				else if(strncasecmp(s,"local_port=",11)==0)
	        		{
	        			pstn_cfg.local_port = atoi(&s[11]);
	        		}
				else if(strncasecmp(s,"pstn_ip=",8)==0)
	        		{
	        			pstn_cfg.server_enable = 1;
	        			strcpy(pstn_cfg.server_ip, &s[8]); 
	        		}
				else if(strncasecmp(s,"pstn_port=",10)==0)
	        		{
	        			pstn_cfg.server_port = atoi(&s[10]);
	        		}
				else if(strncasecmp(s,"user_name=",10)==0)
	        		{
	        			strcpy(pstn_cfg.user_name, &s[10]); 
	        		}				
				else if(strncasecmp(s,"password=", 9)==0)
	        		{
	        			strcpy(pstn_cfg.password, &s[9]); 
	        		}				
			break;
			case 2:
			case 3:
				if(strncasecmp(s,"ip=",3)==0)
	        		{
	        			msan_ptr->server_enable = 1;
	        			strcpy(msan_ptr->server_ip, &s[3]); 
	        		}
				else if(strncasecmp(s,"port=",5)==0)
	        		{
	        			msan_ptr->server_port = atoi(&s[5]);
	        		}
				else if(strncasecmp(s,"user_name=",10)==0)
	        		{
	        			strcpy(msan_ptr->user_name, &s[10]); 
	        		}				
				else if(strncasecmp(s,"password=", 9)==0)
	        		{
	        			strcpy(msan_ptr->password, &s[9]); 
	        		}				
			break;
			case 4:	
				pstn_save_msan_slot_info(s);
			break;
			case 5:
				if(strncasecmp(s,"krone_pair_id=",14)==0)
	        		{
	        			pstn_cfg.krone_pair_id_in_crm = atoi(&s[14]);
				}
				else if(strncasecmp(s,"ssentry_id=",11)==0)
	        		{
	        			pstn_cfg.ss_entry_id_in_crm = atoi(&s[11]);
				}				
			break;
			default:				
			break;
      		}
	}
	
	fclose(fpConf);
	
	return 1;
}

long pstn_server_ip()
{
	return tcp_str_to_uip(pstn_cfg.server_ip);	
}

short pstn_server_port()
{
	return pstn_cfg.server_port;
}

long pstn_local_ip()
{
	return tcp_str_to_uip(pstn_cfg.local_ip);	
}

short pstn_local_port()
{
	return pstn_cfg.local_port;
}


void read_dstn_entry()
{
	char s[80];
	int  len;
  	FILE *fpConf;
    
    	
	fpConf = fopen(DSTN_CFG,"rb");
	
	if(fpConf == NULL)
	{
		return ;
	}
   
	strcpy(s,"");
	while(fgets(s,16,fpConf) !=(char *)0)
	{
		if( (int *)strchr(s,'#') !=NULL) continue;
		if( !strlen(s) ) continue;      
		len = strlen(s);
		if(len < 2) continue;
		
		if(s[len-1] == 0x0d ||s[len-1] == 0x0a)
		{
			if(s[len-2] == 0x0d ||s[len-2] == 0x0a)
			{
				s[len-2] = 0;
				len -= 2;			
			}
			else
			{
				s[len-1] = 0;
				len -= 1;
			}
		}
		if(len>0)
		{
			dstn_entry = atoi(s);
		}
	}

	fclose(fpConf);
	
}

void save_dstn_entry()
{
	FILE *fpConf;
    
    	
	fpConf = fopen(DSTN_CFG,"w");
	
	if(fpConf == NULL)
	{
		return ;
	}
	fprintf(fpConf,"%d",dstn_entry);
	fclose(fpConf);
}

int get_pstn_dstn_entry()
{
	int retval = dstn_entry++;

	save_dstn_entry();
	
	return retval;
}

int pstn_get_msan_info(int krone_pair_id, int *msan_id, int *msan_slot_id)
{
	int index;
	_pair_2_slot_param_t *pair_ptr=NULL;

	if(krone_pair_id>=MAX_SLOTS*64 ||krone_pair_id<1 )
		return 0;
	
	index = (krone_pair_id-1)/64;
	pair_ptr = &pstn_cfg.pair_slot_info[index];
	if(pair_ptr->used == 0)
		return 0;
	
	*msan_id = pair_ptr->msan_id;
	*msan_slot_id = pair_ptr->slot_id;

	return 1;
}

int msan_server_ip(int msan_id)
{
	if(msan_id>=2) return 0;
	return tcp_str_to_uip(pstn_cfg.msan[msan_id].server_ip);
}

int msan_server_port(int msan_id)
{
	if(msan_id>=2) return 0;
	return pstn_cfg.msan[msan_id].server_port;
}

int pstn_get_krone_pair_resource_id()
{
	return pstn_cfg.krone_pair_id_in_crm;
}

int pstn_get_ssentry_resource_id()
{
	return pstn_cfg.ss_entry_id_in_crm;
}
/* ----------------------------------------*/
int pstn_assign_proc()
{
	int i;

	for(i=0; i<MAX_PSTN_PROCS; i++)
	{
		if(pstn_procs.procs[i].flag  == 0)
		{
			memset(&pstn_procs.procs[i], 0x00, sizeof(_pstn_proc_t));
			pstn_procs.procs[i].id = i;
			pstn_procs.procs[i].flag = 1;
			return i;

		}
	}

	return -1;
}

void pstn_release_proc(unsigned int id)
{
	if(id >= MAX_PSTN_PROCS)
		return;

	pstn_release_tcp_port(&pstn_procs.procs[id]);

	memset(&pstn_procs.procs[id], 0x00, sizeof(_pstn_proc_t));
	
}

void pstn_send_command(_pstn_proc_t *ptr, char *command)
{
	printf("%s.port=%d, cmd=%s\n\r", __FUNCTION__, ptr->id, command);
	
	tcp_send(sw_tcp_sap_id, ptr->id, ptr->tcp_port_id, command, strlen(command));
}

void pstn_send_telnet_no_echo(_pstn_proc_t *ptr)
{
	char telnet_echo[6]={0xff, 0xfe, 0x03, 0xff, 0xfe, 0x01};

	tcp_send(sw_tcp_sap_id, ptr->id, ptr->tcp_port_id, telnet_echo, 6);
	
}

void	pstn_send_user_name_pwd(_pstn_proc_t *ptr, int flag)
{
	char command[128];

	if(flag == 0)
	{
		sprintf(command, "%s\r\n", pstn_cfg.user_name);
	}
	else
	{
		sprintf(command, "%s\r\n", pstn_cfg.password);
	}

	pstn_send_command(ptr, command);
}

void	pstn_send_msan_user_name_pwd(_pstn_proc_t *ptr, int flag)
{
	int index=ptr->prov.msan_id;
	char command[128];

	if(index>=2) return;
	if(flag == 0)
	{
		sprintf(command, "%s\r\n", pstn_cfg.msan[index].user_name);
	}
	else
	{
		sprintf(command, "%s\r\n", pstn_cfg.msan[index].password);
	}

	pstn_send_command(ptr, command);
}

void pstn_send_create_command(_pstn_proc_t *ptr, int sub_state)
{
	char create_command[PCS_MAX][128]={"NULL","INIT","ECHO","NAME","PASSWD",
		"adm /nogui", "dstn", "entry=","station=",
		"dstn=","name=", "signal=sip", "sip_user=", 
		"voip_grp=6","toll=0", "exit", "activate",
		"logout", "logout"};
/*
	char create_command[PCS_MAX][128]={"NULL","INIT","NAME","PASSWD",
		"adm /nogui", "dstn","station=",
		"dstn=","name=", "signal=sip", "sip_user=", 
		"toll=1", "exit", "activate",
		"logout", "logout"};
*/
	char command[128];
	char user_name[128];
	
	switch(sub_state)
	{
		case PCS_ENTRY:
		//	ptr->dstn_entry =  get_pstn_dstn_entry();
			sprintf(command, "%s%d\r\n", create_command[sub_state], ptr->prov.ss_entry);	
		break;
		case PCS_STATION:
			sprintf(command, "%s%s\r\n", create_command[sub_state], &ptr->prov.isdn_number[5]);	
		break;
		case PCS_DSTN:
			sprintf(command, "%s%s\r\n", create_command[sub_state], &ptr->prov.isdn_number[5]);
		break;
		case PCS_NAME:
//			sprintf(user_name,"dstn=0/0.%d", ptr->dstn_entry );
			sprintf(user_name,"dstn=0/0.0" );
			sprintf(command, "%s%s\r\n", create_command[sub_state], user_name);		
		break;
		case PCS_SIP_USER:
			sprintf(command, "%s%s\r\n", create_command[sub_state], ptr->prov.isdn_number);
		break;
		default:
			sprintf(command, "%s\r\n", create_command[sub_state]);
		break;
	}
	
	pstn_send_command(ptr, command);
	
}


void pstn_send_delete_command(_pstn_proc_t *ptr, int sub_state)
{
	char create_command[PDS_MAX][128]={"NULL","INIT","NAME","PASSWD",
		"adm /nogui", "dstn", "ent=","del=",
		"exit",
		"logout", "logout"};
	char command[128];
	char user_name[128];
	
	switch(sub_state)
	{
		case PDS_ENTRY:
			sprintf(command, "%s%d\r\n", create_command[sub_state], ptr->prov.ss_entry);
		break;
		case PDS_DEL:
			sprintf(command, "%s%s\r\n", create_command[sub_state], &ptr->prov.isdn_number[5]);
		break;
		default:
			sprintf(command, "%s\r\n", create_command[sub_state]);
		break;
	}
	
	pstn_send_command(ptr, command);
	
}

void pstn_send_update_command(_pstn_proc_t *ptr, int sub_state)
{
	char create_command[PUS_MAX][128]={"NULL","INIT","NAME","PASSWD",
		"adm /nogui", "dstn=",
		"toll=", "locki=on",
		"exit","activate",
		"logout"};
	char command[128];
	char user_name[128];
	
	switch(sub_state)
	{
		case PUS_DSTN:
			sprintf(command, "%s%s\r\n", create_command[sub_state], &ptr->prov.isdn_number[5]);
		break;
		case PUS_TOLL:
			if(ptr->prov.update_status == 5)//resume
			{
				sprintf(command, "toll=0\r\n");
			}
			else
				sprintf(command, "toll=2\r\n");				
		break;
		case PUS_LOCKI:
			if(ptr->prov.update_status == 4)//disable
			{
				sprintf(command, "locki=on\r\n");
			}
			else
				sprintf(command, "locki=off\r\n");				
		break;
		default:
			sprintf(command, "%s\r\n", create_command[sub_state]);
		break;
	}
	
	pstn_send_command(ptr, command);
}

void pstn_send_msan_create_command(_pstn_proc_t *ptr, int sub_state)
{
	char create_command[MCS_MAX][256]={"NULL","INIT","ECHO","NAME","PASSWD",
		"adm /nogui", "dstn", "/cfgm/save","exit"};

	char command[256];
	char user_name[128];
	
	switch(sub_state)
	{
		case MCS_CREATE:
			sprintf(command, "set /unit-19/portgroup-%d/port-%d/cfgm/PstnPort true {%s %s \"\" %s none;} false false none none none none none\r\n", 
				ptr->prov.msan_slot_id, ptr->prov.msan_slot_port_id, ptr->prov.isdn_number, ptr->prov.isdn_number, &ptr->prov.isdn_number[4]);	
		break;
		case MCS_BRING_UP:
			sprintf(command, "set /unit-%d/port-%d/main/AdministrativeStatus up\r\n", ptr->prov.msan_slot_id, ptr->prov.msan_slot_port_id);	
		break;
		case MCS_SAVE:
		case MCS_EXIT:
			sprintf(command, "%s\r\n", create_command[sub_state]);		
		break;
	}
	
	pstn_send_command(ptr, command);
	
}

void pstn_send_msan_delete_command(_pstn_proc_t *ptr, int sub_state)
{
	char create_command[MDS_MAX][256]={"NULL","INIT","NAME","PASSWD",
		"adm /nogui", "dstn", "/cfgm/save","exit"};

	char command[256];
	char user_name[128];
	
	switch(sub_state)
	{
		case MDS_DELETE:
			sprintf(command, "set /unit-19/portgroup-%d/port-%d/cfgm/PstnPort false {\"\" \"\" \"\" \"\" none;} true false none none none none none\r\n", 
				ptr->prov.msan_slot_id, ptr->prov.msan_slot_port_id);	
		break;
		case MDS_BRING_DOWN:
			sprintf(command, "set /unit-%d/port-%d/main/AdministrativeStatus down\r\n", ptr->prov.msan_slot_id, ptr->prov.msan_slot_port_id);	
		break;
		case MDS_SAVE:
		case MDS_LOGOUT:
			sprintf(command, "%s\r\n", create_command[sub_state]);		
		break;
	}
	
	pstn_send_command(ptr, command);
	
}

int pstn_tcp_recv_msg(int user_port, u_char *recv_buf, int len)
{
	_pstn_proc_t *ptr=NULL;

	if(user_port>=MAX_PSTN_PROCS)
		return 0;

	ptr = &pstn_procs.procs[user_port];
	ptr->recv_msg_flag = 1;
	ptr->msg_len = len;
	memcpy(ptr->recv_msg, recv_buf, len);
	
	return 1;
}


void pstn_release_tcp_port(_pstn_proc_t *ptr)
{
	tcp_port_release_port(sw_tcp_sap_id, ptr->id, ptr->tcp_port_id);
}

void crm_prov_pstn_account(char sub_data[][1024], int ssentry, int krone_pair_id)
{
	int oper_type = PC_NULL;
	char isdn_number[16];
	int proc_id, state = PS_NULL, sub_state=0;
	_pstn_proc_t *ptr=NULL;

	switch(atoi(sub_data[5]))//OPR_TYPE
	{
		case 1://add
			oper_type = PC_CREATE;
			state = PS_CREATE;
			sub_state = PCS_INIT;
		break;
		case 2:
			oper_type = PC_UPDATE;
			state = PS_UPDATE;
			sub_state = PUS_INIT;
		break;
		case 4://delete
			oper_type = PC_DELETE;
			state = PS_DELETE;
			sub_state = PDS_INIT;
		break;
	}
	strcpy(isdn_number, sub_data[1]);

	proc_id = pstn_assign_proc();
	if(proc_id < 0)
		return ;
	ptr = &pstn_procs.procs[proc_id];
	ptr->prov.command = oper_type;
	ptr->prov.pre_id = atoll(sub_data[4]);
	strcpy(ptr->prov.isdn_number, isdn_number);
	
	if(oper_type == PC_CREATE)
	{
		strcpy(ptr->prov.user_name, sub_data[2]);
	}
	else //(oper_type == PC_UPDATE)
	{
		if(strstr(sub_data[2],"Suspend"))
			ptr->prov.update_status = 3;
		else if(strstr(sub_data[2],"Disable"))
			ptr->prov.update_status = 4;
		else if(strstr(sub_data[2],"Resume"))
			ptr->prov.update_status = 5;		
		else //Disconnect
		{
			ptr->prov.update_status = 6;

			oper_type = PC_DELETE;
			state = PS_DELETE;
			sub_state = PDS_INIT;
			
		}
			
	}	
	ptr->prov.ss_entry = ssentry;
	ptr->prov.krone_pair_id = krone_pair_id;
	pstn_get_msan_info(krone_pair_id, &ptr->prov.msan_id, &ptr->prov.msan_slot_id); 
	ptr->prov.msan_slot_port_id = krone_pair_id%(64);
	if(ptr->prov.msan_slot_port_id == 0 )
		ptr->prov.msan_slot_port_id = 64;
	ptr->state = state;
	ptr->sub_state = sub_state;
	
}

void crm_prov_pstn_account_delete(char *isdn_number, int krone_pair_id)
{

	int oper_type = PC_NULL;
	int proc_id, state = PS_NULL, sub_state=0;
	_pstn_proc_t *ptr=NULL;

	oper_type = PC_DELETE;
	state = PS_DELETE;
	sub_state = PDS_INIT;

	proc_id = pstn_assign_proc();
	if(proc_id < 0)
		return ;
	ptr = &pstn_procs.procs[proc_id];
	ptr->prov.command = oper_type;
	strcpy(ptr->prov.isdn_number, isdn_number);
	ptr->prov.krone_pair_id = krone_pair_id;
	pstn_get_msan_info(krone_pair_id, &ptr->prov.msan_id, &ptr->prov.msan_slot_id); 
	ptr->prov.msan_slot_port_id = krone_pair_id%(64);
	if(ptr->prov.msan_slot_port_id == 0 )
		ptr->prov.msan_slot_port_id = 64;
	ptr->state = state;
	ptr->sub_state = sub_state;
	
}

int pstn_create_tcp_sess(_pstn_proc_t *ptr)
{

	int tcp_port_id;
	tcp_session_t sess;

	if(ptr == NULL)
		return -1;
	
	sess.bServer = 0;
	sess.src_ip = pstn_local_ip();
	sess.src_port = pstn_local_port()+ptr->id;
	sess.dst_ip = pstn_server_ip();
	sess.dst_port = pstn_server_port();
	tcp_port_id = tcp_port_alloc_port(sw_tcp_sap_id, ptr->id, &sess);
	if (tcp_port_id < 0)
	{
		return -1;
	}

	ptr->tcp_port_id = tcp_port_id;
}

int pstn_create_update_result(_pstn_proc_t *ptr, int flag)
{
	char sql[1024];
	int result=1;
	
	if(flag == 0) result = 2;//failed

	sprintf(sql, "UPDATE tb_sync_pstn set STATE=2, OPER_RESULT=%d where POST_ID=%d",ptr->prov.pre_id, result);
	
	return subs_provisioning_update_result(sql);
}

#define PSTN_CHANGE_STATE(value) do { \
ptr->timer = 0;\
ptr->state = value;\			
ptr->recv_msg_flag = 0;\		
} while(0)

#define PSTN_CHANGE_SUB_STATE(value) do { \
ptr->timer = 0;\
ptr->sub_state = value;\			
ptr->recv_msg_flag = 0;\		
} while(0)

int pstn_create_fsm(_pstn_proc_t *ptr)
{
	if(ptr->timer ++ > MSG_TIMER)
	{
		pstn_release_tcp_port(ptr);
	
		PSTN_CHANGE_STATE(PS_RELEASE);
		return 0;
	}
	
	switch(ptr->sub_state)
	{
		case PCS_INIT:
			if(pstn_create_tcp_sess(ptr)<0)
			{
				pstn_create_update_result(ptr, 0);
				PSTN_CHANGE_STATE(PS_RELEASE);
				break;
			}
			PSTN_CHANGE_SUB_STATE(PCS_ECHO);
		break;
		case PCS_ECHO:
			if(ptr->recv_msg_flag == 0)
				break;
			pstn_send_telnet_no_echo(ptr);
			PSTN_CHANGE_SUB_STATE(PCS_LOGIN);			
		break;
		case PCS_LOGIN:
			if(ptr->timer<10)
				break;
			pstn_send_user_name_pwd(ptr, 0);
			PSTN_CHANGE_SUB_STATE(PCS_PSSWD);
		break;
		case PCS_PSSWD:
//			if(ptr->recv_msg_flag == 0)
			if(ptr->timer<MSG_WAIT_TIMER)
				break;
			pstn_send_user_name_pwd(ptr, 1);
			PSTN_CHANGE_SUB_STATE(PCS_NOGUI);
		break;
		case PCS_NOGUI:
		default:
			if(ptr->timer<MSG_WAIT_TIMER)
				break;
			if(ptr->sub_state == PCS_ENTRY)
			{
				if(ptr->timer<MSG_WAIT_LTIMER)//wait longer since server will take more time to reply
					break;

			}
			if( ptr->sub_state == PCS_TRANS_TO_MSAN)
			{
				//pstn_release_tcp_port(ptr);
				
				PSTN_CHANGE_STATE(PS_CREATE_MSAN);
				PSTN_CHANGE_SUB_STATE(MCS_INIT);
				
				break;
			}
			
			pstn_send_create_command(ptr, ptr->sub_state);
			
			ptr->sub_state++;
			PSTN_CHANGE_SUB_STATE(ptr->sub_state);

			break;
		
	}

	return 1;
}

int pstn_update_fsm(_pstn_proc_t *ptr)
{
	if(ptr->timer ++ > MSG_TIMER)
	{
		PSTN_CHANGE_STATE(PS_RELEASE);
		return 0;
	}
	
	switch(ptr->sub_state)
	{
		case PUS_INIT:
			if(pstn_create_tcp_sess(ptr)<0)
			{
				PSTN_CHANGE_STATE(PS_RELEASE);
				break;
			}
			PSTN_CHANGE_SUB_STATE(PUS_LOGIN);
		break;
		case PUS_LOGIN:
			if(ptr->timer<10)
				break;
			pstn_send_user_name_pwd(ptr, 0);
			PSTN_CHANGE_SUB_STATE(PUS_PSSWD);
		break;
		case PUS_PSSWD:
			if(ptr->timer<MSG_WAIT_TIMER)
				break;
			pstn_send_user_name_pwd(ptr, 1);
			PSTN_CHANGE_SUB_STATE(PUS_NOGUI);
		break;
		case PUS_NOGUI:
		default:
			if(ptr->timer<MSG_WAIT_TIMER)
				break;
			pstn_send_update_command(ptr, ptr->sub_state);
			
			if(ptr->sub_state == PUS_LOGOUT)
			{
				pstn_update_provisiong_result(1, ptr->prov.pre_id, NULL);
			
				PSTN_CHANGE_SUB_STATE(PS_RELEASE);
			}
			else
			{
				ptr->sub_state++;
				PSTN_CHANGE_SUB_STATE(ptr->sub_state);
			}
			break;
		
	}

	return 1;
}

int pstn_delete_fsm(_pstn_proc_t *ptr)
{
	if(ptr->timer ++ > MSG_TIMER)
	{
		PSTN_CHANGE_STATE(PS_RELEASE);
		return 0;
	}
	
	switch(ptr->sub_state)
	{
		case PDS_INIT:
			if(pstn_create_tcp_sess(ptr)<0)
			{
				PSTN_CHANGE_STATE(PS_RELEASE);
				break;
			}
			PSTN_CHANGE_SUB_STATE(PDS_LOGIN);
		break;
		case PDS_LOGIN:
			if(ptr->timer<MSG_WAIT_TIMER)
				break;
			pstn_send_user_name_pwd(ptr, 0);
			PSTN_CHANGE_SUB_STATE(PDS_PSSWD);
		break;
		case PDS_PSSWD:
			if(ptr->timer<MSG_WAIT_TIMER)
				break;
			pstn_send_user_name_pwd(ptr, 1);
			PSTN_CHANGE_SUB_STATE(PDS_NOGUI);
		break;
		case PDS_NOGUI:
		default:
			if(ptr->timer<MSG_WAIT_TIMER)
				break;

			
			if(ptr->sub_state == PDS_ENTRY)
			{
				if(ptr->timer<MSG_WAIT_LTIMER)//wait longer since server will take more time to reply
					break;

			}

			
			pstn_send_delete_command(ptr, ptr->sub_state);
			
			if(ptr->sub_state == PDS_LOGOUT_AGAIN)
			{
				PSTN_CHANGE_SUB_STATE(MDS_INIT);
				PSTN_CHANGE_STATE(PS_DELETE_MSAN);
			}
			else
			{
				ptr->sub_state++;
				PSTN_CHANGE_SUB_STATE(ptr->sub_state);
			}
			break;
		
	}

	return 1;
}


int msan_create_tcp_sess(_pstn_proc_t *ptr)
{

	int tcp_port_id;
	tcp_session_t sess;

	if(ptr == NULL)
		return -1;
	
	sess.bServer = 0;
	sess.src_ip = pstn_local_ip();
	sess.src_port = pstn_local_port()+ptr->id+1000;
	sess.dst_ip = msan_server_ip(ptr->prov.msan_id);
	sess.dst_port = msan_server_port(ptr->prov.msan_id);
	tcp_port_id = tcp_port_alloc_port(sw_tcp_sap_id, ptr->id, &sess);
	if (tcp_port_id < 0)
	{
		return -1;
	}

	ptr->tcp_port_id = tcp_port_id;
}


int msan_create_fsm(_pstn_proc_t *ptr)
{
	if(ptr->timer ++ > MSG_TIMER)
	{
		pstn_release_tcp_port(ptr);
	
		PSTN_CHANGE_STATE(PS_RELEASE);
		return 0;
	}
	
	switch(ptr->sub_state)
	{
		case MCS_INIT:
			if(msan_create_tcp_sess(ptr)<0)
			{
				PSTN_CHANGE_STATE(PS_RELEASE);
				break;
			}
			PSTN_CHANGE_SUB_STATE(MCS_ECHO);
		break;
		case MCS_ECHO:
			if(ptr->timer<10)
				break;
			pstn_send_telnet_no_echo(ptr);
			PSTN_CHANGE_SUB_STATE(MCS_LOGIN);			
		break;
		case MCS_LOGIN:
			if(ptr->timer<10)
				break;
			pstn_send_msan_user_name_pwd(ptr, 0);
			PSTN_CHANGE_SUB_STATE(MCS_PSSWD);
		break;
		case MCS_PSSWD:
			if(ptr->timer<MSG_WAIT_TIMER)
				break;
			pstn_send_msan_user_name_pwd(ptr, 1);
			PSTN_CHANGE_SUB_STATE(MCS_CREATE);
		break;
		case MCS_CREATE:
		case MCS_BRING_UP:
		case MCS_SAVE:
		case MCS_EXIT:
		default:
			if(ptr->timer<MSG_WAIT_TIMER)
				break;
			if(ptr->sub_state == MCS_WAIT_EXIT)
			{
				pstn_update_provisiong_result(1, ptr->prov.pre_id, NULL);
				pstn_release_tcp_port(ptr);
				PSTN_CHANGE_STATE(PS_RELEASE);
				break;
			}

			pstn_send_msan_create_command(ptr, ptr->sub_state);
			{
				ptr->sub_state++;
				PSTN_CHANGE_SUB_STATE(ptr->sub_state);
			}
		break;		
	}

	return 1;
}

int msan_delete_fsm(_pstn_proc_t *ptr)
{
	if(ptr->timer ++ > MSG_TIMER)
	{
		PSTN_CHANGE_STATE(PS_RELEASE);
		return 0;
	}
	
	switch(ptr->sub_state)
	{
		case MDS_INIT:
			if(msan_create_tcp_sess(ptr)<0)
			{
				PSTN_CHANGE_STATE(PS_RELEASE);
				break;
			}
			PSTN_CHANGE_SUB_STATE(MDS_LOGIN);
		break;
		case MDS_LOGIN:
			if(ptr->timer<MSG_WAIT_TIMER)
				break;
			pstn_send_msan_user_name_pwd(ptr, 0);
			PSTN_CHANGE_SUB_STATE(MDS_PSSWD);
		break;
		case MDS_PSSWD:
			if(ptr->timer<MSG_WAIT_TIMER)
				break;
			pstn_send_msan_user_name_pwd(ptr, 1);
			PSTN_CHANGE_SUB_STATE(MDS_DELETE);
		break;
		case MDS_DELETE:
		case MDS_BRING_DOWN:
		case MDS_SAVE:
		case MDS_LOGOUT:
		default:
			if(ptr->timer<MSG_WAIT_TIMER)
				break;
			pstn_send_msan_delete_command(ptr, ptr->sub_state);
			if(ptr->sub_state == MDS_LOGOUT)
			{
				pstn_update_provisiong_result(1, ptr->prov.pre_id, NULL);
			
				PSTN_CHANGE_STATE(PS_RELEASE);
			}
			else
			{
				ptr->sub_state++;
				PSTN_CHANGE_SUB_STATE(ptr->sub_state);
			}
		break;		
	}

	return 1;
}

int   pstn_proc_fsm()
{
	int i;
	_pstn_proc_t *ptr=NULL;
	
	for(i=0; i<MAX_PSTN_PROCS; i++)
	{
		if(pstn_procs.procs[i].flag == 0)
		{
			continue;
		}
		ptr=&pstn_procs.procs[i];
		
		switch(ptr->state)
		{
			case PS_NULL:
			break;
			case PS_CREATE:
				pstn_create_fsm(ptr);
			break;
			case PS_UPDATE:
				pstn_update_fsm(ptr);				
			break;
			case PS_DELETE:
				pstn_delete_fsm(ptr);
			break;
			case PS_BAR_OUT:
			break;
			case PS_UNBAR_OUT:
			break;
			case PS_BAR_IN:
			break;
			case PS_UNBAR_IN:
			break;
			case PS_CREATE_MSAN:
				msan_create_fsm(ptr);
			break;			
			case PS_DELETE_MSAN:
				msan_delete_fsm(ptr);
			break;
			case PS_RELEASE:
			default:
				pstn_release_proc(ptr);
			break;
		}
	}
	

}

int pstn_tcp_init()
{
	tcp_sap_t tcpSap;

	// bind
	strcpy(tcpSap.name, "pstnCli");
	tcpSap.recv_cb = pstn_tcp_recv_msg;
	sw_tcp_sap_id = tcp_bind(&tcpSap);
	if (sw_tcp_sap_id < 0)
	{
		return -1;
	}

	return 0;
}

int pstn_client_thread(void *param)
{	
	pstn_client_read_conf();

	tcp_init();
	
	pstn_tcp_init();
	
	while(1)
	{
		tcp_timer();
		
		pstn_proc_fsm();
		
		usleep(10000);
	}
	
	return 1;
}
