/*
 * Project 1 for CS640
 * Blastee
 *
 * Names: Will McCardell & Tristan H.
 * cs logins: mccardel & tristan
 * 
 * things to do: 
 *  check errors for recvfrom
 *  set it up so it finishes when an END is received
 *  differntiate between END and DATA packets. -- DONE
 *  get the time when the test started -- DONE
 *  get the time when it ended -- DONE...kinda
 *  figure out how long the test lasted -- DONE
 *  figure out average packets per second -- DONE?
 *  keep a running total of bytes received (of payload or of packet?)
 *      tristan thinks that it is the bytes of the packet  -- DONE
 *  keep track of number of packets received -- DONE
 *  figure out the IP address of the packets received
 *  goodOptionBitmap thing to make sure the p is set -- DONE
 */
#include <sys/types.h>
#include <sys/socket.h>

//following two lines (the netinet ones) are for ip
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <iostream>
#include <netdb.h>

#include <sys/time.h>
#include <time.h>

#include "p1_util.h"

#define secToMicro 1000000

using namespace std;

// stuff for getopt
extern char *optarg;
extern int optind, opterr, optopt;

void yellAtUser();

int main(int argc, char* argv[]) 
{
	
	int optVal;
	unsigned short blasteePort;
    struct sockaddr_in sin;
    const struct sockaddr* sin2;
    //void* buf;
    //int len;
    ssize_t numRead;
    ssize_t totalNumRead = 0;
    int numDataPkts = 0;

    struct timeval recTime;
    struct timeval startTime, endTime, totTime;
    //struct timeval endTime;
    //struct timeval totTime;
    uint64_t usTime;


    struct packet p;

    bool hasFirst = false;

    //socket!
    int s;

    char* hostName = (char*)calloc(16,sizeof(char));

    //GoodOptionBitmap. Makes sure that if the number of args is 3,
    //  then the args are the right ones too.
    int gOB = 0;

	if(argc != 3) {
		yellAtUser();
	}

    //reads in the port number for us. Quit if nothing found.
	if((optVal = getopt(argc, argv, "p:")) != -1) 
    {
        if(optVal == 'p')
        {
			//get the port we're firing packets into
			if(EOF == sscanf(optarg, "%hu", &blasteePort))
            {
                yellAtUser();
            }
			cout << "bP = " << blasteePort << endl;
            gOB++;
        }
	} 
    else 
    {
		yellAtUser();
	}

    if(gOB != 1)
    {
        yellAtUser();
    }

    //from the book...
    bzero((char *)&sin, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = INADDR_ANY;
    sin.sin_port = htons(blasteePort);
/*
    bzero((char *)&sin2, sizeof(sin2));
    sin2.sin_family = AF_INET;
    sin2.sin_addr.s_addr = INADDR_ANY;
    sin2.sin_port = htons(blasteePort);
*/
    cout << "Set up the sin\n";

    // try to get the socket set up.
    // If it fails, it takes the program down with it
    if( (s = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
    {
        perror("socket");
        exit(-1);
    }

    cout << "Socket has been set up.\n";
    //bind the socket to the prt
    if((bind(s, (struct sockaddr *)&sin, sizeof(sin)) == -1))
    {
        perror("bind");
        exit(-1);
    }

    cout << "Socket has been bound\n";

    socklen_t sa_s = sizeof(struct sockaddr_storage);
    while(1)
    {
        while((numRead = recvfrom(s, &p, sizeof(struct packet), 0, (struct sockaddr *)&sin2, &sa_s)))
        {
            //get what time we received the packet
            //we keep this outside of the packettype stuff so
            //  in the case of the pkt being a DATA, we can use 
            //  it as the time it was received, but if it was END,
            //  we can use it as the time the test ended.
            if(gettimeofday(&recTime, NULL) == -1)
            {
                perror("gettimeofday()");
                exit(-1);
            }   
            if(!hasFirst)
            {
                startTime.tv_sec = recTime.tv_sec;
                startTime.tv_usec = recTime.tv_usec;
                hasFirst = true;
            }
            
            //if the packet type is DATA
            if(p.pkt_type == 'D')
            {
                //increment the total number of bytes that have been in the 
                //  packet. Could probably just multiply the packet size by
                //  the number of packets, but meh.
                totalNumRead += ntohl(p.length);

                usTime = recTime.tv_sec * secToMicro + recTime.tv_usec;
                //so we can output the first 4 bytes

                char *shortLoad = startOfPayload(p.payload, ntohl(p.length), 4);
                if(getnameinfo(sin2, sa_s, hostName, 16*sizeof(char), NULL, 0,0))
                {
                    //uhhh
                }
                cout << "IP: " <<hostName << " Port: " << blasteePort << " Size(bytes): ";
                cout << numRead << " seq_no: " << ntohl(p.seq_no) << " time rec: ";
                cout << recTime.tv_sec << "." << recTime.tv_usec;
                cout << "s First 4 bytes of data: " << shortLoad << endl;
                free(shortLoad);

                numDataPkts++;
            }
            //otherwise, it's the End.
            else
            {
                /*
                 * need to dcide what exactly I mean by 
                 *  "numSeconds". Should we round up?
                 *  Just ignore the microseconds?
                 */
                //so it's the end...
                //  therefore we get the time.
                endTime.tv_sec = recTime.tv_sec;
                endTime.tv_usec = recTime.tv_usec;

                //get the total time it took
                timersub(&endTime, &startTime, &totTime);
                
                //just ignore the microseconds
                uint64_t numSeconds = totTime.tv_sec;

                float pPS = ((float) numDataPkts) / (float) numSeconds;
                float bPS = ((float) totalNumRead) / (float) numSeconds;

                //timersub(endTime, beginTime, totTime);
                //uint64 duration = totTime.tv_sec * secToMicro 
                //                              + totTime.tv_usec;
                cout << "Test concluded.\n";
                cout << "Total Packets: " << numDataPkts << endl;
                cout << "Total bytes: " << totalNumRead << endl;
                cout << "Avg Packets per Second: " << pPS << endl;
                cout << "Avg Bytes per Second: " << bPS << endl;

                //uint64_t dur = totTime.tv_sec * secToMicro + totTime.tv_usec;
                cout << "Duration of test: " << totTime.tv_sec << "." << totTime.tv_usec << "s" << endl;

                goto endProg;
            }

        }
    }
endProg:

    //that's all folks!
    return 0;
}

void yellAtUser()
{
    cout << "Incorrect usage. Please try again. \n";
    cout << "blastee -p <port>\n";
    exit(-1);
}
