//*****************************************************************************
//
// Program:     vsend -- Programming Project 2
//
// Author:      Travis Dillon
//
// Class:       CS 444
// Email:       tdillon@ace.cs.ohiou.edu
//
// Description: This is a program to test the vbridge. This program sends a
//              packet to another computer on the virtual lan.
//
// Date:        January 14, 2005
//
//*****************************************************************************


#include <stdlib.h>
#include <iostream.h>
#include <vrouter.h>
#include <unistd.h>

#define DEFAULT_GROUP 6
#define BUFFER_SIZE 512
#define INTERFACE_NAME_LENGTH 100
#define DEFAULT_MSECS 1000

using namespace std;

void option_h();
void option_l(lanlist* all_lans, _lan* lan_ptr);
void option_g(int argc, char** argv, unsigned& group, int& i);
void option_i(int argc, char** argv, unsigned& group, int& i,
              char*& interface, lanlist* all_lans);
void option_t(int argc, char** argv, int& msecs, int& i);
void option_c(int argc, char** argv, int& count, int& i);
void make_buffer(char** argv, char buf[], lanlist* all_lans, int& i);

typedef struct _Ethernet
{
   unsigned char destination[7];
   unsigned char source[7];
   unsigned char type[3];
}Ethernet;


int main(int argc, char *argv[])
{
   int i, packet_size(0), msecs(DEFAULT_MSECS), count(-1);
   char buf[BUFFER_SIZE], *interface;
   unsigned group = DEFAULT_GROUP;
   bool debug_on = false, infinite_loop = true;
   lanlist* all_lans = whichlans();
   _lan* lan_ptr;
   INTERFACE* iface;

   if(all_lans == NULL)
   {
      cout << "whichlans() failed and returned NULL\n";
      exit(EXIT_FAILURE);
   }
   lan_ptr = all_lans -> lan;
   interface = lan_ptr -> lname;

   for(i=1; i < argc - 1; ++i)  //parse the command line
   {
      if(argv[i][0] != '-')  //at this point an argument must have a '-'
      {
         cout << "Invalid option - \""  << argv[i] << "\".\n";
         option_h();
      }
      switch(argv[i][1])
      {
         case 'd': debug_on = true; break;
         case 'l': option_l(all_lans, lan_ptr); break;
         case 'g': option_g(argc, argv, group, i); break;
         case 'i': option_i(argc, argv, group, i, interface, all_lans); break;
         case 't': option_t(argc, argv, msecs, i); break;
         case 'c': option_c(argc, argv, count, i); break;
         case 'h': option_h(); break;
         default:
         {
            cout << "Invalid option - \""  << argv[i] << "\".\n";
            option_h();
            break;
         }
      }   
   }

   make_buffer(argv, buf, all_lans, i);

   if(debug_on)
   {
      cout << "interface = " << interface << "\ngroup = " << group
           << "\nmsecs = " << msecs << "\ncount = " << count << endl << endl;
   }

   iface = openinterface(interface, group);
   if(iface == NULL)
   {
      cout << "openinterface failed.\n";
      exit(EXIT_FAILURE);
   }

   if(count > 0) infinite_loop = false;

   while(count > 0 || infinite_loop)  //send packets
   {
      packet_size = writepacket(iface, buf, 101);
      if(packet_size < 0)
      {
         cout << "\nERROR in writepacket.\n";
         exit(EXIT_FAILURE);
      }
      usleep(msecs);  //wait to send next packet
      --count;
   }

   closeinterface(iface);
   exit(EXIT_SUCCESS);
}


//****************************************************************************
//
// Function:   option_h
//
// Purpose:    If the -h flag is given in the command line or someone enters
//             an invalid command argument, this will print out options.
//
// Parameters: none
//
// Calls:      exit
//
//****************************************************************************


void option_h()
{
   cout << "-d        To print debugging information.\n"
        << "-l        List all interfaces and exit.\n"
        << "-g NUM    Use group NUM. If NUM is ommitted then " << DEFAULT_GROUP
        << " will be used.\n"
        << "-i IFACE  Use interface IFACE. Use first interface"
        << " if IFACE is ommitted.\n"
        << "-t MSECS  Send packet every MSECS milliseconds, " << DEFAULT_MSECS
        << " by default.\n"
        << "-c COUNT  Send COUNT packets or infinitely many for default.\n"
        << "-h        Print a quick summary of the command line arguments"
        << " and exit.\n";

   exit(EXIT_SUCCESS);
}


//****************************************************************************
//
// Function:   option_l
//
// Purpose:    If the -l flag is given in the command line this will print
//             all the names and addresses of network devices on a computer.
//
// Parameters: all_lans - linked list that holds all network addresses and
//                        names of them on the computer
//             lan_ptr  - pointer to the head of the linked list
//
// Calls:      exit
//
//****************************************************************************


void option_l(lanlist* all_lans, _lan* lan_ptr)
{
   for(int j(0); j < all_lans -> cnt; ++j)
   {
      cout << "name = " << lan_ptr -> lname << endl;
      cout << "addr = " << lan_ptr -> hwaddr << endl << endl;
      lan_ptr = lan_ptr -> next;
   }
   exit(EXIT_SUCCESS);
}


//****************************************************************************
//
// Function:   option_g
//
// Purpose:    If the -g NUM flag is given in the command line this will
//             change the group to NUM as long as NUM is in [0, 63]
//
// Parameters: argc - number of command line arguments
//             argv - the commmand line arguments
//             i - position in the command line arguments
//
// Calls:      none
//
//****************************************************************************


void option_g(int argc, char** argv, unsigned& group, int& i)
{
   if(i < argc - 2 && argv[i + 1][0] != char(45))
   {  //true if -g isn't last command and -g has numbers after it
      group = argv[++i][0] - 48;  //ascii for zero is 48
      if(argv[i][1] != '\0')  //true if the group is greater than ten      
      {
         group = (group * 10) + (argv[i][1] - 48);
         if(argv[i][2] != '\0')  //true if the group is greater than 99
         {
            cout << "INVALID group number. Defaulting to "
                 << DEFAULT_GROUP << ".\n";
            group = DEFAULT_GROUP;
         }
      }
   }
   if(group > 63)  //valid range of group numbers is 0 - 63
   {
      cout << "INVALID group number. Defaulting to " << DEFAULT_GROUP << ".\n";
      group = DEFAULT_GROUP;
   }
}


//****************************************************************************
//
// Function:   option_i
//
// Purpose:    If the -i IFACE flag is given in the command line this will
//             change the interface name to IFACE if it is valid.
//
// Parameters: argc - number of command line arguments
//             argv - the commmand line arguments
//             group - the group the packets are sent to
//             i - position in the command line arguments
//             interface - the name of the interface being used
//             all_lans - linked list that holds all network addresses and
//                        names of them on the computer
//
// Calls:      strncmp
//
//****************************************************************************


void option_i(int argc, char** argv, unsigned& group, int& i,
              char*& interface, lanlist* all_lans)
{
   _lan* temp_lan_ptr = all_lans -> lan;
   bool good_iface_name = false;

   if(i < argc - 2 && argv[i + 1][0] != char(45))  //check if next arg starts
   {                                              //with a '-'
      ++i;
      for(int j(0); j < all_lans -> cnt; ++j)
      {
         if(strncmp(temp_lan_ptr -> lname, argv[i], INTERFACE_NAME_LENGTH) == 0)
         {  //the given arguemnt to -i is a good one.
            good_iface_name = true;
         }
         temp_lan_ptr = temp_lan_ptr -> next;
      }
      if(good_iface_name)
      {  //the given IFACE is a valid one, let's use it.
         interface = argv[i];
      }
      else
      {  //the given argument to -i was invalid so we'll use the first one
         cout << "INVALID interface name. Defaulting to " << interface << endl;
      }
   }
}


//****************************************************************************
//
// Function:   option_t
//
// Purpose:    If the -t MSECS flag is given in the command line this will
//             change the delay between the time packets are sent. 1000 default.
//
// Parameters: argc - number of command line arguments
//             argv - the commmand line arguments
//             msecs - number of milliseconds of delay
//             i - position in the command line arguments
//
// Calls:      none
//
//****************************************************************************


void option_t(int argc, char** argv, int& msecs, int& i)
{
   if(i < argc - 2 && argv[i + 1][0] != char(45))
   {
      ++i;
      for(size_t j(0); argv[i][j] != '\0'; ++j)
      {
         msecs = ((j == 0) ? (argv[i][j] - 48) : (msecs*10 + argv[i][j] - 48));
      }
   }
}


//****************************************************************************
//
// Function:   option_c
//
// Purpose:    If the -c NUM flag is given in the command line this will
//             change the number of packets that are sent. Infinite is default.
//
// Parameters: argc - number of command line arguments
//             argv - the commmand line arguments
//             count - number of packets sent
//             i - position in the command line arguments
//
// Calls:      none
//
//****************************************************************************


void option_c(int argc, char** argv, int& count, int& i)
{
   if(i < argc - 2 && argv[i + 1][0] != char(45))
   {
      ++i;
      for(size_t j(0); argv[i][j] != '\0'; ++j)
      {
         count = ((j == 0) ? (argv[i][j] - 48) : (count*10 + argv[i][j] - 48));
      }
   }
}


//****************************************************************************
//
// Function:   make_buffer
//
// Purpose:    To make a packet.
//
// Parameters: argv - the commmand line arguments
//             buf - holds the data
//             all_lans - linked list that holds all network addresses and
//                        names of them on the computer
//             i - position in the command line arguments
//
// Calls:      none
//
//****************************************************************************


void make_buffer(char** argv, char buf[], lanlist* all_lans, int& i)
{
   size_t g = 0;
   int tens, ones, hex_to_dec(1);

   for(size_t w(0); w < 17; ++w)
   {  //this creates the destination, first 6 octets of buffer
      tens = argv[i][w];
      ones = argv[i][w + 1];
      tens = ((tens > 57) ? (tens -= 87) : (tens -= 48));
      ones = ((ones > 57) ? (ones -= 87) : (ones -= 48));
      hex_to_dec = 16 * tens + ones;
      buf[g++] = hex_to_dec;
      w += 2;
   }

   for(size_t w(0); all_lans -> lan -> hwaddr[w] != '\0'; ++w)
   {  //this creates the destination, next 6 octets of buffer
      tens = all_lans -> lan -> hwaddr[w];
      ones = all_lans -> lan -> hwaddr[w + 1];
      tens = ((tens > 57) ? (tens -= 87) : (tens -= 48));
      ones = ((ones > 57) ? (ones -= 87) : (ones -= 48));
      hex_to_dec = 16 * tens + ones;
      buf[g++] = hex_to_dec;
      w += 2;
   }
   //this creates the type, next two octets of buffer
   buf[g++] = 0;
   buf[g++] = 0;

   for(; g < 100; ++g)
   {  //this creates some junk data for the rest of the buffer
      buf[g] = g + 20;
   }
   buf[g] = '\0';
}