#include "./include/radius_pub.h"

#define	test_nowsms_enable		0
#define test_css_enable			0

#define radius_set_pid_state(val) do { \
	pid_ptr->state = val; \
	radius_log_event("[%05d] \33[32m%s\33[0m transfer to state \33[36m%s\33[0m\r\n", pid, __FUNCTION__, #val); \
} while(0)

#define radius_set_pid_timer(val) do { \
	pid_ptr->timer = val; \
} while(0)

#if(test_nowsms_enable)
static void radius_test(int pid, BYTE sap_index, WORD *usr_pid, RADIUS_MSG *ptr_radius)
{
	RADIUS_MSG				pdu;

	if(usr_pid == NULL || ptr_radius == NULL)
		return;
	memset(&pdu, 0, sizeof(RADIUS_MSG));
	pdu.msg_type = RADIUS_MSG_TYPE_ACCESS_ACCEPT;
	pdu.msg.access_accept.message_authenticator.flag = 1;
	radius_send_access_accept(sap_index, pid, *usr_pid, &pdu);
	return;
}
#elif(test_css_enable)
static void radius_test(int pid, BYTE sap_index, WORD *usr_pid, RADIUS_MSG *ptr_radius)
{
	static BYTE				user_id = 0;
	BYTE					count = 0;
	int						num, i;
	RADIUS_MSG				pdu;
	RADIUS_ACCESS_ACCEPT	*access_accept;
	RADIUS_VENDOR_SPECIFIC	*vendor_specific;
	RADIUS_VENDOR_ATTRIBUTE_3GPP2_PPAC		*ppac;
	RADIUS_VENDOR_ATTRIBUTE_3GPP2_PPAQ		*ppaq;

	if(usr_pid == NULL || ptr_radius == NULL)
		return;
	if(ptr_radius->msg.access_request.user_password.flag == 1)
	{
		count = 1;
		*usr_pid = user_id;
		user_id++;
	}
	else if((num = ptr_radius->msg.access_request.vendor_specific.num) > 0)
	{
		for(i = 0; i < num; i++)
		{
			vendor_specific = &ptr_radius->msg.access_request.vendor_specific;
			ppaq = &vendor_specific->vendor_specific[i].attribute_3gpp2.ppaq;
			if((vendor_specific->vendor_specific[i].vendor_type == RADIUS_VENDOR_TYPE_3GPP2_PPAQ) && (ppaq->qid.flag == 1))
			{
				*usr_pid = (ppaq->qid.qid.value >> 16) & 0xFF;
				count = (ppaq->qid.qid.value & 0xFF) + 1;
			}
		}
	}
	
	memset(&pdu, 0, sizeof(RADIUS_MSG));
	pdu.msg_type = RADIUS_MSG_TYPE_ACCESS_ACCEPT;
	access_accept = &pdu.msg.access_accept;
	vendor_specific = &access_accept->vendor_specific;
	num = vendor_specific->num;
	vendor_specific->vendor_specific[num].flag = 1;
	vendor_specific->vendor_specific[num].vendor_type = RADIUS_VENDOR_TYPE_3GPP2_PPAC;
	ppac = &vendor_specific->vendor_specific[num].attribute_3gpp2.ppac;
	ppac->sfs.flag = 1;
	//ppac->sfs.sfs = 2;	//dt && dq
	ppac->sfs.sfs = 1;	//vt && vq
	vendor_specific->num++;
	num = vendor_specific->num;
	vendor_specific->vendor_specific[num].vendor_type = RADIUS_VENDOR_TYPE_3GPP2_PPAQ;
	ppaq = &vendor_specific->vendor_specific[num].attribute_3gpp2.ppaq;
	ppaq->qid.flag = 1;
	ppaq->qid.qid.value = ((*usr_pid) << 16) | (count * 1);

	if(ppac->sfs.sfs == 1)
	{
		ppaq->vq.flag = 1;
		ppaq->vq.vq.integer = (count + 1) * 1024 * 20;
		ppaq->vq.vq.exponent = 1;
		ppaq->vt.flag = 1;
		ppaq->vt.vt.integer = (count + 1) * 1024 * 20 - 1024 * 2;
		ppaq->vt.vt.exponent = 1;
	}
	else if(ppac->sfs.sfs == 2)
	{
		ppaq->dq.flag = 1;
		ppaq->dq.dq.value = count * 20;
		ppaq->dt.flag = 1;
		ppaq->dt.dt.value = count * 20 - 2;
	}
	
	vendor_specific->num++;
	access_accept->message_authenticator.flag = 1;
	radius_send_access_accept(sap_index, pid, *usr_pid, &pdu);
	return;
}
#endif

int radius_pid_indication_process(int pid)
{
	BYTE		sap_index;
	RADIUS_PID	*pid_ptr;

	pid_ptr = radius_get_pid_ptr(pid);

	sap_index = pid_ptr->sap_index;

	switch (pid_ptr->state)
	{
	case RADIUS_PID_IND_STATE_IDLE:
		if (pid_ptr->rv_msg_flag != 0)
		{
			pid_ptr->rv_msg_flag = 0;

			if (radius_sap[sap_index].radius_ind == NULL)
				return 1;
#if(test_nowsms_enable || test_css_enable)
{
			radius_test(pid, sap_index, &pid_ptr->usr_pid, &radius_msg_pdu);
}
#else
			if (radius_sap[sap_index].radius_ind(&pid_ptr->usr_pid, pid, &radius_msg_pdu) < 0)
			{
				radius_log_err("[%04d] %s: radius_ind error! usr_pid=%d\r\n", pid, __FUNCTION__, pid_ptr->usr_pid);
				return 1;
			}
#endif
			radius_log_event("[%04d] %s: recv indication and send it to up layer\r\n", pid, __FUNCTION__);

			radius_set_pid_state(RADIUS_PID_IND_STATE_WAIT_FOR_USER_RESP);
			radius_set_pid_timer(RADIUS_TIMER_16S);
		}
		else
		{
			return 1;
		}
		break;
	case RADIUS_PID_IND_STATE_WAIT_FOR_USER_RESP:
		if (pid_ptr->sd_msg_flag == 1)
		{
			pid_ptr->sd_msg_flag = 0;

			radius_send_msg(pid);

			radius_set_pid_timer(RADIUS_TIMER_16S);

			radius_set_pid_state(RADIUS_PID_IND_STATE_WAIT_FOR_TERMINATE);

			break;
		}

		if (pid_ptr->timer > 0)
		{
			if (--pid_ptr->timer == 0)
			{
				radius_log_warn("[%04d] %s: wait user response time out\r\n", pid, __FUNCTION__);
				radius_set_pid_state(RADIUS_PID_IND_STATE_TERMINATE);

				return 1;
			}
		}
		break;
	case RADIUS_PID_IND_STATE_WAIT_FOR_TERMINATE:
		if (pid_ptr->rv_msg_flag != 0)
		{
			radius_send_msg(pid);

			pid_ptr->rv_msg_flag = 0;
		}

		if (pid_ptr->timer > 0)
		{
			if (--pid_ptr->timer == 0)
			{
				radius_set_pid_state(RADIUS_PID_IND_STATE_TERMINATE);
				return 1;
			}
		}
		break;
	}

	return 0;
}

int radius_pid_indication_fsm(int pid)
{
	switch (radius_pid_indication_process(pid))
	{
	case 0:
		break;
	case 1:
		radius_init_pid(pid);
		break;
	}

	return 0;
}

int radius_pid_request_process(int pid)
{
	BYTE			sap_index;
	RADIUS_PID		*pid_ptr;

	pid_ptr = radius_get_pid_ptr(pid);
	sap_index = pid_ptr->sap_index;

	switch (pid_ptr->state)
	{
	case RADIUS_PID_REQ_STATE_IDLE:
		if (pid_ptr->sd_msg_flag == 1)
		{
			pid_ptr->sd_msg_flag = 0;

			radius_send_msg(pid);

			radius_set_pid_state(RADIUS_PID_REQ_STATE_WAIT_FOR_PEER_RESP);

			radius_set_pid_timer(RADIUS_TIMER_16S);

			break;
		}

		if (pid_ptr->timer > 0)
		{
			if (--pid_ptr->timer == 0)
			{
				return 1;
			}
		}
		else
		{
			return 1;
		}

		break;
	case RADIUS_PID_REQ_STATE_WAIT_FOR_PEER_RESP:
		if (pid_ptr->rv_msg_flag != 0)
		{
			radius_log_event("[%04d]%s: recv peer response and send it to up layer\r\n", pid, __FUNCTION__);

			radius_set_pid_state(RADIUS_PID_REQ_STATE_TERMINATE);

			if (radius_sap[sap_index].radius_cnf == NULL)
			{
				radius_log_err("[%04d] %s: radius_cnf error! usr_pid=%d\r\n", pid, __FUNCTION__, pid_ptr->usr_pid);
				return 1;
			}

			radius_sap[sap_index].radius_cnf(pid_ptr->usr_pid, pid, &radius_msg_pdu);

			return 1;
		}

		if (pid_ptr->timer > 0)
		{
			if (--pid_ptr->timer == 0)
			{
				radius_log_warn("[%04d] %s: wait peer response time out\r\n", pid, __FUNCTION__);
				radius_set_pid_state(RADIUS_PID_REQ_STATE_TERMINATE);

				return 1;
			}
			else
			{
				if ((pid_ptr->timer % RADIUS_RETRANS_TIMER) == 0)
				{
					radius_send_msg(pid);
				}
			}
		}

		break;
	default:
		return 1;
	}

	return 0;
}

int radius_pid_request_fsm(int pid)
{
	switch (radius_pid_request_process(pid))
	{
	case 0:
		break;
	case 1:
		radius_init_pid(pid);
		break;
	}

	return 0;
}

int radius_p_fsm(int pid)
{
	RADIUS_PID		*pid_ptr;

	if ((pid_ptr = radius_get_pid_ptr(pid)) == NULL)
		return -1;

	switch (pid_ptr->type)
	{
	case RADIUS_PID_TYPE_IND:
		radius_pid_indication_fsm(pid);
		break;
	case RADIUS_PID_TYPE_REQ:
		radius_pid_request_fsm(pid);
		break;
	default:
		radius_init_pid(pid);
		break;
	}

	return 0;
}

int radius_fsm(void)
{
	int				i;
	RADIUS_PID		*pid_ptr;

	for (i = 0; i < RADIUS_MAX_NUM_OF_PID; i++)
	{
		pid_ptr = radius_get_pid_ptr(i);

		if (pid_ptr->flag != 1)
			continue;

		radius_p_fsm(i);
	}

	return 0;
}


