#include "persifs.h"
#include "writegrouper.h"
#include "fs.h"

#define DEBUG 0

#define WRAP(args...) wrap(this, &writeGrouper::args)


fileState::fileState(inumber num, inode ino, ref<chunkable> content,
                     nonce lastWrite)
  : num(num), ino(ino), content(content), lastWrite(lastWrite)
{
}


writeGrouper::writeGrouper(FS *fs, time_t writeGroupTime)
  : fs(fs), writeGroupTime(writeGroupTime)
{
  lastNonce = 0;
}


writeGrouper::~writeGrouper()
{
}


nonce
writeGrouper::newNonce() 
{
  return ++lastNonce;
}


const fileState *
writeGrouper::read(inumber num)
{
  return state[num];
}


void
writeGrouper::write(inumber num, inode ino, ref<chunkable> content)
{
  fileState *s = state[num];
  
  if (s == NULL) {
    s = New fileState(num, ino, content, newNonce());
    state.insert(s);
  } else {
    s->ino = ino;
    s->content = content;
    s->lastWrite = newNonce();
  }
#if DEBUG
  warn << "grouper: write " << num << " nonce=" << s->lastWrite << "\n";
#endif
  
  delaycb(writeGroupTime, WRAP(doDelayedWrite,
                               num, s->lastWrite));
}


void
writeGrouper::doDelayedWrite(inumber num, nonce n)
{
#if DEBUG
  warn << "doing delayed write for " << num << " nonce=" << n << "\n";
#endif
  fileState *s = state[num];

  if (s == NULL) {
    fatal << "attempting to do delayed write for file "
          << "with no state in grouper";
  }

  if (n != s->lastWrite)
    return;
  
#if DEBUG
  warn << "actually doing delayed write for " << num << " nonce=" << n << "\n";
#endif
  
  ref<chunkable> contentCopy = New refcounted<chunkable>(*s->content);
  contentCopy->flush(WRAP(doDelayedWrite_after_flush,
                          num, s->ino, contentCopy, n),
                     fatalCBMsg("doDelayedWrite: flush"));
}

void
writeGrouper::doDelayedWrite_after_flush(inumber num, inode ino,
                                         ref<chunkable> contentCopy,
                                         nonce n)
{
  contentCopy->marshall(WRAP(doDelayedWrite_after_marshall,
                             num, ino, contentCopy, n),
                        fatalCBMsg("doDelayedWrite: marshall"));
}

void
writeGrouper::doDelayedWrite_after_marshall(inumber num, inode ino,
                                            ref<chunkable> contentCopy,
                                            nonce n,
                                            chunkable::marshalled m)
{
  ino.content = m;
  fs->log->put(ino, WRAP(doDelayedWrite_after_inodePut,
                         num, ino, contentCopy, n),
               fatalCBMsg("doDelayedWrite: inodeLog put"));
}

void
writeGrouper::doDelayedWrite_after_inodePut(inumber num, inode ino,
                                            ref<chunkable> contentCopy,
                                            nonce n, timestamp ts)
{
  fileState *s = state[num];
  
  if (s == NULL) {
    warn << "grouper: state disappeared while performing delayed write\n";
  }

  if (n != s->lastWrite) {
    // There was another write to the grouper while we were flushing,
    // so don't destroy the state.
    return;
  }
  
  state.remove(s);
  delete s;
}

