#include "fs.h"
#define DEBUG 0

fsdirent::fsdirent(str xname, str xfileid) : name(xname),fileid(xfileid)
{
#if DEBUG
  warn << "creating new fsdirent " << (unsigned long) this
       << ": name=" << name
       << " fileid= " << fileid << "\n";
#endif
}


fsdirent::fsdirent(const char *buf, unsigned long len, const char **next)
{
  unsigned long i = 0;
  const char *ptr = buf;
  unsigned long cnt;

  if (len < sizeof(unsigned long)) 
  {
    warn << "fsdirent buf not long enough for name len\n";
    return;
  }
  cnt = *((unsigned long *)ptr);
  len -= sizeof(unsigned long);
  ptr += sizeof(unsigned long);
  if (len < cnt) 
  {
    warn << "fsdirent buf not long enough for name\n";
    return;
  }
  name = str(ptr, cnt);
  ptr += cnt;
  len -= cnt;
  if (len < sizeof(unsigned long)) 
  {
    warn << "fsdirent buf not long enough for fileid len\n";
    return;
  }
  cnt = *((long *)ptr);
  len -= sizeof(unsigned long);
  ptr += sizeof(unsigned long);
  if (len < cnt) 
  {
    warn << "fsdirent buf not long enough for fileid\n";
    return;
  }
  fileid = str(ptr, cnt);
  ptr += cnt;
  len -= cnt;

#if DEBUG
  warn << "fsdirent: read " << (unsigned long) this
       << "   " << name << " : " << fileid << "\n";
#endif
  
  *next = ptr;
//   if (len > 0)
//     warn << "leftover characters in fsdirent buf\n";
}


str
fsdirent::strify()
{
//  strbuf buf;
  char buf[1024];
  unsigned long len;
  char *ptr;
#if DEBUG
  warn << "strify: strifying " << name << " : " << fileid << "\n";
#endif
  
//   len = name.len();
//   buf << str((char *)&len, sizeof(unsigned long));
//   buf << name;
//   len = fileid.len();
//   buf << str((char *)&len, sizeof(unsigned long));
//   buf << fileid;
  ptr = buf;
  len = name.len();
  *((unsigned long *)ptr) = len;
  ptr += sizeof(unsigned long);
  for (unsigned long i = 0; i < len; i++) 
    *ptr++ = name[i];
  len = fileid.len();
  *((unsigned long *)ptr) = len;
  ptr += sizeof(unsigned long);
  for (unsigned long i = 0; i < len; i++) 
    *ptr++ = fileid[i];

  str s = str(buf, ptr-buf);
  
#if DEBUG
  warn << "strify: returning " << s << "\n";
#endif
  
  return s;
}



fsdir::fsdir(fs *xfs, str xfileid) : fsfile(xfs, xfileid)
{
}


void
fsdir::get_dircontents(callback<void, int, ptr<fsdirlist> >::ref cb)
{
  get_fattr(wrap(this, &fsdir::get_dircontents_on_get_fattr, cb));
}

void
fsdir::get_dircontents_on_get_fattr(
  callback<void, int, ptr<fsdirlist> >::ref cb, bool ok, fattr3 fa)
{    
  read(0, LONG_MAX, wrap(this, &fsdir::get_dircontents_on_read, cb, fa));
}

void
fsdir::get_dircontents_on_read(callback<void, int, ptr<fsdirlist> >::ref cb,
                                fattr3 fa, bool ok, str data)
{
  if (!ok)
  {
    cb(EIO, ptr<fsdirlist>(NULL));
    return;
  }

  const char *startptr = data.cstr();
  const char *ptr = startptr;
  ref<fsdirlist> lst = New refcounted<fsdirlist>;

  while ((uint64)(ptr - startptr) < fa.size) 
  {
    fsdirent *ent = New fsdirent(ptr,
                               data.len()-(ptr-startptr),
                               &ptr);
    lst->insert(ent);
  }

  cb(0, lst);
}


void
fsdir::write_dircontents(ref<fsdirlist> lst, callback<void, int>::ref cb)
{
  strbuf buf;

  fsdirent *ent;
  for (ent = lst->first(); ent; ent = lst->next(ent))
  {
    
#if DEBUG
    warn << "write_dircontents: ent = " << (u_long)ent << "  name=" << ent->name
         << " fileid= " << ent->fileid << "\n";
#endif
    buf << ent->strify();
  }

  str s = str(buf);
#if DEBUG
  warn << "write_dircontents: writing " << s << "\n";
#endif
  write(0, s.len(), s, wrap(this, &fsdir::write_dircontents_on_write,
                            cb, (unsigned long)s.len()));
}

void
fsdir::write_dircontents_on_write(callback<void, int>::ref cb,
                                  unsigned long len,
                                  bool ok)
{
  if (!ok) 
  {
    cb(EIO);
    return;
  }

  set_size(len, wrap(this, &fsdir::write_dircontents_on_set_size, cb));
}

void
fsdir::write_dircontents_on_set_size(callback<void, int>::ref cb,
                                     bool ok)
{
  if (!ok)
    cb(EIO);
  else
    cb(0);
}


void
fsdir::add_file(str filename, str fileid, callback<void, int>::ref cb)
{
  get_dircontents(wrap(this, &fsdir::add_file_on_get_dircontents,
                       filename, fileid, cb));
}

void
fsdir::add_file_on_get_dircontents(str filename, str fileid,
                                   callback<void, int>::ref cb,
                                   int r, ptr<fsdirlist> lst)
{
  if (r != 0) 
  {
    cb(r);
    return;
  }

  if ((*lst)[filename] != NULL)
  {
    cb(EEXIST);
    return;
  }

  fsdirent *ent = New fsdirent(filename, fileid);
  lst->insert(ent);

  write_dircontents(lst, wrap(this, &fsdir::add_file_on_write_dircontents,
                              cb));
}

void
fsdir::add_file_on_write_dircontents(callback<void, int>::ref cb, int r)
{
  cb(r);
}
