//TODO:
//* Some message handlers and some way of registering message handlers perhaps??
//  Message handling will be needed for joins and ad-hoc broadcast anyway so
//  might as well put general message handling in here
//* join for adhoc networks
//* n-hop broadcast for n-hop networks
//*   

#define DEBUG 1

#include "network_interface.h"
#include "marshall.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <map>
#include <set>
#include <vector>

class network_client : public network_interface{

 protected:

  std::vector<node_id> peer_list;
  node_id client_id;
  message_id msg_id;
  pthread_mutex_t msg_id_lock;
  int broadcast_sockfd;
  int send_sockfd;

  struct pending_msg {
    node_id replier;
    bool replied;
    pthread_mutex_t lock;
    pthread_cond_t wait;
  };
  

  std::map<node_id, std::set<int>*> received_msgs;
  std::map<message_id, pending_msg*> pending_msgs;
  pthread_mutex_t pending_lock;

  bool known_node(node_id id);
  
  unsigned int get_msg_id();
  
  //move message header and message into buf
  void attach_header(network_protocol::message_header *h, char *msg, int len, 
                     char *buf);

  //basic send to another client
  virtual int basic_send(node_id dest, char *msg, int len);
  
  //basic broadcast to the entire subnet
  virtual int basic_broadcast(char *msg, int len);

  
  
  //message handler for message that are receive, override this method to 
  //handle messages in different ways
  virtual void message_handler(node_id sender, network_protocol::message_header *h,
                               char *msg, int len);
  
  
 public:
  static const int INCOMING_PORT = 6829;
  static const node_id BROADCAST_ADDRESS = ~0;
  static const int ACK_TIMEOUT = 5;
  network_client();

  ~network_client();

  //find other nodes in the system, returns 0 for found
  virtual int join();
  
  //send a message to some number of other nodes dictated by hops
  virtual int broadcast(int hops, char *msg, int len); 
  
  //send a message to another node in the system, this must be a known node
  virtual int send(node_id dest, char *msg, int len);   

  //get nodes in the system
  virtual std::vector<node_id> neighbors();
  
  //a receive function that just loops forever receiving and 
  //calling the message handler 
  void receive();

};
