// nrlolsr.cpp - application to run nrlolsr 
#ifndef SIMULATE
#ifdef UNIX
#include "mprselPub.h"
#endif //endif UNIX
#endif //SIMULATE
#include "protokit.h"
#include "olsr_packet_types.h"
#include "nbr_queue.h"

//#include <errno.h>
//#include <stdio.h>
//#include <signal.h>
#include <ctype.h>
#include <math.h>

#define NETWORK_DIAMETER 4
#define MAXMSSN 65536
#define OLSRMAXDELAY 65536.0 //16 bits
#define LOADMAXROUTES 3 //max number of routes to a single destination only used in loadBalancing

inline double UniformRand(double max){
  return (max * ((double)rand() / (double)RAND_MAX));
}

inline int GetLittleTime()
{
  struct timeval tv;
  ProtoSystemTime(tv);
  double current_time = ((double)tv.tv_usec)/1.0e06; 
  int hacknumber=(int)current_time;
  current_time-=(double)hacknumber;
  current_time=1.0e06*current_time;
  return (int)current_time;
}

class Nrlolsr 
{
 public:
  Nrlolsr(ProtoSocket::Notifier& theNotifier, ProtoTimerMgr& theTimer); //SetOlsrRouteMgr must be called before Start!
  bool Start();
  bool ProcessCommands(int argc, const char*const* argv);
  void Stop();

  bool SetOlsrBroadcastAddress(const char* addrname,const char* netmask);
  bool SetOlsrInterfaceAddress(const char* addrname);
  bool SetOlsrIPv4(bool ipv4mode);
  bool SetOlsrDebugLevel(int debuglvl);
  bool SetOlsrDebugLog(const char* logfilename);
  bool SetOlsrHelloInterval(double interval);
  bool SetOlsrHelloJitter(double jitter);
  bool SetOlsrHelloTimeout(double timeout);
  bool SetOlsrTCInterval(double interval);
  bool SetOlsrTCJitter(double jitter);
  bool SetOlsrTCTimeout(double timeout);
  bool SetOlsrHNAInterval(double interval);
  bool SetOlsrHNAJitter(double jitter);
  bool SetOlsrHNATimeout(double timeout);
  bool SetOlsrAllLinks(bool on);
  bool SetOlsrWillingness(int willingness);
  bool SetOlsrHysUp(double up);
  bool SetOlsrHysDown(double down);
  bool SetOlsrHysAlpha(double a);
  bool SetOlsrHysOff(bool off);
  bool SetOlsrHNAOff(bool off);
  bool SetOlsrHNAFile(const char* filename);
  bool SetOlsrQos(const char* qosvalue);
  bool SetOlsrRouteTable(ProtoRouteMgr *theRouteMgr);

  ProtoSocket& GetSocket() {return socket;}
  
protected:
  //important needed referances
  ProtoTimerMgr *timerMgrPtr;
  ProtoRouteMgr *realRouteTable;
  ProtoRouteTable initialRouteTable;

  ProtoAddress invalidAddress;
  // options
  int            olsrDebugValue;
  int            noerrors;
  bool           allLinks;
  int            localWillingness;

  //olsr class variables
  ProtoAddress myaddress;  //IP Address of this node ***switch to network class***

  bool           userDefBroadcast;
  ProtoAddress userDefNetBroadAddr; //address to use for adding network 
  unsigned int   userDefBroadMaskLength; //user defined subnet mask length

  ProtoAddress netBroadAddr; //address to use for adding network 
  unsigned int   broadMaskLength; //subnet mask length
  ProtoAddress broadAddr; //broadcast address actually broadcast address

  char           interfaceName[256]; //eth0 eth1 etc....
  unsigned int   interfaceIndex;
  UINT16         seqno;  //Sequence Number
  int            bid;    //Broadcast ID 
  int            mssn;   //mpr selector list number

  double         Hello_Interval;
  UINT8       Mantissa_Hello_Interval;
  UINT8       Mantissa_Hello_Hold_Interval;
  double         Hello_Timeout_Factor;
  double         Hello_Jitter; //% of Hello_Interval
  double         Neighb_Hold_Time;
  double         D_Hold_Time;

  double         TC_Interval;
  double         TC_Timeout_Factor;
  double         TC_Jitter; //% of TC_Interval
  double         Top_Hold_Time;

  double         HNA_Interval;
  double         HNA_Timeout_Factor;
  double         HNA_Jitter; //% of HNA_Interval
  double         HNA_Hold_Time;

  ProtoAddress::Type    ipvMode;
  unsigned int          hostMaskLength;

  unsigned int            qosvalue;
  bool           dohna;
  bool           hnaFromFile;
  
  //historisis variables
  double T_up;          //neighbor threshold 0-1
  double T_down;        //neighbor threshold used with time outs and time out timer 0-1 less than T_up
  double alpha;         //decides rate of change smaller number allow for faster rates of neighbor chages between 0-1

  // shared memory variables
#ifndef SIMULATE
#ifdef UNIX
  MPRSELHandle handle_mprsel; // handle to shared memory used by other processes
  MPRSELPosition mprselectors; //list of ipv4 address who are mpr selectors
#endif //ifdef UNIX
#endif//SIMULATE
  // lists
  NBRQueue       mprSelectorList;  //list of mpr selector nodes
  NBRQueue       nbr_list;         //list of neighbors
  NBRQueue       nbr_2hop_list;    //list of 2 hop neighbors
  NBRQueue       duplicateTable;   //list of recently recieved packets
  NBRQueue       forwardTable;     //list of recently forwared packets
  NBRQueue       topologySet;      //list of topology tuples 
  NBRQueue       routeTable;       //list of routes
  NBRQueue       extraQueue;       //extra queue to use
  NBRQueue       oldRouteTable;    //temp list of routes

  NBRQueue       hnaAddresses;     //list of local hna info
  NBRQueue       hnaSet;           //list of hna tuples
  NBRQueue       hnaRoutes;        //list of hna routes

  
private:

  //  static void SignalHandler(int sigNum);
  // timed functions
  bool OnHelloTimeout(ProtoTimer &theTimer);
  bool SendHello();
  bool sendHelloTimerOn;

  bool OnTcTimeout(ProtoTimer &theTimer);
  bool SendTc();
  bool sendTcTimerOn;

  bool OnHnaTimeout(ProtoTimer &theTimer);
  bool SendHna();
  bool sendHnaTimerOn;
  // recieving functions
  void OnSocketEvent(ProtoSocket &thesocket,ProtoSocket::Event theEvent);
  // object variables
  //ProtoDispatcher dispatcher;
  ProtoSocket socket;
  ProtoTimer hello_timer;
  ProtoTimer hello_jitter_timer;
  ProtoTimer tc_timer;
  ProtoTimer tc_jitter_timer;
  ProtoTimer hna_timer;
  ProtoTimer hna_jitter_timer;

  // helper functions  
  int update_nbr(ProtoAddress id,int status,UINT16 delayValue);
  int update_nbr(ProtoAddress id,int status,UINT16 delayValue,UINT8 Vtime,UINT8 willingness);
  void update_2hop_nbr(ProtoAddress onehop_addr,ProtoAddress twohop_addr);
  void remove_2hop_link(ProtoAddress oneaddr,ProtoAddress twoaddr);
  
  void addHnaInfo(ProtoAddress gwaddr,ProtoAddress subnetaddr,ProtoAddress subnetmask,UINT8 Vtime);

  void addTopologyInfo(ProtoAddress T_last,ProtoAddress T_dest, UINT16 T_seq, UINT16 delayvalue, UINT8 Vtime);
  int updateTopology(ProtoAddress T_last, UINT16 T_seq);
  
  void update_mprselector(ProtoAddress id, int status);
  void selectmpr();
  void makempr(NbrTuple *parent);

  void makeNewRoutingTable();

  void addHnaRoutes();

  int WasForwarded(ProtoAddress addr,UINT16 dseqno);
  void addForwarded(ProtoAddress addr,UINT16 dseqno);

  int IsDuplicate(ProtoAddress addr,UINT16 dseqno);
  void addDuplicate(ProtoAddress addr,UINT16 dseqno);

  //printing and debuging functions
  void printDuplicateTable(int debuglvl); //prints duplicate table
  void printRoutingTable(int debuglvl); //prints routing table
  void printTopology(int debuglvl); //prints topology links
  void printLinks(); //prints the known links in the network debuglvl >=1
  int checkCurrentTable(int callvalue); // does some validity checking on internal tables to make sure everything is ok
  void printCurrentTable(int debuglvl); //prints neighbor table with interconnections and neighbor status
  void printHnaTables(int debuglvl); // prints hna tables 
  void printHnaLinks();//prints the known hna associations in the network debuglvl >=2
  
  //purging functions
  void nb_purge();  //cleans up the neighbor table
  void for_purge(); //cleans up the was forwarded table
  void dup_purge(); //cleans up the duplicate table
  void hna_purge(); //cleans up the hna table
  void top_purge(); //cleans up the topology table made from tc messages.

  //other helper functions
  bool discoverAndSetHNAs(char* ignoreDev); //discovers hnas to broadcast (only currently working in UNIX and only in IPv4 mode)
  void updateSM(); //updates shared memory list of mprselectors
  UINT8 doubletomantissa(double timeinseconds);
  double mantissatodouble(UINT8 mantissatime);
};
