// Made by Jessica Codr (jmc5@cec.wustl.edu or jmc5@arl.wustl.edu)
// as a collection of additional methods that might be helpful
// for plugin creation.


#include "plugin_api.h"
#include "plugin_dl.h"

char* append_int_sram(__declspec(sram) char* str, int n);
void print_message(int sink, __declspec(sram) char *msg, int debug);
void print_message_1(int sink, __declspec(sram) char *msg, int a1, int debug);
void print_message_2(int sink, __declspec(sram) char *msg, int a1, int a2, int debug);
void print_message_3(int sink, __declspec(sram) char *msg, int a1, int a2, int a3, int debug);
void print_message_4(int sink, __declspec(sram) char *msg, int a1, int a2, int a3, int a4, int debug);
/*
void printf_message_1(unsigned int sink, __declspec(sram) char *msg, int a1, int debug);
void printf_message_2(unsigned int sink, __declspec(sram) char *msg, int a1, int a2, int debug);
void printf_message_3(unsigned int sink, __declspec(sram) char *msg, int a1, int a2, int a3, int debug);
void printf_message_4(unsigned int sink, __declspec(sram) char *msg, int a1, int a2, int a3, int a4, int debug);
*/
void onl_api_long2decstr_sram(__declspec(sram) unsigned long val, __declspec(sram) char valcalc[9]);

__forceinline void onl_api_intarr2str_sram(__declspec(sram) unsigned int intarr[7], __declspec(sram) char msg[28])
{
  msg[0] = (intarr[0] >> 24) & 0xff;
  msg[1] = (intarr[0] >> 16) & 0xff;
  msg[2] = (intarr[0] >> 8) & 0xff;
  msg[3] = (intarr[0]) & 0xff;

  msg[4] = (intarr[1] >> 24) & 0xff;
  msg[5] = (intarr[1] >> 16) & 0xff;
  msg[6] = (intarr[1] >> 8) & 0xff;
  msg[7] = (intarr[1]) & 0xff;

  msg[8] = (intarr[2] >> 24) & 0xff;
  msg[9] = (intarr[2] >> 16) & 0xff;
  msg[10] = (intarr[2] >> 8) & 0xff;
  msg[11] = (intarr[2]) & 0xff;

  msg[12] = (intarr[3] >> 24) & 0xff;
  msg[13] = (intarr[3] >> 16) & 0xff;
  msg[14] = (intarr[3] >> 8) & 0xff;
  msg[15] = (intarr[3]) & 0xff;

  msg[16] = (intarr[4] >> 24) & 0xff;
  msg[17] = (intarr[4] >> 16) & 0xff;
  msg[18] = (intarr[4] >> 8) & 0xff;
  msg[19] = (intarr[4]) & 0xff;

  msg[20] = (intarr[5] >> 24) & 0xff;
  msg[21] = (intarr[5] >> 16) & 0xff;
  msg[22] = (intarr[5] >> 8) & 0xff;
  msg[23] = (intarr[5]) & 0xff;

  msg[24] = (intarr[6] >> 24) & 0xff;
  msg[25] = (intarr[6] >> 16) & 0xff;
  msg[26] = (intarr[6] >> 8) & 0xff;
  msg[27] = (intarr[6]) & 0xff;
}

unsigned int sram_ring_get_buffer_1word_sram(unsigned int ring_number)
{
  SIGNAL ring_signal;
  __declspec(sram_read_reg) unsigned int data[1];

  __declspec(sram) void* ring_addr =
       (__declspec(sram) void *) ((SRAM_CONTROL_RING_CHANNEL<<QDESC_CHANNEL_BITPOS) | (ring_number<<2));

  sram_get_ring(data, ring_addr, sizeof(data) / sizeof(data[0]), ctx_swap, &ring_signal);

  return data[0];
}

// fills valstr with the decimal representation of val
__forceinline void onl_api_int2decstr(__declspec(gp_reg) unsigned int val, __declspec(local_mem) char valcalc[9])
{
  __declspec(gp_reg) unsigned int i = 0;
  __declspec(gp_reg) unsigned int j = 0;
  __declspec(gp_reg) unsigned int addVal = 1;
  __declspec(gp_reg) unsigned int addValCopy = 1;
  __declspec(gp_reg) unsigned int numDigits = 1;
  __declspec(gp_reg) unsigned int nine = 57;
  __declspec(gp_reg) unsigned int zero = 48;
  __declspec(gp_reg) unsigned int maxVal = 127;

  valcalc[i] = 48;

  while(val > 0){
  
 	if(val & 0x1){
		addValCopy = addVal;
		while(addValCopy >= maxVal){ //to avoid wrap-arounds w/ chars
			i = numDigits - 1;
			valcalc[i] = valcalc[i] + maxVal;  
			while(valcalc[i] > nine){
				if(i==0){ 
					j = numDigits;
					while(j>0){ 
						valcalc[j] = valcalc[j-1];
						j = j - 1;
					}
					valcalc[0] = zero;
					i = 1;
					numDigits = numDigits + 1;
				}	
				valcalc[i] -= 10;
				valcalc[i-1] += 1;
				if(valcalc[i] <= nine && i > 0){
					i -= 1;
				}
			}
			addValCopy = addValCopy - maxVal;
		}
		i = numDigits - 1;
		valcalc[i] = valcalc[i] + addValCopy; 
		while(valcalc[i] > nine){
			if(i==0){ 
			// shift digits over for more space if needed. 
				j = numDigits;
				while(j>0){ //remember that j is unsigned!!!!
					valcalc[j] = valcalc[j-1];
					j = j - 1;
				}
				valcalc[0] = zero;
				i = 1;
				numDigits = numDigits + 1;
			}	
			valcalc[i] -= 10;
			valcalc[i-1] += 1;
			if(valcalc[i] <= nine && i > 0){
				i -= 1;
			}
		}
  	}
	val = val >> 1;
  	addVal = addVal * 2;
  }
  valcalc[numDigits] = '\0';
}


// fills valstr with the decimal representation of val
void onl_api_int2decstr_sram(__declspec(sram) unsigned int val, __declspec(sram) char valcalc[9])
{
  __declspec(sram) unsigned int i = 0;
  __declspec(sram) unsigned int j = 0;
  __declspec(sram) unsigned int addVal = 1;
  __declspec(sram) unsigned int addValCopy = 1;
  __declspec(sram) unsigned int numDigits = 1;
  __declspec(sram) unsigned int nine = 57;
  __declspec(sram) unsigned int zero = 48;
  __declspec(sram) unsigned int maxVal = 127;

  valcalc[i] = 48;

  while(val > 0){
  
 	if(val & 0x1){
		addValCopy = addVal;
		while(addValCopy >= maxVal){ 
			i = numDigits - 1;
			valcalc[i] = valcalc[i] + maxVal;  
			while(valcalc[i] > nine){
				if(i==0){ 
					j = numDigits;
					while(j>0){ 
						valcalc[j] = valcalc[j-1];
						j = j - 1;
					}
					valcalc[0] = zero;
					i = 1;
					numDigits = numDigits + 1;
				}	
				valcalc[i] -= 10;
				valcalc[i-1] += 1;
				if(valcalc[i] <= nine && i > 0){
					i -= 1;
				}
			}
			addValCopy = addValCopy - maxVal;
		
		}
		i = numDigits - 1;
		valcalc[i] = valcalc[i] + addValCopy; 
		while(valcalc[i] > nine){
			if(i==0){
				j = numDigits;
				while(j>0){ //remember that j is unsigned!!!!
					valcalc[j] = valcalc[j-1];
					j = j - 1;
				}
				valcalc[0] = zero;
				i = 1;
				numDigits = numDigits + 1;
			}	
			valcalc[i] -= 10;
			valcalc[i-1] += 1;
			if(valcalc[i] <= nine && i > 0){
				i -= 1;
			}
		}
  	}
	val = val >> 1;
  	addVal = addVal * 2;
  }
  valcalc[numDigits] = '\0';
}

// fills valstr with the decimal representation of val
void onl_api_long2decstr_sram(__declspec(sram) unsigned long val, __declspec(sram) char valcalc[9])
{
  __declspec(sram) unsigned int i = 0;
  __declspec(sram) unsigned int j = 0;
  __declspec(sram) unsigned int addVal = 1;
  __declspec(sram) unsigned int addValCopy = 1;
  __declspec(sram) unsigned int numDigits = 1;
  __declspec(sram) unsigned int nine = 57;
  __declspec(sram) unsigned int zero = 48;
  __declspec(sram) unsigned int maxVal = 127;

  valcalc[i] = 48;

  while(val > 0){
  
 	if(val & 0x1){
		addValCopy = addVal;
		while(addValCopy >= maxVal){ 
			i = numDigits - 1;
			valcalc[i] = valcalc[i] + maxVal;  
			while(valcalc[i] > nine){
				if(i==0){ 
					j = numDigits;
					while(j>0){ 
						valcalc[j] = valcalc[j-1];
						j = j - 1;
					}
					valcalc[0] = zero;
					i = 1;
					numDigits = numDigits + 1;
				}	
				valcalc[i] -= 10;
				valcalc[i-1] += 1;
				if(valcalc[i] <= nine && i > 0){
					i -= 1;
				}
			}
			addValCopy = addValCopy - maxVal;
		
		}
		i = numDigits - 1;
		valcalc[i] = valcalc[i] + addValCopy; 
		while(valcalc[i] > nine){
			if(i==0){
				j = numDigits;
				while(j>0){ //remember that j is unsigned!!!!
					valcalc[j] = valcalc[j-1];
					j = j - 1;
				}
				valcalc[0] = zero;
				i = 1;
				numDigits = numDigits + 1;
			}	
			valcalc[i] -= 10;
			valcalc[i-1] += 1;
			if(valcalc[i] <= nine && i > 0){
				i -= 1;
			}
		}
  	}
	val = val >> 1;
  	addVal = addVal * 2;
  }
  valcalc[numDigits] = '\0';
}


// fills valstr with the hexadecimal representation of val
void onl_api_int2hstr_sram(__declspec(sram) unsigned int val, __declspec(sram) char valstr[9])
{
  __declspec(sram) unsigned int c;
  __declspec(sram) unsigned int i = 0;
  __declspec(sram) unsigned int found_non_zero = 0;
  __declspec(sram) signed int shift = 28;

  valstr[i++] = '0';
  valstr[i++] = 'x';

  while(shift >= 0){
  	c = (val >> shift) & 0xf;
  	if(found_non_zero == 1 || c != 0)
  	{
  		found_non_zero = 1;
    		if(c > 9) { valstr[i++] = c - 10 + 'a'; }
    		else { valstr[i++] = c + '0'; }
  	}
	shift = shift - 4;
  }
  if(i == 2){
	valstr[i++] = '0';
  }
  valstr[i] = '\0';
}


int onl_api_str2intarr_sram_write(__declspec(sram) char *msg, __declspec(sram_write_reg) unsigned int intarr[7])
{
  __declspec(sram) unsigned int msglen;
  __declspec(sram) int i,j;
  __declspec(sram) char padmsg[28];

  msglen = strlen_sram(msg);
  if(msglen > 27)
  {
    return -1;
  }

  for(i=0; i<msglen; ++i)
  {
    padmsg[i] = msg[i];
  }
  padmsg[i] = '\0';
  for(i=msglen+1; i<28; ++i)
  {
    padmsg[i] = 0xff;
  }

  j = 0;
  for(i=0; i<7; ++i)
  {
  	intarr[i] = //(unsigned int)(padmsg[j]); //does this alone work? - nope
		((((unsigned int)(padmsg[j])) & 0xff) << 24) | 
		((((unsigned int)(padmsg[j+1])) & 0xff) << 16) | 
		((((unsigned int)(padmsg[j+2])) & 0xff) << 8) | 
		(((unsigned int)(padmsg[j+3])) & 0xff);
 	j=j+4;
  }

  return 0;
}

void dl_source_message_sram(unsigned int source, __declspec(sram) unsigned int *msg)
{
  __declspec(gp_reg) unsigned int sring;
  __declspec(gp_reg) unsigned int i;
  __declspec(gp_reg) unsigned int n;

  // only procede once the lock is available
  while(dl_source_message_lock == LOCKED)
  {
    ctx_swap();
  }
  dl_source_message_lock = LOCKED;

  if(source == MESSAGE_IN_RING_0)
  {
    sring = ONL_XSCALE_TO_PLUGIN_0_CTRL_SRAM_RING;
  }
  else if(source == MESSAGE_IN_RING_1)
  {
    sring = ONL_XSCALE_TO_PLUGIN_1_CTRL_SRAM_RING;
  }
  else if(source == MESSAGE_IN_RING_2)
  {
    sring = ONL_XSCALE_TO_PLUGIN_2_CTRL_SRAM_RING;
  }
  else if(source == MESSAGE_IN_RING_3)
  {
    sring = ONL_XSCALE_TO_PLUGIN_3_CTRL_SRAM_RING;
  }
  else if(source == MESSAGE_IN_RING_4)
  {
    sring = ONL_XSCALE_TO_PLUGIN_4_CTRL_SRAM_RING;
  }
  else
  {
    return;
  }

  msg[0] = sram_ring_get_buffer_1word_sram(sring);
  while(msg[0] == 0)
  {
    ctx_swap();
    msg[0] = sram_ring_get_buffer_1word_sram(sring);
  }

  n = (msg[0]>>16) & 0xFF;

  if(n >= 1)
  {
    msg[1] = sram_ring_get_buffer_1word_sram(sring);
    while(msg[1] == 0)
    {
      ctx_swap();
      msg[1] = sram_ring_get_buffer_1word_sram(sring);
    }
  }
  if(n >= 2)
  {
    msg[2] = sram_ring_get_buffer_1word_sram(sring);
    while(msg[2] == 0)
    {
      ctx_swap();
      msg[2] = sram_ring_get_buffer_1word_sram(sring);
    }
  }
  if(n >= 3)
  {
    msg[3] = sram_ring_get_buffer_1word_sram(sring);
    while(msg[3] == 0)
    {
      ctx_swap();
      msg[3] = sram_ring_get_buffer_1word_sram(sring);
    }
  }
  if(n >= 4)
  {
    msg[4] = sram_ring_get_buffer_1word_sram(sring);
    while(msg[4] == 0)
    {
      ctx_swap();
      msg[4] = sram_ring_get_buffer_1word_sram(sring);
    }
  }
  if(n >= 5)
  {
    msg[5] = sram_ring_get_buffer_1word_sram(sring);
    while(msg[5] == 0)
    {
      ctx_swap();
      msg[5] = sram_ring_get_buffer_1word_sram(sring);
    }
  }
  if(n >= 6)
  {
    msg[6] = sram_ring_get_buffer_1word_sram(sring);
    while(msg[6] == 0)
    {
      ctx_swap();
      msg[6] = sram_ring_get_buffer_1word_sram(sring);
    }
  }
  if(n >= 7)
  {
    msg[7] = sram_ring_get_buffer_1word_sram(sring);
    while(msg[7] == 0)
    {
      ctx_swap();
      msg[7] = sram_ring_get_buffer_1word_sram(sring);
    }
  }

  dl_source_message_lock = UNLOCKED;
}

char* append_int_sram(__declspec(sram) char* str, int n){
	__declspec(sram) char dataStr[9];

	onl_api_int2decstr_sram(n, dataStr);
	strcat_sram(str, " ");
	strcat_sram(str, dataStr);

	return str;

}

void print_message_1(int sink, __declspec(sram) char *msg, int a1, int debug){
	__declspec(sram) char msgStr[100];
	
	strcpy_sram(msgStr, msg);
	append_int_sram(msgStr, a1);
	
	print_message(sink, msgStr, debug);
}

/*void printf_message_1(unsigned int sink, __declspec(sram) char *msg, int a1, int debug){
	__declspec(sram) char dataStr[10];
	__declspec(sram) char msgStr[100];
	
	strcpy_sram(msgStr, msg);
	onl_api_int2decstr_sram(a1, dataStr);
	strrep_sram(msgStr, "%i", dataStr);
	
	print_message(sink, msgStr, debug);
}*/

void print_message_2(int sink, __declspec(sram) char *msg, int a1, int a2, int debug){
	__declspec(sram) char msgStr[100];
	
	strcpy_sram(msgStr, msg);
	append_int_sram(msgStr, a1);
	append_int_sram(msgStr, a2);

	print_message(sink, msgStr, debug);
}

/*void printf_message_2(unsigned int sink, __declspec(sram) char *msg, int a1, int a2, int debug){
	__declspec(sram) char dataStr[10];
	__declspec(sram) char msgStr[100];
	
	strcpy_sram(msgStr, msg);
	onl_api_int2decstr_sram(a1, dataStr);
	strrep_sram(msgStr, "%i", dataStr);
	
	printf_message_1(sink, msgStr, a2, debug);
}*/

void print_message_3(int sink, __declspec(sram) char *msg, int a1, int a2, int a3, int debug){
	__declspec(sram) char msgStr[100];
	
	strcpy_sram(msgStr, msg);
	append_int_sram(msgStr, a1);
	append_int_sram(msgStr, a2);
	append_int_sram(msgStr, a3);
	
	print_message(sink, msgStr, debug);
}

/*void printf_message_3(unsigned int sink, __declspec(sram) char *msg, int a1, int a2, int a3, int debug){
	__declspec(sram) char dataStr[10];
	__declspec(sram) char msgStr[100];
	
	strcpy_sram(msgStr, msg);
	onl_api_int2decstr_sram(a1, dataStr);
	strrep_sram(msgStr, "%i", dataStr);
	
	printf_message_2(sink, msgStr, a2, a3, debug);
}*/

void print_message_4(int sink, __declspec(sram) char *msg, int a1, int a2, int a3, int a4, int debug){
	__declspec(sram) char msgStr[100];
	
	strcpy_sram(msgStr, msg);
	append_int_sram(msgStr, a1);
	append_int_sram(msgStr, a2);
	append_int_sram(msgStr, a3);
	append_int_sram(msgStr, a4);
	
	print_message(sink, msgStr, debug);
}

/*void printf_message_4(unsigned int sink, __declspec(sram) char *msg, int a1, int a2, int a3, int a4, int debug){
	__declspec(sram) char dataStr[10];
	__declspec(sram) char msgStr[100];
	
	strcpy_sram(msgStr, msg);
	onl_api_int2decstr_sram(a1, dataStr);
	strrep_sram(msgStr, "%i", dataStr);
	
	printf_message_3(sink, msgStr, a2, a3, a4, debug);
}*/

void print_message(int sink, __declspec(sram) char *msg, int debug){

  //__declspec(sram) unsigned int message[8];
  __declspec(sram) onl_api_ctrl_msg_hdr msghdr;
  __declspec(sram) int sring = sink + ONL_PLUGIN_0_TO_XSCALE_CTRL_SRAM_RING - MESSAGE_OUT_RING_0;

  //for put buffer method:
  SIGNAL_PAIR ring_signal;
  __declspec(sram_write_reg) unsigned int data[8];

  // The compiler is associating the size of the put data with the size of the returned status
  __declspec(sram_read_reg) unsigned int status[8]; 
	//this was status[8] before... I adjusted it to seem to work w/ a single int
	//using just status instead of status[8] seems to cause a warning

  __declspec(sram) void* ring_addr =
       (__declspec(sram) void *) ((SRAM_CONTROL_RING_CHANNEL<<QDESC_CHANNEL_BITPOS) | (sring<<2));

  if(debug){
	msghdr.type = CM_DEBUGMSG;
	msghdr.mid = 1; //trying another number to see what happens
  }
  else{
	msghdr.type = CM_CONTROLMSGRSP;
  }
  msghdr.response_requested = 0;
  msghdr.num_words = 7;
  data[0] = msghdr.value;
 
  if(onl_api_str2intarr_sram_write(msg, &data[1]) < 0) //was trying to do it w/out the header
  {
    return; //should be return -1; ?  change these to return int?
  }
 
  // only proceed once the lock is available
  while(dl_sink_message_lock == LOCKED)
  {
    ctx_swap();
  }
  dl_sink_message_lock = LOCKED;
		
  do
  {
    //what happens here if we do this w/out the msghdr???
    //first arg is for status, next three are for sending message, last two for signals
    //sram_put_ring is method provided by IXP framework
    sram_put_ring(&status[0], data, ring_addr, msghdr.num_words + 1, sig_done, &ring_signal); 
	//using msg as second argument didn't seem to compile properly
	//using message as second argument seemed to get closer, but still didn't work
    wait_for_all(&ring_signal);
  }
  while(!(status[0] & 0xf0000000));
  //end code taken from other method

  dl_sink_message_lock = UNLOCKED;

}

void sram_ring_put_buffer_nwords_sram(unsigned int ring_number, __declspec(sram) unsigned int* in, unsigned int n)
{
  int i;
  SIGNAL_PAIR ring_signal;
  __declspec(sram_write_reg) unsigned int data[8];

  // The compiler is associating the size of the put data with the size of the returned status
  __declspec(sram_read_reg) unsigned int status[8];

  __declspec(sram) void* ring_addr =
       (__declspec(sram) void *) ((SRAM_CONTROL_RING_CHANNEL<<QDESC_CHANNEL_BITPOS) | (ring_number<<2));

  data[0] = in[0];
  data[1] = in[1];
  data[2] = in[2];
  data[3] = in[3];
  data[4] = in[4];
  data[5] = in[5];
  data[6] = in[6];
  data[7] = in[7];

  do
  {
    sram_put_ring(&status[0], data, ring_addr, n, sig_done, &ring_signal);
    wait_for_all(&ring_signal);
  }
  while(!(status[0] & 0xf0000000));
}


//Following two functions taken from Dr. Wong, but changed
//to make not use sram and to use ints instead of longs

// convert ascii string to unsigned long
//
static __forceinline __declspec(local_mem) unsigned int
kenw_atoi(__declspec(local_mem) char *p ) {
    __declspec(gp_reg) unsigned int x = 0;
    __declspec(gp_reg) int	     y;

    //added && *p != ' ' to keep from adding digits after ' ' -jmc5
    while( *p != '\0' && *p != ' ') {
    	if( *p != ' ' )	{
    	    x *= 10;
    	    y = *p - '0';
	    if( (y<0) || (y>9) )	return 0;
	    x += y;
	}
	p++;
    }
    return x;
}

static __forceinline __declspec(local_mem) char *
nxt_token(__declspec(local_mem) char *p ) {

    while( *p != '\0' ) {  //find SP char
    	if( *p == ' ' )	break;
	p++;
    }
    if( *p == '\0' )	return 0;
    ++p;

    while(*p != '\0' ) {		// scan past SP characters
    	if( *p == ' ' )	++p;
	else		break;
    }

    if( *p == '\0' )	return 0;
    else		return p;
}

static __forceinline void
helper_set_meta_default( __declspec(gp_reg) int nextBlk ) {
    dlNextBlock = nextBlk;

    // ignore TX and XSCALE for now

    if( nextBlk == QM ) {
	onl_api_update_ring_out_to_qm(
    		ring_in.buf_handle_lo24, 
    		(ring_in.uc_mc_bits >> 3) & 0x7,
		ring_in.qid, 
		ring_in.l3_pkt_len);
    } else if( nextBlk == DROP ) {
	onl_api_update_ring_out_to_freelist( ring_in.buf_handle_lo24 );
    } else if( nextBlk == MUX ) {
	onl_api_update_ring_out_to_mux(
    		ring_in.buf_handle_lo24, 
    		(ring_in.uc_mc_bits >> 3) & 0x7,
		ring_in.in_port,
		ring_in.plugin_tag, 
		ring_in.stats_index, 
		0, 
		ring_in.qid, 
		ring_in.l3_pkt_len);
    } else if(	nextBlk == PACKET_IN_RING_0	||
		(nextBlk == PACKET_IN_RING_1)	||
		(nextBlk == PACKET_IN_RING_2)	||
		(nextBlk == PACKET_IN_RING_3)	||
		(nextBlk == PACKET_IN_RING_4)		) {
	onl_api_update_ring_out_to_plugin(
    		ring_in.buf_handle_lo24, 
    		(ring_in.uc_mc_bits >> 3) & 0x7,
		ring_in.in_port,
		ring_in.plugin_tag,
		ring_in.stats_index, 
		0,
		ring_in.qid, 
		ring_in.nh_eth_daddr_hi32,
		ring_in.nh_eth_daddr_lo16,
		ring_in.eth_type,
		ring_in.uc_mc_bits,
		ring_in.l3_pkt_len);
    }
}

static __forceinline void
helper_set_meta_mux_tag( __declspec(gp_reg) int nextBlk ) {
    ring_out.plugin_mux_data_out.plugin_tag = nextBlk;
}

