// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* 
// ** Copyright UCAR (c) 1990 - 2016                                         
// ** University Corporation for Atmospheric Research (UCAR)                 
// ** National Center for Atmospheric Research (NCAR)                        
// ** Boulder, Colorado, USA                                                 
// ** BSD licence applies - redistribution and use in source and binary      
// ** forms, with or without modification, are permitted provided that       
// ** the following conditions are met:                                      
// ** 1) If the software is modified to produce derivative works,            
// ** such modified software should be clearly marked, so as not             
// ** to confuse it with the version available from UCAR.                    
// ** 2) Redistributions of source code must retain the above copyright      
// ** notice, this list of conditions and the following disclaimer.          
// ** 3) Redistributions in binary form must reproduce the above copyright   
// ** notice, this list of conditions and the following disclaimer in the    
// ** documentation and/or other materials provided with the distribution.   
// ** 4) Neither the name of UCAR nor the names of its contributors,         
// ** if any, may be used to endorse or promote products derived from        
// ** this software without specific prior written permission.               
// ** DISCLAIMER: THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS  
// ** OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED      
// ** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.    
// *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* 

//////////////////////////////////////////////////////////
// MultBuf.cc
//
//
// A buffer with multiple parts - see also MultBufPart.hh
//
// A MultBuf represents a buffer which has multiple parts.
// The objects may be assembled (serialized) or
// disassembled (deserialized) to facilitate buffer storage.
//
// A MultBuf has 3 sections, a header struct which contains the
// number of parts, an array of part structs which indicate the
// part types, lengths and offsets, and the data parts themselves.
//
// MultBuf format:
//
//   header_t
//   nParts * part_t
//   nParts * data
//
// Much of this code copied from DsMessage.
//
// Mike Dixon, RAP, NCAR, POBox 3000, Boulder, CO, 80307-3000
//
// March 2000
//
////////////////////////////////////////////////////////////


#include <dataport/bigend.h>
#include <toolsa/mem.h>
#include <toolsa/TaStr.hh>
#include <rapformats/MultBuf.hh>
#include <rapformats/MultBufPart.hh>
using namespace std;

//////////////
// constructor
//

MultBuf::MultBuf()
{
  _debug = false;
  _version = currentVersion;
  clearAll();
}

/////////////////////////////
// Copy constructor
//

MultBuf::MultBuf(const MultBuf &rhs)

{
  if (this != &rhs) {
    _copy(rhs);
  }
}

/////////////
// destructor

MultBuf::~MultBuf()
{
  clearParts();
}

/////////////////////////////
// Assignment
//

MultBuf &MultBuf::operator=(const MultBuf &rhs)

{
  return _copy(rhs);
}

////////////////////////////////////////////////////////////////
// peek at header
//
// This is used if you just want to peek at the header id and
// version before deciding how to handle the buffer.
// 
// Returns: 0 on success, -1 on error
// Error string retrieved with getErrStr().

int MultBuf::peekAtHeader(const void *in_buf, const int buf_len,
			  int *id /* = NULL*/,
			  int *version /* = NULL*/ )
  
{
  
  _errStr = "ERROR - MultBuf::peekAtHeader.\n";
  
  if (buf_len < (int) sizeof(header_t)) {
    _errStr += "  Buffer too short for header.\n";
    TaStr::AddInt(_errStr, "  Buffer len: ",  buf_len, true);
    TaStr::AddInt(_errStr, "  Header size: ", sizeof(header_t), true);
    return -1;
  }
  
  header_t hdr;
  memcpy(&hdr, in_buf, sizeof(header_t));
  _BE_to_header(hdr);
  
  if (id) {
    *id = hdr.id;
  }
  if (version) {
    *version = hdr.version;
  }

  return 0;
}

////////////////////////////////////////////////////
// disassemble a buffer into parts, store in
// MultBuf object.
//
// Returns: 0 on success, -1 on error
  
int MultBuf::disassemble(const void *in_buf, const int buf_len)

{

  _errStr = "ERROR - MultBuf::disassemble.\n";
  
  if (buf_len < (int) sizeof(header_t)) {
    _errStr += "  Buffer too short for header.\n";
    TaStr::AddInt(_errStr, "  Buffer len: ", buf_len, true);
    TaStr::AddInt(_errStr, "  Header size: ", sizeof(header_t), true);
    return -1;
  }
  
  header_t hdr;
  memcpy(&hdr, in_buf, sizeof(header_t));
  _BE_to_header(hdr);
  
  _id = hdr.id;
  _version = hdr.version;
  
  clearParts();
  
  for (int i = 0; i < hdr.n_parts; i++) {
    MultBufPart *part = new MultBufPart;
    if (part->loadFromBuf(i, in_buf, buf_len)) {
      _errStr += part->getErrStr();
      return -1;
    }
    _parts.push_back(part);
  }
  return 0;
}

////////////////////////////////////////////////
// does a part exist?
// returns the number of parts of the given type

int MultBuf::partExists(const int part_type) const

{
  int count = 0;
  for (size_t i = 0; i < _parts.size(); i++) {
    if (_parts[i]->getType() == part_type) {
      count++;
    }
  }
  return (count);
}

////////////////////////////////////////////
// Get a part from the parts array, given
// the index into the array.
//
// Returns pointer to part, NULL on failure.

MultBufPart *MultBuf::getPart(const int index) const
  
{
  if (index > (int) _parts.size() - 1) {
    return (NULL);
  }
  return (_parts[index]);
}

////////////////////////////////////////////////////////////
// get a part by type.
//
// If more than 1 part of this type exists, use index to
// select the required one.
//
// Returns pointer to the requested part, NULL on failure.

MultBufPart *MultBuf::getPartByType(const int part_type,
				    const int index /* = 0*/ ) const
  
{
  int count = 0;
  for (size_t i = 0; i < _parts.size(); i++) {
    if (_parts[i]->getType() == part_type) {
      if (count == index) {
	return(_parts[i]);
      }
      count++;
    }
  }
  return (NULL);
}
  
/////////////////////////////
// clear before adding parts.
//
// This initializes the number of parts to 0.
//
// It does NOT clear the header attributes set using the
// set() routines.

void MultBuf::clearParts()

{
  // free parts
  for(size_t i = 0; i < _parts.size(); i++) {
    delete _parts[i];
  }
  _parts.erase(_parts.begin(), _parts.end());
}

///////////////////////////////////////////////////////////////
// clear everything -- header and parts.
//
// A convenience routine for clients who want to call setType()
// instead of setHdrAttr() before assembling a buffer.

void MultBuf::clearAll()
{
  _id = -1;
  clearParts();
}

////////////////////////////////////////////////////////////
// Add a part to the object.
//
// The part is added at the end of the part list.
//
// The buffer must be in BE byte order.

void MultBuf::addPart(const int type, const int len, const void *data)

{
  
  MultBufPart *part = new MultBufPart;
  part->loadFromMem(type, len, data);
  _parts.push_back(part);
}

/////////////////////////////////////
// assemble the parts into a buffer
//
// Returns pointer to the assembled buffer.

void *MultBuf::assemble()

{
  
  _assembledBuf.free();

  // load up header
  
  header_t header;
  MEM_zero(header);
  header.id = _id;
  header.version = _version;
  header.n_parts = (int) _parts.size();
  _BE_from_header(header);
  _assembledBuf.add(&header, sizeof(header));

  // compute the part offsets

  int partDataOffset = sizeof(header_t) + _parts.size() * sizeof(part_hdr_t);
  for (size_t i = 0; i < _parts.size(); i++) {
    _parts[i]->setOffset(partDataOffset);
    partDataOffset += _parts[i]->getLength();
  }

  // load up part headers
  
  for (size_t i = 0; i < _parts.size(); i++) {
    part_hdr_t partHdr;
    MEM_zero(partHdr);
    partHdr.type = _parts[i]->getType();
    partHdr.len = _parts[i]->getLength();
    partHdr.offset = _parts[i]->getOffset();
    _BE_from_part_hdr(partHdr);
    _assembledBuf.add(&partHdr, sizeof(part_hdr_t));
  }

  // load up part data
  
  for (size_t i = 0; i < _parts.size(); i++) {
    _assembledBuf.add(_parts[i]->getBuf(), _parts[i]->getLength());
  }

  return (_assembledBuf.getPtr());
  
}

//////////////////////////////////////////
// print out main header and parts headers
//

void MultBuf::print(ostream &out, const string &spacer) const
{
  printHeader(out, spacer);
  printPartHeaders(out, spacer);
}

////////////////////////////////
// print out the buffer header
//

void MultBuf::printHeader(ostream &out, const string &spacer) const
{
  
  out << spacer << "Buffer id: " << _id<< endl;
  out << spacer << "  version: " << _version << endl;
  out << spacer << "  n_parts: " << _parts.size() << endl;

}

/////////////////////
// print part headers

void MultBuf::printPartHeaders(ostream &out, const string &spacer) const
{
  for (size_t i = 0; i < _parts.size(); i++) {
    getPart(i)->printHeader(out, i, spacer);
  }
}

/////////////////////////////////////////////////
// byte order swapping routines


void MultBuf::_BE_to_header(header_t &hdr)
     
{
  BE_to_array_32(&hdr, sizeof(header_t));
}

void MultBuf::_BE_from_header(header_t &hdr)

{
  BE_from_array_32(&hdr, sizeof(header_t));
}

void MultBuf::_BE_to_part_hdr(part_hdr_t &part)

{
  BE_to_array_32(&part, sizeof(part_hdr_t));
}

void MultBuf::_BE_from_part_hdr(part_hdr_t &part)

{
  BE_from_array_32(&part, sizeof(part_hdr_t));
}


/////////////////////////////////////////////////
// copy
  
MultBuf &MultBuf::_copy(const MultBuf &rhs)

{

  if (&rhs == this) {
    return *this;
  }

  // copy members

  _id = rhs._id;
  _version = rhs._version;
  _assembledBuf = rhs._assembledBuf;
  _debug = rhs._debug;
  _errStr = "";

  for (size_t ii = 0; ii < rhs._parts.size(); ii++) {
    delete _parts[ii];
  }
  _parts.clear();
  
  for (size_t ii = 0; ii < rhs._parts.size(); ii++) {
    MultBufPart *part = new MultBufPart(*rhs._parts[ii]);
    _parts.push_back(part);
  }

  // return ref to self
  
  return *this;

}