/*
  to do: v1
  1) how many entries in array (Hemant will let us know)
  2) First_free_cache_ptr = 0x2000
  3) Cache base = 0x2001
  4) initialize double linked list on initialization
  5) each entry in array should have next ptr initialized to NULL
  6) NOTE: cache entry = 8* 32 (bits)
  7) for polling
  - #define 2 32-bit words (1 words is for the function call (i.e. lookup cache entry =1 , remove cache entry = 2)
  - we signal to Hemant when we're done.

  to do:v2
  1) finalizing the polling()
  2) Finishing the writing cache


*/

/********************************************INCLUDE ****************************************************************/
#include "c:/tornado/target/h/stdio.h"
#include "c:/ixp1200/include/uclo.h"
#include "c:/ixp1200/include/libd.h"
#include "c:/tornado/target/h/stdlib.h"
#include "c:/ixp1200/include/hal_sram.h"
#include "c:/ixp1200/include/mem_map.h"
#include "c:/tornado/target/h/inetlib.h"
#include "c:/tornado/target/h/taskLib.h"
#include "unistd.h"
#include "config_1200.h"
#include "net_app.h"
#include "ueng.h"
#include "netinet/in.h"
#include <iostream>
#include <string.h> 
/********************************************INCLUDE ****************************************************************/


/*******************************************DEFINE*******************************************************************/
#define HASH_ARRAY_SIZE 256
/* Hemant will set this address to contain FREELIST_START */
#define CACHE_FREELIST_PTR 0x2000
/* 256 hash array entries, each entry is 32 bytes (256 bits)
   each number in hex is 32 bits
   each cache entry is 8 numbers in hex
   256 * 8 = 2048
   2048 in hex = 800
   2001 + 800 = 2801
*/

#define SRAM_CACHE_START 0x2001
#define SRAM_CACHE_END 0x2801

/* 6911 possible cache entries */
#define FREELIST_START 0x2802
#define FREELIST_END 0xFFA2

/* each policy takes 4 hex numbers
   We set a max of 50 policies
   --> 50 * 4 = 200 hex numbers
   -->200 dec = C8 hex
*/

#define SRAM_POLICY_START 0x10001
#define SRAM_POLICY_END 0x100C9

/* Each filter takes 4 hex numbers */
   
#define SRAM_FILTER_START 0x100CA
#define SRAM_FILTER_END 0x15000

#define SRAM_LOG_START 0x15001
#define SRAM_LOG_END 0x1FFF5

/* Used for Polling */
/* Logic: If we have only 2 registers (1 for the func, 1 for the param), 
then we run the risk of head of line blocking. Peter suggests to have
> 1 place to poll so Hemant can dump packets (that are the beginning of a flow)
if they happen to come in a burst
- If have chosen 5 such polling places. That can be increased/decreased.
*/

#define ORDER_ADDRESS

#define POLL_ADDRESS 0x1FFE0 /* Using that thread 1 uses Poll Address, and the paramter uses
				the address next over*/
#define NUM_THREADS 12


/* well-known values that tell the control plane what function to call */
#define DO_CACHE_VALIDATION 1
#define DONE_REQUEST 0
#define DO_CACHE_REMOVAL 2



#define SRC_PORT 1 // (16 bits)
#define DST_PORT 2
#define SRC_ADDR 3 // (32 bits)
#define DST_ADDR 4
#define PROTOCOL 5 //  (8 bits)

/*******************************************DEFINE*******************************************************************/


/********************************************EXTERN *****************************************************************/
//Function declarations
static SramUnit* sram;
int number_policies;
#if defined(__cplusplus)
extern "C"
{
//extern 

extern int init();
extern int initialize_poll_sram();
extern int initialize_log_sram();
extern int initialize_policy_table();
extern int initialize_cache_table();
extern int polling();
extern int packet_validation(struct cache_entry&, int *, int);
extern int policy_table_lookup(); // return 0 if allowable; -1 to deny.

extern int policy_decision(char*); 
extern struct Admin_struct read_policy_pkt(char*);
extern int add_policy(struct Admin_struct &); //add to policy table; return -1 if error
extern int flush_policy(); //used to flush policy table and all filters
extern int flush_cache(); //flush the cache if new policy is added
extern int write_cache(); //called within policy_table_lookup
extern int write_log(); //called within policy_table_lookup
extern int packetize_log_table(); //called within policy_table_lookup()
				  //if after writing a log, the log table is full, 
				  //packetize the table immediately
extern int send_log(); //called by packetize_log_table
extern int stringToHex(char *number);
extern int add_filter(struct Filter *);
extern int policy_add(int,int,int);
extern int validate_flow(int,int,short,short,int);
extern int Admin_read();
}
#endif
/********************************************EXTERN *****************************************************************/
struct Filter{
  int policy_id; //which policy it belongs to
  int filter_id; //type of filter (e.g src port)
  int filter_value; //either contains the value or the address (pointer) to the value
  int filter_mask; //applies if masks are necessary (we know if its necessary by looking at the 			//policy_id
};

struct Admin_struct{
	int whattodo;
	int numfilters;
	struct Filter filts[6];
};



/* 12 bytes total
   12 * 8 = 106 bits
*/

/* We don't need a previous pointer in policy because we flush the policy table upon deletion of a policy
*/
 
struct policy{
  int policy_id;
  int num_filters;
  int next;
  int forward; // 1 is forward/ 0 is drop
};

//= 8 * 4 bytes
struct cache_entry{
  /* 1st LSB - drop or forward packet
     2nd LSB - is logging required
     1st MSB - null entry or used
     6 bits are still free to be used
  */	  
  char bitvector; //(1 byte)	
  char protocol; //(1 byte) 
  short int counter; // (2 bytes) - counts # of packets in flow
  int src_addr; //(4 bytes)
  int dst_addr; //(4 bytes)
  short int src_port; //(2 bytes)
  short int dst_port; //(2 bytes)
  int string_ptr; //(4 bytes) - ptr to the string we want to search for in memory
  int func_ptr; //(4 bytes) - ptr to the function we give the string to, to search for in the packet
  int next; //(4 bytes)
  int prev; //(4 bytes)
  //we will next extra bits to decide what extended functions to call
};

//we log at the end of the flow
struct log{
	short int src_port; //(2 bytes)
	short int dst_port; //(2 bytes)
	int src_addr; //(4 bytes)
	int dst_addr; //(4 bytes)
	char protocol; //(1 byte)
	short int counter;
};
	
int init(){
  /* Note: srv.sin_addr.s_addr is a 32-bit IPv4 address that is
     network byte ordered
  */
  int  x=0;
  struct sockaddr_in srv; 
  sram = SRAM_Attach();
  initialize_policy_table();
  initialize_cache_table();
  initialize_log_sram();
  initialize_poll_sram();
  /* Initialize the Free list head ptr */
  sram->write(CACHE_FREELIST_PTR, FREELIST_START);  



  /* The following is a hack to manual configure pre-hashed values in to
     the cache array - note that the base of the cache must be added to these 
     values 
  */
  // Hash values:
  // 1 0x518



  //Do initialization
  //1st initialize the filters/policies
  //2nd initialize hash array/initialize free cache entries to point to the next guy
  //3th initialize log sram


// polling();
}

int initialize_poll_sram(){
   unsigned int i;
   for(i = POLL_ADDRESS; i <= NUM_THREADS*2+POLL_ADDRESS; i++){
	sram->write(i,0);
   }
    
}


int initialize_log_sram(){
  unsigned int i;
  for(i = SRAM_LOG_START; i < SRAM_LOG_END; i++){
    sram->write(i, 0);
  }
}

int initialize_policy_table(){
  int i;
  unsigned int addr = SRAM_POLICY_START;
  static SramUnit* sram;
  printf("Clearing Policy Table\n");
  sram = SRAM_Attach();

  for(i =addr; i < SRAM_POLICY_END; i++){
    sram->write(i, 0);
  }
  for (i = SRAM_FILTER_START; i< SRAM_FILTER_END;i++){
	sram->write(i,-1);
  }
  number_policies =0;
}

int initialize_cache_table() {
  //	static SramUnit* sram;
  int flag = 0;
  unsigned int i;
  unsigned int nextaddr =0;
  /*Initialize the entire cache structure (including the free list
    and the hash array) to be 0
  */ 
  
  for(i =SRAM_CACHE_START;i<SRAM_CACHE_END;i++) {
    sram->write(i,0);
  }
  for(i =FREELIST_START;i<FREELIST_END;i++) {
    sram->write(i,0);
  }
  
  /* Start 256 entries after the cache_start, and initialize the
     free list such that each entry points to the next entry in 
     the free list
  */
  for(i= FREELIST_START;i < FREELIST_END;i = i+8){
    /* Write the previous pointer address into memory */
    
    if(i != FREELIST_START){
      sram->write(i+7,i-8);
    }
    if(i != FREELIST_END -8){
      /* Write the next pointer address into memory */
      sram->write(i+6,i+8);
    }
  }
}

int polling(unsigned int maxTimer){
  unsigned int timer=0;
  unsigned int i;
  unsigned int value;
  unsigned int ptr;
  struct cache_entry entry;
  int int_mask,size;
  unsigned int data;
  unsigned int addr;
  int order[50];
  char all_ones = (char)-1;
  unsigned int forward;
  printf("Polling-----------------------------------------------------\n");
  size =5;
  for(i=0;i<size;i++){
	order[i]=i;
  }  
  while(timer < maxTimer){
    timer++;
    taskDelay(2);
    for(i = POLL_ADDRESS; i <= 2*NUM_THREADS + POLL_ADDRESS; i+=2){
      sram->read(i, &value);
      switch(value){
      case DO_CACHE_VALIDATION:
	printf("DOING CACHE VALIDATION ----------------\n\tThread: %d",i);
	addr = i+1;
	/* Find the cache entry that the addr points to which starts at ptr */
	sram->read(addr, &ptr);
	sram->read(ptr, &data);
	printf("\n\tLocation: %x\n", ptr);
	/* Fill up the cache entry before validating */
        int_mask = 0xFF000000;
        entry.bitvector = data & int_mask;
	data = data << 8;
	entry.protocol = data & int_mask;
	/* we can ignore reading the counter since we're doing validation*/
	
	sram->read(ptr+1, &data);
	entry.src_addr = data;
	sram->read(ptr+2, &data);
	entry.dst_addr = data;
	int_mask = 0xFFFF;
        sram->read(ptr+3, &data);
	entry.dst_port = data & int_mask;
	data = data >> 16;
	entry.src_port = data & int_mask;
	/* We dont need to read the next or prev ptrs in the validation */
	forward = packet_validation(entry,order,size);
        sram->read(ptr,&data);
        data = data | (forward << 24);
        sram->write(ptr,data);
	/* Now write the cache_entry back into the SRAM */
//	write_cache(&entry, ptr);
	/* Let the Data plane know that we're done with this entry */
	sram->write(i, 0);
	sram->write(addr, 0);
	
	break;
      case DO_CACHE_REMOVAL:
	/* Remove Entry for cache - then send the log of the entry */
	break;
      case DONE_REQUEST:
	break;
      default:
	break;
      }
    }
  }
}
/*
#define SRC_PORT 1 (16 bits)
#define DST_PORT 2
#define SRC_ADDR 3 (32 bits)
#define DST_ADDR 4
#define PROTOCOL 5 (8 bits)
*/
//= 8 * 4 bytes

int does_match(struct cache_entry& entry,unsigned int i) {
  unsigned int type;
  unsigned int val;
  unsigned int mask;
  sram->read(i+1,&type);
  sram->read(i+2,&val);
  sram->read(i+3,&mask);
  switch(type){
     case SRC_PORT:
	cout << "Matching SRC_PORT" << endl;
        if(((entry.src_port & (short)mask) ^ ((short)val & (short)mask))!=0) {
		return 0; //fails
	}            
     break;
     case DST_PORT:
	cout << "Matching DEST_PORT" << endl;
        if(((entry.dst_port & (short)mask) ^ ((short)val & (short)mask))!=0) {
		return 0; //fails
	}
     break;
     case SRC_ADDR:
	cout << "Matching SRC_ADDR" << endl;
	if(((entry.src_addr & mask) ^ (val & mask))!=0){
		return 0; //fails
	}
     break;
     case DST_ADDR:	cout << "Matching DST_ADDR" << endl;

	if(((entry.dst_addr & mask) ^ (val & mask))!=0){
		return 0; // fails
	}
     break;
     case PROTOCOL:
cout << "Matching Protocol " << entry.protocol << " (entry) with " << val << " (filter)" << endl;
	if(((entry.protocol & (char)mask) ^ (val& mask))!=0){
		return 0; //fails
	}
     break; 
     default:
     	printf("We shouldn't be here\n");
        return 0;
     break;
   }
  cout << "matched" << endl;
  return 1;
}
int validate_flow(int src_addr,int dst_addr,short src_port,short dst_port, int size) {
	struct cache_entry entry;
	int order[50];
	int i =0;
	entry.src_addr = src_addr,
        entry.dst_addr = dst_addr;
	entry.src_port = src_port;
	entry.dst_port = dst_port;
	for (i = 0; i<size;i++) {
		order[i]=i;
	}
	printf(" Pass? %d",packet_validation(entry,order,size));
}
int packet_validation(struct cache_entry& entry,int order[50],int size){
  unsigned int addr = SRAM_FILTER_START;
  int type;
  unsigned int policy[50] = {0};
  unsigned int policy_id;
  unsigned int i,j;
  unsigned int policy_addr = SRAM_POLICY_START;
  unsigned int num_filters,forward;
  int match =0;
  order[0]=0;
  for(i = addr; i < SRAM_FILTER_END;i=i+4) {
       sram->read(i,&policy_id);
       if(policy_id != -1) { 
           cout << "Matching with "<< policy_id <<  endl;
           if(does_match(entry,i)){ 
	       
		policy[policy_id]++;
           } else cout << " DOESN'T MATCH " << endl;
       }
  }
  for(i = order[0],j=0;j<size;j++,i=order[j]) {
     //this needs to be updated
     sram->read(policy_addr+i*4+1,&num_filters);
     cout << i << endl; 
     printf("%d\n",policy[i]);
	
     cout << num_filters << endl;
     if(policy[i]==num_filters){
	  sram->read(policy_addr+i*4+3,&forward); //meets a rule
	  cout << "Forward ? " << forward << endl;
	  return forward;
     }
  }
  return 0;
}


/*
  Take in a control packet that specifies a policy
  Validate control packet first.
  For each filter in the policy, call add_filter

*/
int add_filter(Filter* filter){
  unsigned int i;
  //static SramUnit* sram;
   
  if(!filter){
    return ERROR;
  }

  printf("Adding Policy\n");
 // sram = SRAM_Attach();
  unsigned int group_id;
  // start search from addr to SRAM POLICY END, jumping every i
  for(i=SRAM_FILTER_START;i<=SRAM_FILTER_END;i=i+4) {
    sram->read(i,&group_id);    // find the emtpy slot
    if(group_id ==-1) { // write to the empty slot
      sram->write(i,filter->policy_id);
      sram->write(i+1,filter->filter_id);
      sram->write(i+2,filter->filter_value);
      sram->write(i+3,filter->filter_mask);
      break;
    }
  }
  return 0;
}
int add_policy(struct Admin_struct &policyitem){
      int i =0;
      for(i=0;i<policyitem.numfilters;i++){
	add_filter(&(policyitem.filts[i]));
      }
      sram->write(SRAM_POLICY_START+4*number_policies,policyitem.filts[0].policy_id);
      cout << "Number of filters " << policyitem.numfilters << endl;
      sram->write(SRAM_POLICY_START+4*number_policies+1,policyitem.numfilters);
      sram->write(SRAM_POLICY_START+4*number_policies+2,0);
      sram->write(SRAM_POLICY_START+4*number_policies+3,policyitem.whattodo);     
      number_policies++;
}


  
int delete_policy(int policy_id) {
    // first clear the filter table of that policy, then relink the list
    unsigned int addr = SRAM_FILTER_START;
    unsigned int i = 0;
    unsigned int temp =0;
    for(i=addr;i<SRAM_FILTER_END;i=i+4) {
	 sram->read(i,&temp);
	 if (temp == policy_id) {
		sram->write(i,0);
		sram->write(i+1,0);
	        sram->write(i+2,0);
  	        sram->write(i+3,0);
         }
    }
  //flush the policy table?
}

int policy_add(int policy_id,int number,int forward) {
	int value[18]= {0};
	int i =0; 
	struct Admin_struct addingpolicy;
	sram = SRAM_Attach();
	cout << endl << endl << endl << endl;
	cout.flush();
        cout << "Type = #define SRC_PORT 1 // (16 bits)
		#define DST_PORT 2
		#define SRC_ADDR 3  32 bits 
		#define DST_ADDR 4
		#define PROTOCOL 5
		" << endl;
	cout << " Policy ID: " << policy_id << endl;
	cout << " Filter Number: " << number << endl;
	cout << " Allow?: " << forward << endl;
        for(i=0;i<number;i++) {
		cout << "Filter type? " << endl; 
                cin >> value[3*i];
		cout.flush();
             	cout << value[3*i] << endl;
		cout.flush();
		cout << "Filter Value? " << endl;
		cin >> value[3*i+1];
		cout.flush();
		cout << value[3*i+1] << endl;
                cout << "Filter Mask? " <<endl;
                cin >> value[3*i+2];
		cout.flush();
		cout << value[3*i+2] << endl;
		addingpolicy.filts[i].policy_id = policy_id;
		addingpolicy.filts[i].filter_id = value[3*i];
		addingpolicy.filts[i].filter_value = value[3*i+1];
		addingpolicy.filts[i].filter_mask = value[3*i+2];
       } 
       addingpolicy.numfilters  = number;
       addingpolicy.whattodo =forward;
       add_policy(addingpolicy);
}
  
int flush_cache() {
	static SramUnit* sram;
    	printf("Flushing Cache\n"); 
    	// get sram pointers and re-initialize sram memory
    	sram = SRAM_Attach();
    
	//let X be the number of bytes in each cache entry
	for(int i = SRAM_CACHE_START; i< SRAM_CACHE_END; i++){
		sram->write(i, 0);
	}				
	
}

int write_log() {
	//called by policy_table_lookup after deciding if there is a violation
	//takes in a packet descriptor
	//need timestamp functionality (ask Hemant)
	//5-tuple
		
}

int packetize_log_table() {
}

int send_log() {
}
 
int CaddEntry(
	unsigned int address,
	unsigned int bitvector,
	unsigned int source,
	unsigned int dest,
	unsigned int ports,
	unsigned int param,	
	unsigned int func,
	unsigned int next,
	unsigned int prev)
{
unsigned int aptr;
static SramUnit* sram;
sram = SRAM_Attach();

sram->write(address, bitvector);
sram->write(address + 1, source );
sram->write(address + 2, dest);
sram->write(address + 3, ports);
sram->write(address + 4, param);
sram->write(address + 5, func);
sram->write(address + 6, next);
sram->write(address + 7, prev);
return 1;
}
int Admin_read() {
 FILE* fileptr;
 struct Admin_struct new_policy;      
 int i = 0;
 char* s = malloc(3*sizeof(struct Admin_struct));
 char* ptr;

 if((fileptr = fopen("packet_output" , "r")) == NULL){
    printf("Cannot open packet_output\n");
  }

 // char *fgets(char *s, int size, FILE *stream);
 
 if(fgets(s, 3*sizeof(struct Admin_struct), fileptr) == NULL){
   printf("Could not read the packet in\n");
 }
 
 //printf("This is the string we just read in: \n %s \n", s);
 
 /* Call Strtok to parse values in the packet */
 //  char *strtok(char *s, const char *delim);
 
 ptr = strtok(s, " ");
 new_policy.whattodo = atoi(ptr);
 printf("What to do = %d\n", new_policy.whattodo);

 ptr = strtok(NULL, " ");
 new_policy.numfilters = atoi(ptr);
 printf("Numfilters = %d\n", new_policy.numfilters);

 for(i = 0; i <new_policy.numfilters; i++){
    ptr = strtok(NULL, " ");
    new_policy.filts[i].policy_id = atoi(ptr);
    printf("Policy_id = %d\n", new_policy.filts[i].policy_id);
    ptr = strtok(NULL, " ");
    new_policy.filts[i].filter_id = atoi(ptr);
    printf("Filter_id = %d\n", new_policy.filts[i].filter_id);
  
    if(new_policy.filts[i].filter_id ==3 || new_policy.filts[i].filter_id ==4){
        ptr = strtok(NULL, " ");
	sscanf(ptr, "%x", &new_policy.filts[i].filter_value);
	printf("IP address values is %x \n",new_policy.filts[i].filter_value);
	ptr = strtok(NULL, " ");
	sscanf(ptr, "%x", &new_policy.filts[i].filter_mask);
	printf("IP mask values is %x \n",new_policy.filts[i].filter_mask);
    }
    else{
        ptr = strtok(NULL, " ");  
	new_policy.filts[i].filter_value = atoi(ptr);
	printf("IP port/protocol value is %x \n",new_policy.filts[i].filter_value);
	ptr = strtok(NULL, " ");
	new_policy.filts[i].filter_mask = atoi(ptr);
	printf("IP port/protocol mask is %x \n",new_policy.filts[i].filter_value);
    }
 }
 add_policy(new_policy);
 free(s);

}