/*
 * Copyright (c) 2007 Shakir James and Washington University in St. Louis.
 * All rights reserved
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *    3. The name of the author or Washington University may not be used 
 *       to endorse or promote products derived from this source code 
 *       without specific prior written permission.
 *    4. Conditions of any other entities that contributed to this are also
 *       met. If a copyright notice is present from another entity, it must
 *       be maintained in redistributions of the source code.
 *
 * THIS INTELLECTUAL PROPERTY (WHICH MAY INCLUDE BUT IS NOT LIMITED TO SOFTWARE,
 * FIRMWARE, VHDL, etc) IS PROVIDED BY THE AUTHOR AND WASHINGTON UNIVERSITY 
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR WASHINGTON UNIVERSITY 
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS INTELLECTUAL PROPERTY, EVEN IF 
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * */
/*
 * File: stringSub.c 
 * Author: Jessica Codr
 * Email: jmc5@cec.wustl.edu
 * Organization: Applied Research Laboratory
 * 
 * Derived from: log-new.c
 *
 * Date Created: 07/21/08
 * 
 * Description: replace payload characters with different characters.
 *
 * Modification History:
 *
 */

#ifndef _STRING_SUB_C
#define _STRING_SUB_C


//
//
// NOTE: See pluginFramework/null.c (null plugin) 
//       for description pre-processors values
//
//

#include "plugin_api.h"
//#include "plugin_dl.h"
//#include "scratch_rings_WU.h"
#include "sram_rings_WU.h"
#include "pluginHelpers.h"

#define EOS '\0'

//-----------------------------------------------------------
// Global variables/Registers
//-----------------------------------------------------------
// thread-specific globals 
__declspec(gp_reg) int dlNextBlock;  // where to send packets to next
__declspec(gp_reg) int dlFromBlock;  // where to get packets from
__declspec(gp_reg) int msgNextBlock; // where to send control messages to next
__declspec(gp_reg) int msgFromBlock; // where to get control messages from

// see ring_formats.h for struct definitions
volatile __declspec(gp_reg) plc_plugin_data ring_in;  // ring data from PLC
volatile __declspec(gp_reg) plugin_out_data ring_out; // ring data to next block


__declspec(local_mem) unsigned int timeout; // time (cycles) between callbacks
__declspec(gp_reg) unsigned int pluginId;  // plugin id (0...7)

__declspec(shared gp_reg) unsigned int count;
__declspec(shared gp_reg) unsigned int stride;

__declspec(shared sram) char findStr[10] = "hello\0\0\0\0\0";
__declspec(shared sram) char replaceStr[10] = "adieu\0\0\0\0\0";

//-----------------------------------------------------------
// Function prototypes
//-----------------------------------------------------------
void handle_pkt_user();
void handle_msg_user();
void callback_user();
void plugin_init_user();


//delcared here so can use in handle_pkt_user() w/out implicit delcaration
void default_format_out_data(unsigned int nextblock);

//stolen from NSP version of this code
int prefix(__declspec(shared sram) char* s1, __declspec(local_mem)char* s2) {	
// Return 1 if s1 is a prefix of s2.
  while (*s1 != EOS) {
    if (*s1++ != *s2++) return 0;
  }
  return 1;
}

void copy(__declspec(shared sram) char* s1, __declspec(local_mem)char* s2) {		
// Copy s1 to s2, omitting EOS.
  while (*s1 != EOS) *s2++ = *s1++;
}

void copyls(__declspec(local_mem) char* s1, __declspec(shared sram)char* s2) {		
// Copy s1 to s2, omitting EOS.
  while (*s1 != EOS && *s2 != EOS) *s2++ = *s1++;
  while (*s2 != EOS) *s2++ = EOS; //pad w/ nulls if needed
}

void handle_pkt_user()  {

    __declspec(gp_reg) onl_api_ip_hdr ipv4_hdr; // defined in ixp_lib.h
    __declspec(gp_reg) buf_handle_t buf_handle;  // see dl_buf.h, ixp_lib.h   
    __declspec(gp_reg) onl_api_buf_desc bufDescriptor;  // see dl_buf.h, ixp_lib.h
    __declspec(gp_reg) onl_api_udp_hdr udp_hdr; //not actually needed?
    __declspec(local_mem) sixteenWordsOfData payload;
    __declspec(local_mem) unsigned int bufDescPtr;  //some of these might need to sram
    __declspec(local_mem) unsigned int ipv4HdrPtr;
    __declspec(local_mem) unsigned int dramBufferPtr;
    __declspec(local_mem) unsigned int udpHdrPtr;
    __declspec(local_mem) unsigned int payloadPtr;
    __declspec(local_mem) char* q;  
 
    __declspec(sram) plugin_out_data my_ring_out; // ring data to next block, 

    __declspec(local_mem) char dataStr[9];

    __declspec(sram) char* p;
    __declspec(sram) char* end;
    __declspec(local_mem) int cnt = 0;

 //   __declspec(local_mem) char dbgmsg[28] = "got packet ";
    __declspec(local_mem) char payloadStr[16];
    
    // Read the buf handle from the input Ring
    onl_api_get_buf_handle(&buf_handle); 
    bufDescPtr = onl_api_getBufferDescriptorPtr(buf_handle);
    onl_api_readBufferDescriptor(bufDescPtr, &bufDescriptor);
    dramBufferPtr = onl_api_getBufferPtr(buf_handle);
    ipv4HdrPtr = onl_api_getIpv4HdrPtr(dramBufferPtr, bufDescriptor.offset);
    onl_api_readIpv4Hdr(ipv4HdrPtr, &ipv4_hdr);       
    udpHdrPtr = onl_api_getUdpHdrPtr(ipv4HdrPtr, ipv4_hdr.ip_hl);
    onl_api_readUdpHdr(udpHdrPtr, &udp_hdr);   

    payloadPtr = onl_api_getUdpPacketPayloadPtr(
		onl_api_getUdpHdrPtr(ipv4HdrPtr, ipv4_hdr.ip_hl));
    onl_api_ua_read_8W_dram(payloadPtr, (void *) &payload);

    q = (__declspec(local_mem) char*) &(payload.i);
	while(cnt < 16*4){
		if(prefix(findStr,q)){  //*q == 'h'){
			copy(replaceStr,q); //*q = 'a';
		}
		cnt++;
		q = ((__declspec(local_mem) char*) &(payload.i)) + cnt;
	}


/*
  onl_api_debug_message(msgNextBlock, dbgmsg);
  onl_api_int2str(payload.i[0],payloadStr);
  onl_api_debug_message(msgNextBlock, payloadStr);
  onl_api_int2str(payload.i[1],payloadStr);
  onl_api_debug_message(msgNextBlock, payloadStr);
  onl_api_int2str(payload.i[2],payloadStr);
  onl_api_debug_message(msgNextBlock, payloadStr);
  onl_api_debug_message_sram(msgNextBlock, findStr);
  onl_api_debug_message_sram(msgNextBlock, replaceStr);
*/

    //1 line added after looking over charlie's code:
    onl_api_ua_write_8W_dram(payloadPtr, (void *) &payload);
	

    //more ip header data:  ip length and checksum
    ipv4_hdr.ip_sum = onl_api_ipv4Hdr_cksum16(&ipv4_hdr);
   
 
    //write/save the ip header to the "actual" packet
    onl_api_writeIpv4Hdr(ipv4HdrPtr, &ipv4_hdr);

  
    //more udp header data:  checksum    
    udp_hdr.uh_sum = onl_api_udp_cksum(&ipv4_hdr, &udp_hdr, udpHdrPtr);
 

    //write/save the udp header
    onl_api_writeUdpHdr(udpHdrPtr, &udp_hdr);

}



void handle_msg_user(){}

void callback_user() {
}


void plugin_init_user() {


}


/**
	----------------------------------------------------------------
 @User: YOU SHOULD NOT NEED TO MAKE ANY CHANGES TO THE REST OF THIS FILE
	----------------------------------------------------------------
*/





void default_format_out_data(unsigned int nextblock)
{
  __declspec(gp_reg) int out_port;
  if(nextblock == QM)
  {
    __declspec(gp_reg) dl_buf_handle_t buf_handle;
    __declspec(sram) unsigned int *buf_desc_ptr;
    __declspec(sram_write_reg) unsigned int sram_wr_regs[3];
    SIGNAL sram_sig;

    // assume unicast
    out_port = (ring_in.uc_mc_bits >> 3) & 0x7;
    ring_out.plugin_qm_data_out.out_port = out_port;

    ring_out.plugin_qm_data_out.qid = ((out_port+1) << 13) | ring_in.qid;

    ring_out.plugin_qm_data_out.l3_pkt_len = ring_in.l3_pkt_len;
    ring_out.plugin_qm_data_out.buf_handle_lo24 = ring_in.buf_handle_lo24;

  }
  else // assume MUX and ignore xscale data packets for now
  {
    // assume unicast
    out_port = (ring_in.uc_mc_bits >> 3) & 0x7;
    ring_out.plugin_mux_data_out.out_port = out_port;
    
    ring_out.plugin_mux_data_out.qid = ((out_port+1) << 13) | ring_in.qid;

    // assume pass-through
    ring_out.plugin_mux_data_out.flags = 1;

    ring_out.plugin_mux_data_out.plugin_tag = ring_in.plugin_tag;
    ring_out.plugin_mux_data_out.in_port = ring_in.in_port;
    ring_out.plugin_mux_data_out.stats_index = ring_in.stats_index;
    ring_out.plugin_mux_data_out.l3_pkt_len = ring_in.l3_pkt_len;
    ring_out.plugin_mux_data_out.buf_handle_lo24 = ring_in.buf_handle_lo24;
  }
}

/* handle packets */
void handle_pkt()
{
  dl_source_packet(dlFromBlock);

  handle_pkt_user(); //removed checksum updating

  default_format_out_data(QM);

  dl_sink_packet(QM);

}
/* handle control messages */
void handle_msg()
{
  __declspec(gp_reg) unsigned int message[8];
  __declspec(gp_reg) unsigned int i;
  __declspec(local_mem) char msgstr[12]; // = "\0\0\0\0\0\0\0\0\0\0\0\0";
  __declspec(local_mem) char return_str[12];
  __declspec(local_mem) char setfindstr[4] = "sf \0";
  __declspec(local_mem) char setreplacestr[4] = "sr \0";
  __declspec(local_mem) char bado_str[11] = "BAD OP\0"; 
  __declspec(gp_reg) onl_api_ctrl_msg_hdr hdr;

  //for(i=0; i<8; ++i) { message[i] = 0; }
  //had to unroll loop like Dr. Wong did (after adding his methods)
  message[0] = 0;
  message[1] = 0;
  message[2] = 0;
  message[3] = 0;
  message[4] = 0;
  message[5] = 0;
  message[6] = 0;
  message[7] = 0;

  dl_source_message(msgFromBlock, message);

  hdr.value = message[0];
  if(hdr.type != CM_CONTROLMSG)
  {
    return;
  }
  if(hdr.response_requested != 1)
  {
    return;
  }

  onl_api_intarr2str(&message[1], msgstr);
  //uncommenting the above line causes plugin to not work at all

  if(strncmp_lmem(msgstr, setreplacestr, 3) == 0)
  {  
     //= kenw_atoi(nxt_token(msgstr));
     copyls(msgstr + 3, replaceStr);
  }
  else if(strncmp_lmem(msgstr, setfindstr, 3) == 0)
  {  
     //= kenw_atoi(nxt_token(msgstr));
     copyls(msgstr + 3, findStr);
  }
  else
  {
    if(onl_api_str2intarr(bado_str, &message[1]) < 0)
    {
      return;
    }
  }


  onl_api_intarr2str(&message[1], msgstr);

  hdr.type = CM_CONTROLMSGRSP;
  hdr.response_requested = 0;
  hdr.num_words = 7;
  message[0] = hdr.value;

  dl_sink_message(msgNextBlock, message);
}


/* handle periodic functionality */
void callback()
{
}


/* take care of any setup that needs to be done before processing begins */
void plugin_init()
{
  /* set a default timeout value for callback of 10000 cycles */
  timeout = 10000;

  /* set the default next block to be the Queue Manager */
  dlNextBlock = QM;

  /* by default, get packets and get and put control messages from input rings
   * based on which microengine we are currently running on; this assumes a
   * default one to one mapping */
  switch(__ME())
  {
    case 0x7:
      pluginId = 0;
      dlFromBlock  = PACKET_IN_RING_0;
      msgFromBlock = MESSAGE_IN_RING_0;
      msgNextBlock = MESSAGE_OUT_RING_0;
      break;
    case 0x10:
      pluginId = 1;
      dlFromBlock  = PACKET_IN_RING_1;
      msgFromBlock = MESSAGE_IN_RING_1;
      msgNextBlock = MESSAGE_OUT_RING_1;

      break;
    case 0x11:
      pluginId = 2;
      dlFromBlock  = PACKET_IN_RING_2;
      msgFromBlock = MESSAGE_IN_RING_2;
      msgNextBlock = MESSAGE_OUT_RING_2;  
      break;
    case 0x12:
      pluginId = 3;
      dlFromBlock  = PACKET_IN_RING_3;
      msgFromBlock = MESSAGE_IN_RING_3;
      msgNextBlock = MESSAGE_OUT_RING_3;    
      break;
    case 0x13:
      pluginId = 4;
      dlFromBlock  = PACKET_IN_RING_4;
      msgFromBlock = MESSAGE_IN_RING_4;
      msgNextBlock = MESSAGE_OUT_RING_4;
      break;
    default:  // keep the compiler happy
      pluginId = 0;
      dlFromBlock  = PACKET_IN_RING_0;
      msgFromBlock = MESSAGE_IN_RING_0;
      msgNextBlock = MESSAGE_OUT_RING_0;
      break;
  }

  plugin_init_user(); // user hook
}


/* entry point */
void main()
{
  int c;

  /* do initialization */
  plugin_init();
  dl_sink_init();
  dl_source_init();

  /* get the current thread's context number (0-7) */
  c = ctx();

  if(c >= FIRST_PACKET_THREAD && c <= LAST_PACKET_THREAD)
  {
    while(1)
    {
      handle_pkt();
    }
  }
#ifdef MESSAGE_THREAD
  else if(c == MESSAGE_THREAD)
  {
    while(1)
    {
      handle_msg();
    }
  }
#endif
#ifdef CALLBACK_THREAD
  else if(c == CALLBACK_THREAD)
  {
    while(1)
    {
      callback();
    }
  }
#endif
}

#endif	// _STRING_SUB_C
