/***************************************************************
 * PrDataOutput.cpp
 *
 * This is an implementation of DataOutput methods.
 *
 ****************************************************************/

/*
 * modification history
 *----------------------
 *
 * 02/14/98: K.C. Chang: created.
 */

#include "PrDataOutput.h"
#include "PrSocket.h"

#include <stdio.h>
#include <string.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

typedef union
{
    double d;
    struct 
      {
	  long l,h;
      } s;
} RemapDouble;

PrDataOutput::PrDataOutput( PrSocket *socket, int bufferSize )
{
    socketFD_ = socket->socketFD();
    flag_ = Init(bufferSize);
}

PrDataOutput::~PrDataOutput()
{
    delete[] buffer_;
}

int PrDataOutput::bufferSize(int nbytes)
{
    delete[] buffer_;
    flag_ = Init(nbytes);
    return flag();
}

int PrDataOutput::flush( int type )
{
    Pack2B(buffer_,(short)byteCounter_);
    Pack2B(buffer_+2,(short)type);
    int n=Send();
    if (n!= byteCounter_)
      perror("PrDataOutput: flush() error");

    memset(buffer_,NULL,bufferSize_);
    return ( n - InitByteCounter() );
}

// little endian
//   [0] [1] [2] [3]
//    1   0   0   0
// big endian
//   [0] [1] [2] [3]
//    0   0   0   1
int PrDataOutput::Endian()
{
    int aint=1;
    char *cptr = (char *)&aint;
    if (*cptr == '\0')
      return PR_BIG_ENDIAN; // 1 => big endian
    else
      return PR_LITTLE_ENDIAN;
}

int PrDataOutput::Init(int nbytes)
{
    endian_ = Endian();

    bufferSize_ = nbytes + InitByteCounter();
    buffer_ = new char[bufferSize_];
    memset(buffer_,NULL,bufferSize_);

    return PR_DATAOUTPUT_OK;
}

int PrDataOutput::InitByteCounter()
{
    byteCounter_ = 4; // 2*sizeof(short);
    return byteCounter_;
}

// p279 in UNIX Network Programming by Stevens
int PrDataOutput::Send()
{
    char *ptr = buffer_;
  int nleft,nwritten;
    
  nleft = byteCounter_;
  while(nleft>0)
    {
      nwritten = send(socketFD_,ptr,nleft,0);
      if(nwritten<=0) return (nwritten); /* error */
      nleft -= nwritten;
      ptr += nwritten;
    }
  return(byteCounter_ - nleft);
}

inline void PrDataOutput::Pack1B( char *text, char data )
{
    text[0] = data;
}

inline void PrDataOutput::Pack2B( char *text, short data )
{
    *(short *)text = htons(data);
}

inline void PrDataOutput::Pack4B( char *text, long data )
{
    *(long *)text = htonl(data);
}

inline void PrDataOutput::Pack8B( char *text, double data )
{
    RemapDouble x;
    x.d = data;
    if (endian_ == PR_LITTLE_ENDIAN)
      {
	  Pack4B(text,x.s.l);
	  Pack4B(text+4,x.s.h);
      }
    else
      {
	  Pack4B(text+4,x.s.l);
	  Pack4B(text,x.s.h);
      }
}

int PrDataOutput::Pack( char *data, int num, int size )
{
    char *text = buffer_ + byteCounter_;
    int i;
    switch(size)
      {
	case 4:
	  for(i=0;i<num;i++)
	    Pack4B(text+i*4,*(long *)(data+i*4));
	  break;
	case 2:
	  for(i=0;i<num;i++)
	    Pack2B(text+i*2,*(short *)(data+i*2));
	  break;
	case 1:
	  for(i=0;i<num;i++)
	    Pack1B(text+i,data[i]);
	  break;
	case 8:
	  for(i=0;i<num;i++)
	    Pack8B(text+i*8,*(double *)(data+i*8));
	  break;
      }
    byteCounter_ += size*num;
    return (size*num);
}



