#include <iostream.h>
#include "fs.h"
#include "fsfile.h"
#include "inodelog.h"
#include "chunkable.h"


fsfile::fsfile(ref<chunkable> content, inode in, ref<FS> fs)
  : content(content), in(in), my_fs(fs)
{

}



//--------------------------------------------
void 
fsfile::create(ref<FS> fs, fattr3 fa,  
	       callback<void, ref<fsfile> >::ref cb, cbe error)
{
  /*
   * content = chunkable::create(fs->blob, str());
   * inode in = fa;
   * in.num = fs->log.newInumber();
   * in.content = content.marshall();
   * content.flush();
   * fs->log.put(in);
   * cb(New refcounted <fsfile> content, in);
   */
  
  chunkable::create(fs->blob, str(), wrap(create_cb1, fs, fa, cb, error), error);
}

void
fsfile::create_cb1(ref<FS> fs,  fattr3 fa, callback<void, ref<fsfile> >::ref cb, cbe error, ref<chunkable> content )
{ 
  if (error!=0)
  {
    cerr<< "error create\n";
    exit(1);
  }
  
  inode in;
  in.type=fa.type;  
  in.mode=fa.mode;
  in.nlink=fa.nlink;
  in.uid=fa.uid;
  in.gid=fa.gid;
  in.size=fa.size;
  in.used=fa.used;
  in.rdev=fa.rdev;
  in.fsid=fa.fsid;
  in.fileid=fa.fileid;
  in.atime=fa.atime;
  in.mtime=fa.mtime;
  in.ctime=fa.ctime;
  fs->log->newInumber(wrap(create_cb2, fs, cb, content, in, error), error);
}

void
fsfile::create_cb2(ref<FS> fs, callback<void, ref<fsfile> >::ref cb, ref<chunkable> content, inode in, cbe error, inumber num)
{
  if (error!=0)
  { 
    cerr<< "error create\n";
    exit(1);
  }

  in.num=num;
  content->marshall(wrap(create_cb3, fs, cb, content, in,  error),error);
}

void
fsfile::create_cb3(ref<FS> fs, callback<void, ref<fsfile> >::ref cb, ref<chunkable> content, inode in, cbe error, marshalled m_data)
{
  if (error!=0)
  { 
    cerr<< "error create\n";
    exit(1);
  }
  
  in.content=m_data; 
  content->flush(wrap(create_cb4, fs, cb, content, in, error), error);
}

void
fsfile::create_cb4(ref<FS> fs, callback<void, ref<fsfile> >::ref cb, ref<chunkable> content, inode in, cbe error)
{
  if (error!=0)
  { 
    cerr<< "error create\n";
    exit(1);
  }

  fs->log->put(in, wrap(create_cb5, fs, cb, content, in, error),error);
}

void
fsfile::create_cb5(ref<FS> fs, callback<void, ref<fsfile> >::ref cb, ref<chunkable> content, inode in, cbe error, timestamp ts)
{
  if (error!=0)
  { 
    cerr<< "error create\n";
    exit(1);
  }
    
  cb(New refcounted<fsfile>(content, in, fs));
}







/**
 * Load an existing file, given its inumber.  Flags can be any
 * combination of the L_* flags listed below.
 */
void 
fsfile::load(ref<FS> fs, timestamp ts, inumber num, int flags,
		   callback<void, ref<fsfile> >::ref cb, cbe error)
{
  fs->log->get(ts, num, wrap(load_cb1, fs, flags, cb, error), error);
}

void
fsfile::load_cb1(ref<FS> fs, int flags, callback<void, ref<fsfile> >::ref cb,  cbe error, inode in)
{
  /* if (flags==RO)
     in.ro*/

  chunkable::create(fs->blob, in.content, wrap(load_cb2, fs, in, cb, error), error);
}

void 
fsfile::load_cb2(ref<FS> fs, inode in, callback<void, ref<fsfile> >::ref cb, cbe error, ref<chunkable> content)
{
  content->writeSubstr(0, in.content, wrap(load_cb3, fs, in, content, cb, error), error);  
}

void 
fsfile::load_cb3(ref<FS> fs, inode in, ref<chunkable> content, callback<void, ref<fsfile> >::ref cb, cbe error)
{
  cb(New refcounted<fsfile>(content, in, fs));
}


ref<FS> fsfile::getFS()
{
  return my_fs;
}


inumber fsfile::getInumber()
{
  return in.num;
}


void fsfile::read(uint pos, uint len, callback<void, str>::ref cb, cbe error)
{
  content->readSubstr(pos,len, wrap(this, &fsfile::read_cb1, cb, error), error);
}

void fsfile::read_cb1(callback<void, str>::ref cb, cbe error, str data)
{
  cb(data);
}
  
void 
fsfile::setLength(unsigned long len,  callback<void>::ref cb, cbe error)
{
  /* 
   * truncate
   * marshall
   * */
  content->truncate(len, wrap(this, &fsfile::setLength_cb1, cb, error),error);
}

void 
fsfile::setLength_cb1(callback<void>::ref cb, cbe error)
{
  // set length of the chunk
  content->length(wrap(this, &fsfile::setLength_cb2, cb, error ), error);
}

void 
fsfile::setLength_cb2(callback<void>::ref cb, cbe error, unsigned long len)
{
  // update fattr3.size
  in.size=len;
  // marshall data
  content->marshall(wrap(this, &fsfile::setLength_cb3, cb, error), error);
}

void
fsfile::setLength_cb3(callback<void>::ref cb, cbe error, marshalled m_data)
{
  in.content=m_data;
  //flush data back
  content->flush(wrap(this, &fsfile::setLength_cb4, cb, error),error); 
}

void
fsfile::setLength_cb4(callback<void>::ref cb, cbe error)
{
  cb();
}

void 
fsfile::write(uint pos, str data, callback<void>::ref cb, cbe error)
{
  content->writeSubstr(pos, data, wrap(this, &fsfile::write_cb1, cb, error), error);
}
  
void
fsfile::write_cb1(callback<void>::ref cb, cbe error)
{
  content->length(wrap(this, &fsfile::write_cb2, cb, error), error);
}

void
fsfile::write_cb2(callback<void>::ref cb, cbe error, unsigned long len)
{
  in.size=len;
  //in.date
  
  content->marshall(wrap(this, &fsfile::write_cb3, cb, error), error);
}

void
fsfile::write_cb3(callback<void>::ref cb, cbe error, marshalled m_data)
{
  in.content=m_data;
  content->flush(wrap(this, &fsfile::write_cb4, cb, error), error);
}

void
fsfile::write_cb4(callback<void>::ref cb, cbe error)
{
  cb();
}


void fsfile::getAttr(callback<void, fattr3>::ref cb, cbe error)
{
  // get fattr3 contents
  // return fattr3
   

  fattr3 fa;

  fa.type=in.type;
  fa.mode=in.mode;
  fa.uid=in.uid;
  fa.gid=in.gid;
  fa.size=in.size;
  fa.used=in.used;
  fa.rdev=in.rdev;
  fa.fsid=in.fsid;
  fa.fileid=in.fileid;
  fa.atime=in.atime;
  fa.mtime=in.mtime;
  fa.ctime=in.mtime;

  cb(fa); 
}
  

void fsfile::setAttr(fattr3 fa, callback<void, fattr3>::ref cb, cbe error)
{
   fattr3 old_fa;
   
   old_fa.type=in.type;
   old_fa.mode=in.mode;
   old_fa.uid=in.uid;
   old_fa.gid=in.gid;
   old_fa.size=in.size;
   old_fa.used=in.used;
   old_fa.rdev=in.rdev;
   old_fa.fsid=in.fsid;
   old_fa.fileid=in.fileid;
   old_fa.atime=in.atime;
   old_fa.mtime=in.mtime;
   old_fa.ctime=in.mtime;

   in.type=fa.type; 
   in.mode=fa.mode;
   in.nlink=fa.nlink;
   in.uid=fa.uid;
   in.gid=fa.gid;
   in.size=fa.size;
   in.used=fa.used;
   in.rdev=fa.rdev;
   in.fsid=fa.fsid;
   in.fileid=fa.fileid;
   in.atime=fa.atime;
   in.mtime=fa.mtime;
   in.ctime=fa.ctime;
   
   
   cb(old_fa);
}

// If the lock has been taken for this file, release it.
 
fsfile::~fsfile()
{
  
}














