/*
 *
 * egress_thread.c -- Traffic Squeezer Egress Traffic Handling Thread Code
 *
 * Author: Kiran K (2006-2008)
 *         kiran_cyberpro at yahoo dot com
 *
 *
 * Traffic Squeezer is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * Traffic Squeezer is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with GNU Zebra; see the file COPYING.  If not, write to the Free
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 */


#include "egress_thread.h"
#include "bucket.h"

void *egress_thread_start(void *a)
{
        BYTE ingress_buff[ETH2_MAX_MTU];
        size_t ingress_buff_size=0;
        BYTE egress_buff[ETH2_MAX_MTU];
        size_t egress_buff_size=0;
        mymsgbuf egress_msg_buff;

        init_stats(egress_port_stats);
        egress_msg_buff.msgType=MSG_EGRESS_STATS;

        while(1) /* transfer frame from out-port to in-port */
        {

                if( rx_frame( egress_port_sock_fd, egress_buff, &egress_buff_size)) /* Rx from egress port */
                {

			/*
			 * If the packet is meant for the trafficsqueezer box, just discard the same.
			 */
                        //if(memcmp(egress_port_mac, egress_buff, 6))
                        //        continue;

                        egress_port_stats.rx++;
                        egress_port_stats.rx_bytes+=egress_buff_size;

                        switch(egress_buff[egress_buff_size-1])
			{
                        	case PYLD_PROSD_NONE :
				{
                                	/* Dont do compress */
                                	egress_buff_size--; /* remove the last byte */
					/* Tx to the ingress port - as it is */
					if(tx_frame(ingress_port_sock_fd, egress_buff, egress_buff_size))
					{
						//printf("out-port -> in-port(after decompression)\n");
                                        }
					egress_port_stats.tx++;
                                        egress_port_stats.tx_bytes+=egress_buff_size;
					break;

                        	}
				case PYLD_PROSD_COMP :  /* Compression alone enabled - decompress the payload along by passing the same to the decompressing engine */
                        	{
                           		unsigned char uncomp_buff[ETH2_MAX_MTU];
					size_t uncomp_buff_size=0;
					egress_buff_size--; /* remove the last byte */
			                if(decompress(egress_buff+ETH2_FRAME_PAYLOAD_OFFSET, \
					egress_buff_size-ETH2_FRAME_PAYLOAD_OFFSET, \
					uncomp_buff, &uncomp_buff_size)==FALSE)
                           		{
						printf("error @ decompression\n");
			   		}
                           		else
                           		{       memcpy(egress_buff+ETH2_FRAME_PAYLOAD_OFFSET, \
						uncomp_buff, uncomp_buff_size);
                                   		egress_buff_size = (uncomp_buff_size+ETH2_FRAME_PAYLOAD_OFFSET);
			                        egress_port_stats.comprsedPkts++;
                           		}

					/* Tx to the ingress port - after decompression */
                			if(tx_frame(ingress_port_sock_fd, egress_buff, egress_buff_size))
                			{
                        			//printf("out-port -> in-port(after decompression)\n");
                			}

                        		egress_port_stats.tx++;
                        		egress_port_stats.tx_bytes+=egress_buff_size;
					break;
			 	}
			 	case PYLD_PROSD_COAL : /* Coalescing alone enabled */
			 	{	int i;
					int prev_size = 0;
					ts_bucket_t egress_bucket;
					egress_buff_size--; /* remove the last byte */
					clean_bucket(&egress_bucket);
					get_bucket_PDUs( egress_buff, egress_buff_size, &egress_bucket);
					//printf("total backets in bucket = %d\n", egress_bucket.pdu_cnt);
				#if 0
					{
						int j;
						printf("==============\n");
						for(j=0; j<egress_bucket.size; j++)
						{
							printf(" 0x%x", *(egress_bucket.payload+j) );
						}
                                                printf("\n=====end=======\n");
                                        }
				#endif
					for( i=0 ; i<egress_bucket.pdu_cnt ; i++)
					{
						/* Tx to the ingress port */
						//printf("Sending packet[%d] (pdu_size=%d)\n", i, \
						//	egress_bucket.pdu_size[i] );
                                               	if(tx_frame(ingress_port_sock_fd, \
							egress_bucket.payload+prev_size,\
							egress_bucket.pdu_size[i] ))
                                               	{
                                                       	//printf("out-port -> in-port(after decompression)\n");
                                               	}
				#if 0
					{
						int j;
						printf("======pkt(%d)=tot(%d)==prev_size(%d)=====\n", i, \
							egress_bucket.pdu_size[i], prev_size);

                                                for(j=0; j<egress_bucket.pdu_size[i]; j++)
                                                {
                                                	printf(" 0x%x", \
								*(egress_bucket.payload+prev_size+j) );
                                                }
                                                printf("\n=====end=======\n");
                                        }
 				#endif
						prev_size += egress_bucket.pdu_size[i];

                                      		egress_port_stats.tx++; // count of multiple frames got un-coalesced
                                      		egress_port_stats.tx_bytes+=egress_buff_size;
					}
					egress_port_stats.coalesedPkts++; // there is only one frame got un-coalesced
					break;
				}
				case PYLD_PROSD_COAL_COMP: /* Coaleascing and Compression both enabled */
                        	{
				        // First decompress the frame
                           		unsigned char uncomp_buff[ETH2_MAX_MTU];
			                size_t uncomp_buff_size=0;
                        		egress_buff_size--; /* remove the last byte */
			                if(decompress(egress_buff+ETH2_FRAME_PAYLOAD_OFFSET, \
						egress_buff_size-ETH2_FRAME_PAYLOAD_OFFSET, \
						uncomp_buff, &uncomp_buff_size)==FALSE)
                           		{
						printf("error @ decompression\n");
			   		}
                           		else
                           		{       memcpy(egress_buff+ETH2_FRAME_PAYLOAD_OFFSET, \
							uncomp_buff, uncomp_buff_size);
                                   		egress_buff_size = (uncomp_buff_size+ETH2_FRAME_PAYLOAD_OFFSET);
			                        egress_port_stats.comprsedPkts++;
                           		}

					//Now do Un-Coalescing
					{	int i;
						int prev_size = 0;
						ts_bucket_t egress_bucket;
						
						clean_bucket(&egress_bucket);
						get_bucket_PDUs( egress_buff, egress_buff_size, &egress_bucket);

						for( i=0 ; i<egress_bucket.pdu_cnt ; i++)
						{
							/* Tx to the ingress port */
							//printf("Sending packet[%d] (pdu_size=%d)\n", i, \
							//egress_bucket.pdu_size[i] );
                                               		if(tx_frame(ingress_port_sock_fd, \
								egress_bucket.payload+prev_size,\
								egress_bucket.pdu_size[i] ))
                                               		{
                                                       		//printf("out-port -> in-port(after decompression)\n");
                                               		}

							prev_size += egress_bucket.pdu_size[i];
                                               		egress_port_stats.tx++; // count of multiple frames got un-coalesced
                                               		egress_port_stats.tx_bytes+=egress_buff_size;
						}
						egress_port_stats.coalesedPkts++; // there is only one frame got un-coalesced
			 		}
					break;
				}
			} /* end of switch */

                        /*
                           tx the message (egress stats)
                        */
                        {
                              memcpy( &egress_msg_buff.stats, &egress_port_stats, sizeof(ts_stats_t));
                              egress_msg_buff.mtype=MESSAGE_TYPE;
                              send_message(stats_msgQ_id, &egress_msg_buff);
                        }
                }
        }
}



