/***************************************************************
 * PrDataInput.cpp
 *
 * This is an implementation of DataInput methods.
 *
 ****************************************************************/

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

#include "PrDataInput.h"
#include "PrSocket.h"

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

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

#include <sys/time.h>
#include <sys/select.h>

#include <unistd.h>
#include <sys/ioctl.h>

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

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

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

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

int PrDataInput::select( long msec )
{
  fd_set r,w;
  struct timeval timeout;

  FD_ZERO(&r);
  FD_ZERO(&w);
  FD_SET(socketFD_, &r);
  timeout.tv_sec=(long)(msec / 1000);
  timeout.tv_usec=(long)(msec - timeout.tv_sec*1000)*1000;
  if (::select(socketFD_ + 1, &r, &w, (fd_set *) 0, &timeout) < 0)
    perror("PrDataInput: select() error");
  if (FD_ISSET(socketFD_, &r)) return 1;
  else return 0;
}

// returns number of data bytes.
int PrDataInput::ready()
{
    if (readSize_ == 1)
      {
	  char s[2];
	  if (Peek(s,2) < 2)
	    return -1;
	  size_ = Unpack2B(s);

	  memset(buffer_,NULL,bufferSize_);
	  InitByteCounter();
	  readSize_ = 0;
      }

    if (Peek() < size_ )
      return -1;

    if (Receive() != size_)
      {
	  perror("PrDataInput: ready() error");
	  return PR_DATAINPUT_ERROR;
      }

    type_ = Unpack2B(buffer_+2);

    readSize_ = 1;
    return (size_ - InitByteCounter());
}

/*
// p280 in UNIX Network Programming by Stevens
int PrDataInput::readLine( char *ptr, int maxlen )
{
  int n,rc;
  char c;
    
  for(n=1;n<maxlen;n++)
    {
      if ((rc=recv(socketFD_,&c,1,0))==1)
	{
	  *ptr++ = c; 
	  if(c=='\n') 
	    break;
	} else if (rc == 0) 
	  {
	    if (n==1) return(0);	// EOF, no data read
	    else break;		// EOF, some data was read
	  } 
      else return (-1);		// error 
    }
  *ptr = 0;
  return(n);
}
*/

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

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

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

    memset(buffer_,NULL,bufferSize_);
    readSize_ = 1;

    return PR_DATAINPUT_OK;
}

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

// p279 in UNIX Network Programming by Stevens
int PrDataInput::Receive()
{
  char *ptr = buffer_;

  int nleft,nread;
  nleft = size_;
  while(nleft>0)
    {
      nread = recv(socketFD_,ptr,nleft,0);
      if(nread<0) return (nread); /* error */
      else if(nread==0) break; /* EOF */
      nleft -= nread;
      ptr += nread;
    }
  return(size_ - nleft);  // return >= 0
}

int PrDataInput::Peek()
{
    int arg=0;

#ifdef PR_QNX
    if (ioctl((short)socketFD_,FIONREAD,&arg)<0)
#else // PR_QNX
    if (ioctl(socketFD_,FIONREAD,&arg)<0)
#endif // PR_QNX
      {
	  perror("PrDataInput::Peek():ioctl() error");
	  arg = -1;
      }

    return arg;
}

int PrDataInput::Peek( char *buff, int nbytes )
{
    int arg = Peek();
    if (arg >= nbytes)
      {
	  if (recv(socketFD_,buff,nbytes,MSG_PEEK)!=nbytes)
	    {
		perror("PrDataInput::Peek():recv() error");
		arg = -1;
	    }
      }
    return arg;
}

/*
int PrDataInput::Peek( char *buff, int nbytes )
{
    if (select(0)==0)
      return -1;
    int n=recv(socketFD_,buff,nbytes,MSG_PEEK);
//    printf("peek=%d\n",n);
    if (n==nbytes)
      return nbytes;
    else
      return -1;
}
*/

inline char PrDataInput::Unpack1B( char *text )
{
    return text[0];
}

inline short PrDataInput::Unpack2B( char *text )
{
    return ntohs(*(short *)text);
}

inline long PrDataInput::Unpack4B( char *text )
{
    return ntohl(*(long *)text);
}

inline double PrDataInput::Unpack8B( char *text )
{
    RemapDouble x;
    if (endian_ == PR_LITTLE_ENDIAN)
      {
	  x.s.l = Unpack4B(text);
	  x.s.h = Unpack4B(text+4);
      }
    else
      {
	  x.s.l = Unpack4B(text+4);
	  x.s.h = Unpack4B(text);
      }
    return (x.d);
}

int PrDataInput::Unpack( char *data, int num, int size )
{
    char *text = buffer_ + byteCounter_;
    int i;
    switch(size)
      {
	case 4:
	  for(i=0;i<num;i++)
	    *(long *)(data+i*4) = Unpack4B(text+i*4);
	  break;
	case 2:
	  for(i=0;i<num;i++)
	    *(short *)(data+i*2) = Unpack2B(text+i*2);
	  break;
	case 1:
	  for(i=0;i<num;i++)
	    data[i] = Unpack1B(text+i);
	  break;
	case 8:
	  for(i=0;i<num;i++)
	    *(double *)(data+i*8) = Unpack8B(text+i*8);
	  break;
      }
    byteCounter_ += size*num;
    return (size*num);
}


