/*
 * Simple block database server.
 * Just provides UDP/RPC interface to blockdb.C.
 */

#include "amisc.h"
#include "async.h"
#include "arpc.h"
#include "blockdb.h"
#include "block_proto.h"
#include "lock_server.h"
#include "lock_proto.h"

class BS {
public:
  BS(int verb);
  void attach(ptr<axprt> sx);

private:
  static int verbose;
  ptr<asrv> sxx;
  blockdb *db;
  static int puts;
  static int gets;

  static void dispatch(BS *, svccb *);
  void put_cb(svccb *sbp, bool ok);
  void get_cb(svccb *sbp, bool ok, str value);
  void remove_cb(svccb *sbp, bool ok);
};

int BS::puts = 0;
int BS::gets = 0;
int BS::verbose = 0;

BS::BS(int verb)
{
  verbose = verb;
  db = New blockdb();
}

void
BS::attach(ptr<axprt> sx)
{
  sxx = asrv::alloc(sx, block_prog_1, wrap(BS::dispatch, this));
}

void
BS::put_cb(svccb *sbp, bool ok)
{
  sbp->reply(&ok);
}

void
BS::get_cb(svccb *sbp, bool ok, str value)
{
  get_result *r = sbp->template getres<get_result>();
  r->ok = ok;
  r->value = value;
  sbp->reply(r);
}

void
BS::remove_cb(svccb *sbp, bool ok)
{
  sbp->reply(&ok);
}

void
BS::dispatch(BS *bs, svccb *sbp)
{
  switch(sbp->proc()){
  case BLOCK_PUT:
    {
      puts++;
      if(verbose == 2)
        printf("p");
      if(verbose == 1 && ((puts+gets-1) % 100) == 99)
        printf("%d %d\n", puts, gets);
      put_args *a = sbp->template getarg<put_args>();
      bs->db->put(str(a->key.base(), a->key.size()),
                  str(a->value.base(), a->value.size()),
                  wrap(bs, &BS::put_cb, sbp));
    }
    break;
  case BLOCK_GET:
    {
      gets++;
      if(verbose == 2)
        printf("g");
      if(verbose == 1 && ((puts+gets-1) % 100) == 99)
        printf("%d %d\n", puts, gets);
      get_args *a = sbp->template getarg<get_args>();
      bs->db->get(str(a->key.base(), a->key.size()),
                  wrap(bs, &BS::get_cb, sbp));
    }
    break;
  case BLOCK_REMOVE:
    {
      remove_args *a = sbp->template getarg<remove_args>();
      bs->db->remove(str(a->key.base(), a->key.size()),
                     wrap(bs, &BS::remove_cb, sbp));
    }
    break;
  default:
    fprintf(stderr, "blockdbd: unknown RPC %d\n", sbp->proc());
    sbp->reject(PROC_UNAVAIL);
    break;
  }
}

void
usage()
{
  fprintf(stderr, "Usage: blockdbd [-v] port\n");
  exit(1);
}

int
main(int argc, char *argv[])
{
  int port = -1, i, verbose = 0;

  for(i = 1; i < argc; i++){
    if(strcmp(argv[i], "-v") == 0){
      verbose++;
    } else if(isdigit(argv[i][0]) && port == -1){
      port = atoi(argv[i]);
    } else {
      usage();
    }
  }
  if(port == -1)
    usage();

  setbuf(stdout, 0);

  int ss = inetsocket(SOCK_DGRAM, port, INADDR_ANY);
  if(ss < 0){
    fprintf(stderr, "blockdbd: inetsocket failed\n");
    exit(1);
  }
  ptr<axprt> sx = axprt_dgram::alloc(ss);
  BS *bs = New BS(verbose);
  bs->attach(sx);
  LS *ls = New LS();
  ls->attach(sx);
  
  amain();
}
