/*
 * Simple lock server. Linked with server1.
 */

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

#define DBG 1

LS::LS()
{
}

// start listening to RPCs.
void
LS::attach(ptr<axprt> sx)
{
  sxx = asrv::alloc(sx, lock_prog_1, wrap(LS::dispatch, this));
  c = aclnt::alloc(sx, lock_prog_1);
}

// convert a bit-string to a hex string for debug printouts.
str
LS::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);
}

// done sending a GRANT RPC to a client.
void
LS::grant_done(str name, bool *r, struct sockaddr_in *xsin, clnt_stat err)
{
  delete r;
  delete xsin;
  if(err != 0){
    fprintf(stderr, "grant_done: rpc err %d\n", err);
  }
}

// send a grant RPC to a client.
// the sin argument indicates which client -- it holds the
// client's IP address and port.
// sin should be a copy of the sin that arrived in the acquire()
// handler.
void
LS::send_grant(str name, struct sockaddr_in sin)
{
  bool *r = new bool;
  struct sockaddr_in *xsin = new sockaddr_in;
  *xsin = sin;
#if DBG
  printf("sending grant for %s\n", hex(name).cstr());
#endif
  lname xn;
  xn = name;
  c->call(LOCK_GRANT, &xn, r,
          wrap(this, &LS::grant_done, name, r, xsin),
          (AUTH *) 0,
          (xdrproc_t) 0, (xdrproc_t) 0,
          (u_int32_t) 0, (u_int32_t) 0,
          (struct sockaddr *) xsin);
}


// got an acquire RPC from a client.
void
LS::acquire(svccb *sbp)
{
  lname *arg = sbp->Xtmpl getarg<lname>();
  str name(arg->base(), arg->size());
  bool *res = sbp->Xtmpl getres<bool>();
  const sockaddr_in sin = *(const sockaddr_in *) sbp->getsa();

#if DBG
  printf("got acquire %s\n", hex(name).cstr());
#endif

  // arrange to send a grant RPC immediately.
  // you'll need to change this.
  delaycb(0, wrap(this, &LS::send_grant, name, sin));

  *res = true;
  sbp->reply(res);
}

// got a release RPC from a client.
void
LS::release(svccb *sbp)
{
  lname *arg = sbp->Xtmpl getarg<lname>();
  str name(arg->base(), arg->size());
  bool *res = sbp->Xtmpl getres<bool>();
  const sockaddr_in sin = *(const sockaddr_in *) sbp->getsa();

#if DBG
  printf("got release %s\n", hex(name).cstr());
#endif

  // you'll need to add code here.

  *res = true;
  sbp->reply(res);
}

void
LS::dispatch(LS *bs, svccb *sbp)
{
  switch(sbp->proc()){
  case LOCK_ACQUIRE:
    bs->acquire(sbp);
    break;
  case LOCK_RELEASE:
    bs->release(sbp);
    break;
  default:
    fprintf(stderr, "lock_server: unknown RPC %d\n", sbp->proc());
    const sockaddr_in sin = *(const sockaddr_in *) sbp->getsa();
    fprintf(stderr, "from port %d\n", ntohs(sin.sin_port));
    sbp->reject(PROC_UNAVAIL);
    break;
  }
}
