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

AUTHOR IST ERIK THIELE
INAUTHORISIERTE MANIPULATION DES SOURCES WIRD MIT SOFORTIGEM EINLOCHEN
BESTRAFT.

                communic.H          Eriks C++ Communication Library


      ######  #######    #     #####  ####### #     #    #    #     #
      #     # #         # #   #     # #       ##   ##   # #   ##    #
      #     # #        #   #  #       #       # # # #  #   #  # #   #
      ######  #####   #     # #       #####   #  #  # #     # #  #  #
      #       #       ####### #       #       #     # ####### #   # #
      #       #       #     # #     # #       #     # #     # #    ##
      #       ####### #     #  #####  ####### #     # #     # #     #



ACHTUNG !!!

DIE MESSAGEARTEN 0-100 SIND JEWEILS INCLUSIV FUER COMMUNIC.H RESERVIERT, 
ALLE RESTLICHEN STEHEN FUER ANWENDER ZUR VERFUEGUNG

***************************************************************************
*/
#ifndef __communic_h
#define __communic_h

#include <unistd.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <string>
#include <netdb.h>
#include <stdio.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <stdlib.h>

#include "allgtypedef.H"
#include "textfata.H"
#include "listen2.H"

/********************************************** VERBINDUNGSTYPE ***/
typedef struct
{
  int handle;
}
verbindungstype;


/********************************************** MSGTYPENDEFINES ***/
#define QUIT_MSG          1


/********************************************** TIMEOUT DEFINES ***/
extern timeval communic_polltimeval;

#define RECVMSG_UNENDLICH   NULL
#define RECVMSG_POLL (&communic_polltimeval)

// Reuse a socket 
void set_socket_reuse(const int handle);


/************************************* COMMUNICATION_STREAMTYPE ***/
/// A communication stream (TCP/IP)
class communication_streamtype 
{
public:
  // muell
  communication_streamtype(){}

  communication_streamtype(verbindungstype *v);
  virtual ~communication_streamtype();
  
  // Wenn diese gesendet wurde kann man keine msg mehr senden  (changed by JENS)
  void sendquitmsg();
  
  void sendmsg (const char  *buf,int32  len,int32  msgart);
  int  recvmsg (char **buf,int32 *len,int32 *msgart, timeval *timeout=RECVMSG_POLL);
/*
  Rueckgabewerte:

  0 : Keine Msg da
  1 : Msg da
        wenn buf!=NULL ist die MSG in *buf,len,msgart
	aber wenn buf==NULL, ist die MSG da, aber wird nicht zurueckgegeben
          sie steht fuer einem spaeteren zeitpunkt abholbereit.

  wenn timeout = RECVMSG_UNENDLICH     (NULL)
    es wird gewartet bis msg da ist
    rueckgabewert IMMER 1
  wenn timeout = RECVMSG_POLL          ({0,0})
    es wird ueberhauptnicht gewartet
    rueckgabewert ist 0 oder 1
  wenn timeout = timeval { tv_sec = a; tv_usec = b }
    es wird gewartet, bis entweder zeit abgelaufen, oder msg angekommen.
    rueckgabewert ist 0 oder 1
    in timeout steht danach die restzeit.
    wenn man wissen will, ob die zeit abgelaufen iss, dann fragt man net
    via timeout?=0 sondern man checkt den rueckgabewert !!!!!!!!!!!!!!!!!!!
    die restzeit ist nur interresant wenn rueckgabewert=1
*/
  
  int msgavail (timeval *timeout);
  
  int calc();
  /* Rueckgabewerte: (beziehen sich auf jens iteratorliste)
     0 : Objekt net loeschen
     1 : Objekt loeschen                                        */
  
  virtual void verarbeitemsg (char *buf,int32 len,int32 msgart);
  // der buf muss in der funktion freigegeben werden !!!
  
  int handle;       // handle des streams
private:
  void blocks_yes();
  void blocks_no();
  
  int blocks;       // fuer die blocks_* funktionen, damit nicht 2 mal block machen
  int aktreading;   // aktueller lesemodus siehe unten
  int toberead;     // bis zum naechsten event muss soviel gelesen werden
  int aktrpos;      // aktuelle lesepos in ib
  int quitmodus;    // 0 = keinquitmodus, 1 = quitmodus
  
  // **** es folgen die zwischenpuffer fuer eine msg
  char *ib;         // der buffer
  int32 groesse;    // groesse der message
  int32 ihreart;    // msgart der message
};








/********************************* COMMUNICATION_SERVERTYPE ***/
/* Beschreibung:

Vorraussetzung:
haben alle einen konstruktor mit param: (verbindstype *)
Muessen alle von communication_streamtype abgeleitet sein !
*/

/// A list of TCP/IP connections (the server object)

template <class objtype>
class server_kettetype : public kettetype <objtype>
{
public:
  int msgavail(timeval *timeout);
  server_kettetype(int portnumber);
  objtype *managenewconnects();
  
  // Changed by Jens
  // diese kette ruft den calc aller elems auf aber:
  // wenn ein elem gekillt werden will darf ich das nicht gleich tun !!!
  // drum die liste tokill !!
  // Weil das elem hat dann evtl. kurz vorher noch die quitmsg rausgesendet !!
  int calc()
  {
    objtype *akt=anfang->next;
    while (akt!=ende)
      {
	if (akt->calc())
	  {
	    /*	    // Soll getoetet werden
	    tokill.zeiger_push(akt);
	    akt=akt->next;
	    zeiger_delbefore(akt);*/
	    akt=akt->next;
	    delbefore(akt);
	  }
	else
	  akt=akt->next;
      }
    return (anzzeigerelem==0);
  }
  void sendquitmsg()
  {
    objtype *akt=anfang->next;
    while (akt!=ende)
      {
	akt->sendquitmsg();
	akt=akt->next;
      }
  }

  ~server_kettetype();
private:
  //  kettetype<objtype> tokill;
  sockaddr_in from;
  int f;
};

/*********************** communication_servertype::msgavail ***/
template <class objtype>
int server_kettetype<objtype>::msgavail(timeval *timeout)
{
    fd_set checker;
    FD_ZERO(&checker);

  /* ACHTUNG URGENT TODO JENS CAUSED BY INTERNAL COMPILER ERROR */
  if (!anzzeigerelem) {
    sleep(timeout->tv_sec);
    usleep(timeout->tv_usec);
    return(0);
  };

  objtype *laufzeiger;
  int erg;

  do{
    laufzeiger=anfang->next;
    while(laufzeiger!=ende) {
      if (laufzeiger->msgavail(RECVMSG_POLL)) return(1);
      laufzeiger=laufzeiger->next;
    };
    /*	
    fd_set checker;
    FD_ZERO(&checker);
    laufzeiger=anfang->next;
    while(laufzeiger!=ende) {
      FD_SET(laufzeiger->handle, &checker);
      laufzeiger=laufzeiger->next;
    };
    erg = select(FD_SETSIZE, &checker, NULL, NULL, timeout);
    if (erg < 0)
      fatalfehler("communication_streamtype::recvmsg:"
      "select: errno definiert");*/
  }while((erg)||(!timeout)||(timeout->tv_sec)||(timeout->tv_usec));
  /*  */
  return(0);
};

/******* communication_servertype::communication_servertype ***/
template <class objtype> server_kettetype<objtype>::server_kettetype(int portnumber)
{
#ifdef DEBUG_communic_H
  printf("creating a server_kettetype\n");
#endif
  memset(&from,0, sizeof(from)); // originally bzero(&from,sizeof(from))
    from.sin_port=portnumber;
    f = socket(AF_INET,SOCK_STREAM,0);
    if (f<0) 
	fatalfehler("server_kettetype::server_kettetype:"
		    "socket: Errno definiert");
    set_socket_reuse(f);

    if (fcntl(f,F_SETFL,FNDELAY)<0) 
	fatalfehler("server_kettetype::server_kettetype:"
		    "fcntl: Errno definiert");

    if (bind(f,(sockaddr *)&from,sizeof(from))<0) 
	fatalfehler("server_kettetype::server_kettetype:"
		    "bind: Errno definiert");

    if (listen(f,5)<0)
	fatalfehler("server_kettetype::server_kettetype:"
		    "listen: Errno definiert");
};



/****** server_kettetype::~server_kettetype ***/
template <class objtype> server_kettetype<objtype>::~server_kettetype()
{
#ifdef DEBUG_communic_H
  printf("killing a server_kettetype\n");
#endif
  sendquitmsg();
  if (close(f)<0)
    fatalfehler("server_kettetype::~server_kettetype:"
		"close: Errno definiert");
};



/****************** server_kettetype::managenewconnects ***/
/*

  Rueckgabewerte:

  NULL : Keine neue Verbindung da.
  pointer : Okay, pointer ist das Obj
  
  Es fuehrt _*JEDER*_
  Fehler zum Fatalfehler.

*/
template <class objtype> 
objtype *server_kettetype<objtype>::managenewconnects()
{
    verbindungstype v;   
    uint len=sizeof(from);
    v.handle=accept(f,(sockaddr *)&from,&len);
    if (v.handle<0) {
	if ((errno!=EINTR)&&(errno!=EWOULDBLOCK)) 
	    fatalfehler("void server_kettetype::managenewconnects:"
			"accept: Errno definiert");
	return(NULL);
    };
    objtype *erg=new objtype(&v);
    zeiger_push(erg);
    return(erg);
};




/********************************** COMMUNICATION_CLIENT_CONNECT ***/
/*

  Rueckgabewerte:

  1 : Okay, v ist die Verbindung
  
  spaeter werden andere Werte Bedeutungen haben wie
  "Rechner nicht gefunden" Seither fuehrt _*JEDER*_
  Fehler zum Fatalfehler.

  */  
int communication_client_connect(const char *hostname, int portnumber, verbindungstype *v);


#ifndef COMMUNIC_C_WILL_QUIT_MSG
#undef QUIT_MSG
#define QUIT_MSG HEYMAN DU DARFST QUITMSG NICHT VERWENDEN !!!
#endif
#endif

























