#include "amisc.h"
#include "async.h"
#include "blockdb.h"

const size_t blockdb::max_block_size (8192);

blockdb::blockdb()
{
}

str
blockdb::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
blockdb::put(str key, str value, callback<void, bool>::ref cb)
{
  if (value.len () > max_block_size) {
    warn << "put of " << hex(key) << " too long (" << value.len () << ")!\n";
    delaycb (0, wrap (this, &blockdb::put_done, cb, false));
    return;
  }

  block *b = tab[key];
  if(b){
    tab.remove(b);
    delete b;
  }
  tab.insert(New block(key, value));
  delaycb(0, wrap(this, &blockdb::put_done, cb, true));;
}

void
blockdb::put_done(callback<void, bool>::ref cb, bool ok)
{
  cb(ok);
}

void
blockdb::get(str key, callback<void, bool, str>::ref cb)
{
  block *b = tab[key];
  if(b){
    delaycb(0, wrap(this, &blockdb::get_done, cb, true, b->value));
  } else {
    delaycb(0, wrap(this, &blockdb::get_done, cb, false, str("")));
  }
}

void
blockdb::get_done(callback<void, bool, str>::ref cb, bool ok, str value)
{
  cb(ok, value);
}

void
blockdb::remove(str key, callback<void, bool>::ref cb)
{
  block *b = tab[key];
  bool ok = false;
  if(b){
    tab.remove(b);
    delete b;
    ok = true;
  }
  delaycb(0, wrap(this, &blockdb::remove_done, cb, ok));;
}

void
blockdb::remove_done(callback<void, bool>::ref cb, bool ok)
{
  cb(ok);
}

void
blockdb::testcb(bool wantok, str wantv, bool ok, str v)
{
  if(wantok != ok){
    fprintf(stderr, "blockdb::test failed 1\n");
    exit(1);
  } else if(wantok && v != wantv){
    fprintf(stderr, "blockdb::test failed 2\n");
    exit(1);
  }
}

void
blockdb::testcb1(bool ok)
{
  if(ok != true){
    fprintf(stderr, "blockdb::testcb1 not ok\n");
    exit(1);
  }
}

void
blockdb::test()
{
  get("k1", wrap(this, &blockdb::testcb, false, ""));
  put("k1", "v1", wrap(this, &blockdb::testcb1));
  get("k1", wrap(this, &blockdb::testcb, true, "v1"));
  put("k2", "v2", wrap(this, &blockdb::testcb1));
  get("k2", wrap(this, &blockdb::testcb, true, "v2"));
  get("k1", wrap(this, &blockdb::testcb, true, "v1"));
  put("k2", "v21", wrap(this, &blockdb::testcb1));
  get("k2", wrap(this, &blockdb::testcb, true, "v21"));
  put("k2", "z", wrap(this, &blockdb::testcb1));
  get("k2", wrap(this, &blockdb::testcb, true, "z"));
  get("k1", wrap(this, &blockdb::testcb, true, "v1"));
  remove("k1", wrap(this, &blockdb::testcb1));
  remove("k2", wrap(this, &blockdb::testcb1));
}
