/*
 * client-side glue for blockdbd.
 */

#include "amisc.h"
#include "async.h"
#include "arpc.h"
#include "block_proto.h"
#include "dns.h"
#include "blockdbc.h"

blockdbc::blockdbc(struct sockaddr_in sin)
{
  fd = inetsocket(SOCK_DGRAM);
  if(fd < 0){
    fprintf(stderr, "blockdbc: inetsocket failed: %s\n",
            strerror(errno));
    exit(1);
  }
  x = axprt_dgram::alloc(fd);
  c = aclnt::alloc(x, block_prog_1);

  server = sin;
}

str
blockdbc::hex(str s)
{
  char buf[64];
  unsigned int len = s.len();
  const char *p = s.cstr();
  unsigned int i;

  buf[0] = '\0';
  for(i = 0; i < len && i*2+1 < sizeof(buf); i++){
    sprintf(buf + (i * 2), "%02x", p[i] & 0xff);
  }
  
  return str(buf);
}

void
blockdbc::put_done(callback<void, bool>::ref cb,
             bool *res, clnt_stat err)
{
  if(err || *res != true){
    fprintf(stderr, "blockdbc: put RPC failed: err=%d ok=%d errno=%d\n",
            err, *res, errno);
    cb(false);
  } else {
    cb(true);
  }
  delete res;
}

void
blockdbc::remove_done(callback<void, bool>::ref cb,
                      bool *res, clnt_stat err)
{
  if(err || *res != true){
    cb(false);
  } else {
    cb(true);
  }
  delete res;
}

void
blockdbc::real_put(str key, str value, callback<void, bool>::ref cb)
{
  put_args a;
  a.key = key;
  a.value = value;

  bool *r = new bool;

  c->call(BLOCK_PUT, &a, r,
          wrap(this, &blockdbc::put_done, cb, r),
          (AUTH *) 0,
          (xdrproc_t) 0, (xdrproc_t) 0,
          (u_int32_t) 0, (u_int32_t) 0,
          (struct sockaddr *) &server);
}


void
blockdbc::put(str key, str value, callback<void, bool>::ref cb)
{
  real_put(key, value, cb);
}

void
blockdbc::get_done(callback<void, bool, str>::ref cb, str key,
                   get_result *r, clnt_stat err)
{
  if(err || r->ok != true){
    cb(false, "");
  } else {
    cb(true, str(r->value.base(), r->value.size()));
  }
  delete r;
}

void
blockdbc::real_get(str key, callback<void, bool, str>::ref cb)
{
  get_args a;
  a.key = key;
  get_result *r = new get_result;

  c->call(BLOCK_GET, &a, r,
          wrap(this, &blockdbc::get_done, cb, key, r),
          (AUTH *) 0,
          (xdrproc_t) 0, (xdrproc_t) 0,
          (u_int32_t) 0, (u_int32_t) 0,
          (struct sockaddr *) &server);
}


void
blockdbc::get(str key, callback<void, bool, str>::ref cb)
{
  real_get(key, cb);
}

void
blockdbc::remove(str key, callback<void, bool>::ref cb)
{

  remove_args a;
  a.key = key;
  bool *r = new bool;

  c->call(BLOCK_REMOVE, &a, r,
          wrap(this, &blockdbc::remove_done, cb, r),
          (AUTH *) 0,
          (xdrproc_t) 0, (xdrproc_t) 0,
          (u_int32_t) 0, (u_int32_t) 0,
          (struct sockaddr *) &server);
}

void
blockdbc::test_cb2(str wanted, bool ok, str value)
{
  if(ok == false || wanted != value){
    fprintf(stderr, "blockdbc: get test failed\n");
    exit(1);
  }
}

void
blockdbc::test_cb1(bool ok)
{
  if(ok == false){
    fprintf(stderr, "blockdbc: put test failed\n");
    exit(1);
  }
  get("kkk", wrap(this, &blockdbc::test_cb2, "vvv"));
}

void
blockdbc::test()
{
  put("kkk", "vvv", wrap(this, &blockdbc::test_cb1));
}
