// file: $isip/class/io/Sof/sof_09.cc
// version: $Id: sof_09.cc 4958 2000-09-22 21:35:19Z zhao $
//

// isip include files
//
#include "Sof.h"

// method: resize
//
// arguments:
//  const SysString& name: (input) class name
//  long tag: (input) class tag
//  long size: (input) new required size
//
// return: a boolean value indicating status
//
// resize the current entry, preserving current data
//
boolean Sof::resize(const SysString& name_a, long tag_a, long size_a) {
  
  // find the object and seek to the beginning of the data
  //
  if (!find(name_a, tag_a)) {
    return Error::handle(name(), L"resize", ERR_NOOBJ,
			 __FILE__, __LINE__);
  }

  // check argument
  //
  if (size_a < 0) {
    return Error::handle(name(), L"resize", Error::ARG, __FILE__, __LINE__);
  }

  long obj_size = getObjectSize();
  long obj_pos = index_d.getPosition();
  
  if (obj_size == size_a) {

    // do nothing
    //
    return true;
  }
  else if (obj_size > size_a) {

    if (!fseek(size_a, File::POS_PLUS_CUR)) {
      return Error::handle(name(), L"resize", Error::SEEK, __FILE__, __LINE__);
    }

    // clear space
    //
    if (!clearSpace(obj_size - size_a)) {
      return Error::handle(name(), L"resize", Error::IO, __FILE__, __LINE__);
    }

    // reposition the file pointer to the beginning of the object's data
    //
    if (!rewind()) {
      return Error::handle(name(), L"resize", Error::SEEK, __FILE__, __LINE__);
    }

    // possibly shrink the file, if this is the last object
    //
    if ((obj_pos + obj_size) == end_of_data_d) {
      end_of_data_d -= (obj_size - size_a);
    }

    // reset the size in the index
    //
    return index_d.setSize(size_a);
  }
  else if (obj_size < size_a) {

    // here's the tricky part: delete the current entry and make a new
    // one at the end
    //
    long new_pos;
    long new_data_pos;
      
    // get the new position (end of file)
    //
    if (isText()) {

      // seek to the end of file
      //
      if (!fseek(0, File::POS_PLUS_END)) {
	return Error::handle(name(), L"resize", Error::SEEK,
			     __FILE__, __LINE__);
      }

      // this is the new position (beginning of label)
      //
      new_pos = ftell();
      
      // write the label
      //
      if (!writeLabel(name_a, tag_a)) {
	return Error::handle(name(), L"resize", ERR_LABEL,
			     __FILE__, __LINE__);
      }

      // set the new position for the data
      //
      new_data_pos = ftell();
    }

    // for binary, seek to end_of_data_d
    //
    else {
      
      if (!fseek(end_of_data_d, File::POS)) {
	return Error::handle(name(), L"resize", Error::SEEK,
			     __FILE__, __LINE__);
      }

      end_of_data_d += size_a;
      
      // both position and data position are the same for binary
      //
      new_pos = ftell();
      new_data_pos = new_pos;
    }

    // clear out the new space before we copy data over
    //
    if (!clearSpace(size_a)) {
      return Error::handle(name(), L"resize", Error::IO, __FILE__, __LINE__);
    }
    
    // copy the data currently in the object to the end of the file
    //
    if (!copySegment(cur_data_d, new_data_pos, obj_size)) {
      return Error::handle(name(), L"resize", Error::IO, __FILE__, __LINE__);
    }

    // now empty the old space
    //
    if (!rewind()) {
      return Error::handle(name(), L"resize", Error::SEEK, __FILE__, __LINE__);
    }
    if (!clearSpace(obj_size + cur_data_d - obj_pos)) {
      return Error::handle(name(), L"resize", Error::IO, __FILE__, __LINE__);
    }
    
    // change the values of position and size for this object
    //
    index_d.setPosition(new_pos);
    index_d.setSize(size_a);
    
    // position us back to the beginning of the new object (this will
    // also set cur_data_d)
    //
    if (!seekData()) {
      return Error::handle(name(), L"resize", Error::SEEK, __FILE__, __LINE__);
    }
  }

  // exit gracefully
  //
  return true;
}

// method: resize
//
// arguments:
//  long size: (input) new required size
//
// return: a boolean value indicating status
//
// resize the current entry that is in partial write mode, preserving
// current data. this is only for partial_write mode
//
boolean Sof::resize(long size_a) {
  
  // make sure we are in partial write mode
  //
  if (!partial_write_d) {
    return Error::handle(name(), L"resize", ERR_RESIZE, __FILE__, __LINE__);
  }
  
  // check argument
  //
  if (size_a < 0) {
    debug(L"size went to zero");
    return Error::handle(name(), L"resize", Error::ARG, __FILE__, __LINE__);
  }
  
  long obj_size = getObjectSize();
  long obj_pos = index_d.getPosition();
  
  // make sure it is the last entry
  //
  if (isBinary() && ((obj_size + obj_pos) != end_of_data_d)) {
    return Error::handle(name(), L"resize", ERR, __FILE__, __LINE__);
  }

  if (obj_size == size_a) {

    // do nothing
    //
    return true;
  }
  else if (obj_size > size_a) {

    if (!fseek(size_a, File::POS_PLUS_CUR)) {
      return Error::handle(name(), L"resize", Error::SEEK, __FILE__, __LINE__);
    }

    // clear space
    //
    if (!clearSpace(obj_size - size_a)) {
      return Error::handle(name(), L"resize", Error::IO, __FILE__, __LINE__);
    }
    
    // reposition the file pointer to where it was before
    //
    if (!fseek(-size_a, File::POS_PLUS_CUR)) {
      return Error::handle(name(), L"resize", Error::SEEK, __FILE__, __LINE__);
    }
    
    if (isBinary()) {

      // reset the size in the index
      //
      index_d.setSize(size_a);

      // adjust end_of_data_d
      //
      end_of_data_d += (size_a - obj_size);
    }
  }
  else if (obj_size < size_a) {
    
    if (isBinary()) {

      // reset the size in the index
      //
      index_d.setSize(size_a);

      // adjust end_of_data_d
      //
      end_of_data_d += (size_a - obj_size);
    }
  }

  // exit gracefully
  //
  return true;
}

// method: copySegment
//
// arguments:
//  long src_pos: (input) file position of source
//  long nbytes: (input) number of bytes to copy
//
// return: a boolean value indicating status
//
// this method copies a chunk of nbytes data from src_pos to the end
// of the file
//
boolean Sof::copySegment(long src_pos_a, long nbytes_a) {

  // seek the POS_PLUS_END
  //
  if (!fseek(0, File::POS_PLUS_END)) {
    return Error::handle(name(), L"copySegment", Error::SEEK,
			 __FILE__, __LINE__);
  }

  // call the master function
  //
  return copySegment(src_pos_a, ftell(), nbytes_a);
}

// method: copySegment
//
// arguments:
//  long src_pos: (input) file position of source
//  long dst_pos: (input) file position of destination
//  long nbytes: (input) number of bytes to copy
//
// return: a boolean value indicating status
//
// this method copies a chunk of nbytes data from src_pos to the end
// of the file
//
boolean Sof::copySegment(long src_pos_a, long dst_pos_a, long nbytes_a) {

  // seek to the destination
  //
  if (!fseek(dst_pos_a, File::POS)) {
    return Error::handle(name(), L"copySegment", Error::SEEK,
			 __FILE__, __LINE__);
  }
  
  // copy one buffer at a time
  //
  static byte buffer[BUFFER_SIZE];
  long pass_size;
  
  long diff_pos = dst_pos_a - src_pos_a;
  
  for (long i = nbytes_a; i > 0; i -= BUFFER_SIZE) { 

    if (i > BUFFER_SIZE) {
      pass_size = BUFFER_SIZE;
    }
    else {
      pass_size = i;
    }

    // seek to the position to copy
    //
    if (!fseek(-diff_pos, File::POS_PLUS_CUR)) {
      return Error::handle(name(), L"copySegment", Error::SEEK,
			   __FILE__, __LINE__);
    }
    
    if (fp_d.read(buffer, sizeof(byte), pass_size) != pass_size) {
      return Error::handle(name(), L"copySegment", Error::READ,
			   __FILE__, __LINE__);
    }

    if (isBinary()) {
      cur_pos_d += pass_size * sizeof(byte);
    }

    // seek to the position to write
    //
    if (!fseek(diff_pos - pass_size, File::POS_PLUS_CUR)) {
      return Error::handle(name(), L"copySegment", Error::SEEK,
			   __FILE__, __LINE__);
    }
    
    if (fp_d.write(buffer, sizeof(byte), pass_size) != pass_size) {
      return Error::handle(name(), L"copySegment", Error::WRITE,
			   __FILE__, __LINE__);
    }
    
    if (isBinary()) {
      cur_pos_d += pass_size * sizeof(byte);
    }
  }      
  
  // exit gracefully
  //
  return true;
}
