/*
***************************************************************************

AUTHOR IST ERIK THIELE
INAUTHORISIERTE MANIPULATION DES SOURCES WIRD MIT SOFORTIGEM EINLOCHEN
BESTRAFT
Huhu Erik ich habe es leider changen muessen ;-)

                communic.C          Eriks C++ Communication Library

die implementation der communication library. fuer dokumentation siehe
communic.H

***************************************************************************
*/
#define COMMUNIC_C_WILL_QUIT_MSG
#include "communic.H"
void set_socket_reuse(const int handle)
{
  int reuser=1;
  int len=sizeof(reuser);
  if (setsockopt(handle,SOL_SOCKET,SO_REUSEADDR,(void *)&reuser,len)<0) // (void *) cause of solaris
    fatalfehler("set_socket_reuse: setsockopt SO_REUSEADDR failed");
};

timeval communic_polltimeval={0,0};

inline int communication_streamtype::msgavail(timeval *timeout)
{
  return(recvmsg(NULL,NULL,NULL,timeout));
}


/******************** communication_streamtype::sendquitmsg ***/
void communication_streamtype::sendquitmsg()
{
  sendmsg(NULL,0,QUIT_MSG);
  quitmodus=1;
};



/*************************** communication_streamtype::calc ***/
int communication_streamtype::calc()
{
  char *buf;
  int32 len;
  int32 msgart;
  while (recvmsg(&buf,&len,&msgart)) {
#ifdef DEBUG_communic_H
    printf("MESG angekommen.\n");
#endif
    if (msgart==QUIT_MSG) {
      // free unnoetig, weil buf=NULL
      if (!quitmodus)
	sendquitmsg();
      printf("I have been asked to close a connection\n");
      return(1);
    }
    verarbeitemsg(buf,len,msgart);
  };
  return(0);
};


/****************** communication_streamtype::verarbeitemsg ***/
void communication_streamtype::verarbeitemsg(char *buf,int32 len,int32 msgart)
{
  // ************************ WICHTIG !!! ********************
  delete buf;  // <--- WICHTIGSTER BEFEHL GELL !!!
  fatalfehler("communication_streamtype::verarbeitemsg:"
	      "diese Funktion sollte per Ableitung ueberschrieben sein, oder\n"
	      "eine nicht-QUIT msg wurde empfangen, und diese soll aber nicht kommen.");
#ifdef kickwarn
  len++;
  msgart++;
#endif
}


/********************* communication_streamtype::blocks_yes ***/
void communication_streamtype::blocks_yes()
{
  if (!blocks) {
    blocks=1;
    if (fcntl(handle,F_SETFL,0)<0) 
      fatalfehler("communication_streamtype::blocks_yes:"
		  "fcntl: Errno definiert");
  };
};


/********************** communication_streamtype::blocks_no ***/
void communication_streamtype::blocks_no()
{
  if (blocks) {
    blocks=0;
    if (fcntl(handle,F_SETFL,FNDELAY)<0) 
      fatalfehler("communication_streamtype::blocks_no:"
		  "fcntl: Errno definiert");
  };
};


/************************ communication_streamtype::sendmsg ***/
void communication_streamtype::sendmsg(const char *buf,int32 len,int32 msgart)
{
  // Jens changed this
  if (quitmodus)
    return;

  int e;
  // *** soll notfalls blockieren
  blocks_yes();
  // *** Header schreiben
  e=write(handle,&len,sizeof(len));
  if (!e) fatalfehler("communication_streamtype::sendmsg:"
		      "write #1: gibt 0 zurueck -> stream wurde "
		      "von gegenseite closed");
  if (e<0)
    fatalfehler("communication_streamtype::sendmsg:"
		"write #1: Errno definiert");
  if (e!=sizeof(len))
    fatalfehler("communication_streamtype::sendmsg:"
		"write #1: Nicht so viele Bytes wie angegeben writed");
  
  // *** Msgart schreiben
  e=write(handle,&msgart,sizeof(msgart));
  if (!e) fatalfehler("communication_streamtype::sendmsg:"
		      "write #2: gibt 0 zurueck -> stream wurde "
		      "von gegenseite closed");
  if (e<0)
    fatalfehler("communication_streamtype::sendmsg:"
		"write #2: Errno definiert");
  if (e!=sizeof(msgart))
    fatalfehler("communication_streamtype::sendmsg:"
		"write #2: Nicht so viele Bytes wie angegeben writed");
  
  // *** Daten schreiben
  if (len) {
    e=write(handle,buf,len);
    if (!e) fatalfehler("communication_streamtype::sendmsg:"
			"write #3: gibt 0 zurueck -> stream wurde "
			"von gegenseite closed");
    if (e<0)
      fatalfehler("communication_streamtype::sendmsg:"
		  "write #3: Errno definiert");
    if (e!=len)
      fatalfehler("communication_streamtype::sendmsg:"
		  "write #3: Nicht so viele Bytes wie angegeben writed");
  };
};


/************************ communication_streamtype::recvmsg ***/
int communication_streamtype::recvmsg(char **buf,int32 *len,int32 *msgart, 
				      timeval *timeout)
{
  /***
    Bedeutung von aktreading:
    0 : size+artlesemodus
    1 : datenlesemodus
    2 : MSG ist da und abholbereit (kbhit-problem)
    ***/
  
  fd_set checker;
  int erg;
  
  if (aktreading==2) {
    if (buf) {
      goto ausgabe;
    }else{
      return(1);
    };
  };
  
  blocks_no();
  
  do {
    FD_ZERO(&checker);
    FD_SET(handle, &checker);
    
    erg = select(FD_SETSIZE, &checker, NULL, NULL, timeout);
    
    if (erg < 0)  // changed by jens  todo 
      {      
	cerr <<"Warning !! select failed try again .. this may cause some strange effects\n";
	erg = select(FD_SETSIZE, &checker, NULL, NULL, timeout);
	if (erg < 0)
	  fatalfehler("communication_streamtype::recvmsg:"
		      "select: errno definiert");
      }
    if (FD_ISSET(handle, &checker)) {
    mark1:
      if (toberead) {
	erg = read(handle,ib+aktrpos,toberead);
	if (ib[toberead+aktrpos]!=77)
	  fatalfehler("communic.C: read over buffer");
	if (!erg) fatalfehler("communication_streamtype::recvmsg:"
			      "read: gibt 0 zurueck -> stream wurde\n"
			      "von gegenseite closed");
	if (erg<0) {
	  if (errno==EWOULDBLOCK) {
	    erg=0;
	  }else{
	    fatalfehler("communication_streamtype::recvmsg:"
			"read: Errno definiert");
	  };
	};
      }else{
	erg=0;
      };
      toberead-=erg;
      aktrpos+=erg;
      
      if (!toberead) {
	aktrpos=0;
	if (aktreading) {
	  // *** datenlesemodus beendet
	  if (buf) {
	  ausgabe:
	    aktreading=0;
	    *buf=ib;
	    *len=groesse;
	    *msgart=ihreart;
	    
	    toberead=sizeof(int32)<<1;
	    ib=new char[toberead+1]; 
	    ib[toberead]=77; // only for checks
	  }else{
	    aktreading=2;
	  };
	  return(1);
	}else{
	  // *** size+artlesemodus beendet
	  aktreading=1;
	  groesse=*( (int32*) ib );
	  ihreart=*( (int32*) (ib+sizeof(int32)) );
	  if (groesse<0) fatalfehler ("communication_streamtype::recvmsg:"
				      "groesse: ist kleiner 0 -> msg ungueltig");
	  toberead=groesse;
	  delete ib;
	  ib=new char[groesse+1];
	  ib[toberead]=77; // only for checks
	  goto mark1;
	};
      };
    };    
  }while((!timeout)||(timeout->tv_sec)||(timeout->tv_usec));
  
  return(0);
};



/******* communication_streamtype::communication_streamtype ***/
communication_streamtype::communication_streamtype(verbindungstype *v)
{
  handle=v->handle;
  
#ifdef DEBUG_communic_H
  printf("opening a communicatiom_streamtype\n");
#endif
  
  blocks=1;
  blocks_no();
  aktreading=0;
  aktrpos=0;
  toberead=sizeof(int32)<<1;
  ib=new char[toberead+1]; 
  ib[toberead]=77;
  quitmodus=0;
};



/****** communication_streamtype::~communication_streamtype ***/
communication_streamtype::~communication_streamtype()
{
#ifdef DEBUG_communic_H
  printf("killing a communication_streamtype\n");
#endif
  delete ib;
  
  if (!quitmodus)
    fatalfehler("communication_streamtype::~communication_streamtype"
		"quitmodus: destruktoraufruf, aber stream noch net"
		"gequitted !!!");
  
  if (close(handle)<0) 
    fatalfehler("communication_streamtype::~communication_streamtype:"
		"fclose: errno ist definiert");
};



int communication_client_connect(const char *hostname, int portnumber, verbindungstype *v)
{
  sockaddr_in server;
  hostent *hp;
  
  hp=gethostbyname(hostname);
  if (hp==NULL) {
    fatalfehler("communication_client::communication_client:"
		"gethostbyname: host unknown");
  };
  memset((void *)&server,0,sizeof(server)); // bzero((char*)&server,sizeof(server));
  bcopy(hp->h_addr,(char*)&server.sin_addr,hp->h_length);
  server.sin_family=hp->h_addrtype;
  server.sin_port=portnumber;
  v->handle=socket(AF_INET,SOCK_STREAM,0);

  if (v->handle<0)
    fatalfehler("communication_client::communication_client:"
		"socket: Errno definiert");  
  set_socket_reuse(v->handle);
  
  if (connect(v->handle,(sockaddr*)&server,sizeof(server))<0) 
    fatalfehler("communication_client::communication_client:"
		"connect: Errno definiert");
  
  return(1);
};

