#include "constants.h"
#include "mdp_client.h"
#include <poll.h>
#include <stdio.h>
#include <unistd.h>
#include "str.h"

struct item{
  // simple tree structure
  struct item *_left;
  struct item *_right;
  char key[32];
  char value[128];
  time_ms_t expires;
};

struct item *root=NULL;

static struct item *create_item(const char *key){
  struct item *ret=calloc(1,sizeof(struct item));
  strncpy(ret->key,key,sizeof(ret->key));
  ret->key[sizeof(ret->key) -1]=0;
  return ret;
}

static struct item *find_item(const char *key){
  struct item *item = root;
  
  while(item){
    int c=strcmp(item->key, key);
    if (c==0)
      return item;
    if (c<0){
      item = item->_left;
    }else{
      item = item->_right;
    }
  }
  return NULL;
}

static void add_item(char *key, char *value){
  struct item *item = root, **last_ptr=&root;
  while(item){
    int c=strcmp(item->key, key);
    if (c==0){
      c=strcmp(item->value, value);
      if (c==0){
	item->expires = gettime_ms()+1200000;
	return;
      }
    }
    if (c<0){
      last_ptr = &item->_left;
    }else{
      last_ptr = &item->_right;
    }
    item = *last_ptr;
  }
  
  *last_ptr = item = create_item(key);
  
  strncpy(item->value,value,sizeof(item->value));
  item->value[sizeof(item->value) -1]=0;
  // expire after 20 minutes
  item->expires = gettime_ms()+1200000;
  // used by tests
  fprintf(stderr, "PUBLISHED \"%s\" = \"%s\"\n", key, value);
}

static void add_record(){
  int ttl;
  overlay_mdp_frame mdp;
  
  if (overlay_mdp_recv(&mdp, MDP_PORT_DIRECTORY, &ttl))
    return;
  
  if (mdp.packetTypeAndFlags&MDP_NOCRYPT){
    fprintf(stderr, "Only encrypted packets will be considered for publishing\n");
    return;
  }
  
  // make sure the payload is a NULL terminated string
  mdp.in.payload[mdp.in.payload_length]=0;
  
  char *did=(char *)mdp.in.payload;
  int i=0;
  while(i<mdp.in.payload_length && mdp.in.payload[i] && mdp.in.payload[i]!='|')
    i++;
  mdp.in.payload[i]=0;
  char *name = (char *)mdp.in.payload+i+1;
  char *sid = alloca_tohex_sid(mdp.in.src.sid);
  
  // TODO check that did is a valid phone number
  
  char url[256];
  snprintf(url, sizeof(url), "sid://%s/local/%s|%s|%s", sid, did, did, name);
  add_item(did, url);
}

static void respond(char *token, struct item *item, char *key){
  if (!item)
    return;
  
  int c = strcmp(item->key, key);
  if (c<=0)
    respond(token, item->_left, key);
  if (c==0 && item->expires > gettime_ms())
    printf("%s|%s|\n",token,item->value);
  if (c>=0)
    respond(token, item->_right, key);
}

static void process_line(char *line){
  char *token=line;
  char *p=line;
  while(*p && *p!='|') p++;
  *p++=0;
  char *did = p;
  while(*p && *p!='|') p++;
  *p++=0;
  
  respond(token, find_item(did), did);
  printf("DONE\n");
  fflush(stdout);
}

static void resolve_request(){
  static char line_buff[1024];
  static int line_pos=0;
  
  set_nonblock(STDIN_FILENO);
  
  int bytes = read(STDIN_FILENO, line_buff + line_pos, sizeof(line_buff) - line_pos);
  
  set_block(STDIN_FILENO);
  
  int i = line_pos;
  int processed=0;
  line_pos+=bytes;
  char *line_start=line_buff;
  
  for (;i<line_pos;i++){
    if (line_buff[i]=='\n'){
      line_buff[i]=0;
      if (*line_start)
	process_line(line_start);
      processed=i+1;
      line_start = line_buff + processed;
    }
  }
  
  if (processed){
    // squash unprocessed data back to the start of the buffer
    line_pos -= processed;
    bcopy(line_buff, line_start, line_pos);
  }
}

int main(int argc, char **argv){
  struct pollfd fds[2];

  // bind for incoming directory updates
  sid_t srcsid;
  if (overlay_mdp_getmyaddr(0, &srcsid))
    return WHY("Could not get local address");
  if (overlay_mdp_bind(&srcsid, MDP_PORT_DIRECTORY))
    return WHY("Could not bind to MDP socket");
  
  fds[0].fd = STDIN_FILENO;
  fds[0].events = POLLIN;
  fds[1].fd = mdp_client_socket;
  fds[1].events = POLLIN;
  
  printf("STARTED\n");
  fflush(stdout);
  
  while(1){
    int r = poll(fds, 2, 100);
    if (r>0){
      if (fds[0].revents & POLLIN)
	resolve_request();
      if (fds[1].revents & POLLIN)
	add_record();
      
      if (fds[0].revents & (POLLHUP | POLLERR))
	break;
    }
  }
  
  overlay_mdp_client_done();
  return 0;
}