#include "rec-sock-alsa.h"

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

/* thread for dialog with client */
void * serve_partner(void * ptr);

// global var:
int running_servers = 0;
int sockfd_server = 0;
int conn1[nCHN];
int conn2[nCHN];

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

void * accept_conn(void * ptr)
{
  pthread_t partner;
  int new_fd;
  struct sockaddr_in their_addr;
  int HasToBeVar = sizeof(their_addr);
  int sockfd = ((int *)ptr)[0];

  fprintf(stderr,"Starting thread waiting for new connections\n");

  bzero(&their_addr,sizeof(their_addr));

  while ((run_server) && (sockfd_server > 0))
  {
    if ((new_fd = accept(sockfd, &their_addr, &HasToBeVar)) == -1)
       { perror("accept"); continue; };
    fprintf(stderr,"Starting additional server thread\n");
    pthread_create (&partner, NULL, serve_partner, (void *)&new_fd);
    fprintf(stderr,"Additional server thread started\n");

  };
  
  LOCK;
  run_server = 0; // please stop serving
  // free some memory ... do other stuff ...
  UNLOCK;

  fprintf(stderr,"Waiting for servers to stop\n");
  while (running_servers > 0) usleep(1000);
  close_socket(sockfd);
  
  return ptr;
};

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

int open_socket(int port) /* liefert ein handle zurueck */
{
  struct sockaddr_in my_addr;
  int ch;
  int sockfd;
  pthread_t accepter;

  if ((port<1024) || (port>65519)) return 1;

  fprintf(stderr,"Initialising server sockets\n");

  pthread_mutex_init(&mutex, NULL);
  for (ch=0; ch<nCHN; ch++) conn2[ch] = conn1[ch] = 0;

  if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
     { perror("socket"); return -1; }; /* open socket */
     
  bzero(&my_addr,sizeof(my_addr));
  my_addr.sin_family = AF_INET;
  my_addr.sin_port = htons(port); /* short int ... */
  my_addr.sin_addr.s_addr = INADDR_ANY; /* use my address */

  if (bind(sockfd, &my_addr, sizeof(my_addr)) == -1)
     { perror("bind"); return -1; }; /* bind name to socket */
     
  if (listen(sockfd, 24 /* backlog queue size */) == -1)
     { perror("listen"); return -1; }; /* listen for connections */
       
  sockfd_server = sockfd;
  fprintf(stderr,"Starting server thread\n");
  pthread_create(&accepter, NULL, accept_conn, (void *)&sockfd_server);
  fprintf(stderr,"Server thread started\n");

  return sockfd;
};

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

int close_socket(int handle)
{
  fprintf(stderr,"closing down server\n");
  close(handle);
  return 0;
};

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

void * serve_partner(void * ptr)
{
  int my_fd = ((int *) ptr)[0];
  int len, retval, channel=-1;
  char combuf[200];

// ->
#define WIMP(n) LOCK;  running_servers--; \
                if (channel>=0) \
                   { \
 conn1[channel] = (conn1[channel] == my_fd) ? 0 : conn1[channel]; \
 conn2[channel] = (conn2[channel] == my_fd) ? 0 : conn2[channel]; \
                   }; \
                UNLOCK; close(my_fd); \
                fprintf(stderr,"End of server thread, fd=%d\n",my_fd); \
                return ptr;
// <-
// could use n as some kind of error code...
  
  fprintf(stderr,"Server thread for another client started, fd=%d\n",my_fd);
  
  LOCK;
  running_servers++;
  UNLOCK;

  if (send(my_fd,"Hello!",strlen("Hello!")+1,0) == -1)
     { perror("send"); WIMP(1); };

  if (recv(my_fd,combuf,sizeof(combuf),0) == -1)
     { perror("recv"); WIMP(1); };
  combuf[sizeof(combuf)-1] = 0;
  if ((len>=0) && (len<sizeof(combuf))) combuf[len] = 0;
  
  fprintf(stderr,"Received request <%s>\n",combuf);

  if (strncmp(combuf,"GET",3))
     { /* not GET, is it CLOSE ? */
       if (!strncmp(combuf,"CLOSE",5))
       { /* it is CLOSE */
         LOCK;
         run_server = 0;             // tell the others to stop too
         UNLOCK;
         fprintf(stderr,"CLOSE request detected, about to shut down\n");
         WIMP(0);
       } else
       { /* unknown command */
         fprintf(stderr,"Unknown command: %s\n",combuf);
         if (send(my_fd,"FAIL: Pardon?",strlen("FAIL: Pardon?")+1,0) == -1)
            { perror("send"); WIMP(1); };
         WIMP(1);
       };
     } else
     { /* command is GETn */
       channel = atoi(combuf+3);
       if ((channel<0) || (channel >= nCHN))
          { /* invalid channel selection */
            fprintf(stderr,"Invalid channel in GET %d\n",channel);
            if (send(my_fd,"FAIL: Channel?",strlen("FAIL: Channel??")+1,0) == -1)
               { perror("send"); WIMP(1); };
            WIMP(1);
          };
          
       LOCK;
       if ((conn1[channel]) && (conn2[channel]))
          {
            fprintf(stderr,"Only two clients per channel allowed for now\n"
                           "Channel %d busy (%d,%d)\n",
                           channel,conn1[channel],conn2[channel]);
            if (send(my_fd,"FAIL: Busy",strlen("FAIL: Busy")+1,0) == -1)
               { perror("send"); UNLOCK; WIMP(1); };
            UNLOCK; WIMP(1);
          };
       if (conn1[channel]) conn2[channel]=my_fd; 
          else                 conn1[channel]=my_fd;
       // and maybe alloc buffer and stuff
       UNLOCK;

       fprintf(stderr,"GET for channel %d accepted\n", channel);
       while (run_server) // send until client goes away or global shutdown
       { /* loop to satisfy GET */

         if ((retval = send_buffer(my_fd, channel))) 
            { fprintf(stderr,"Error %d while transmitting channel %d\n",
                      retval,channel);
              WIMP(1); 
            } else { /* fprintf(stderr,"."); */ };

         // convert buffer (locked), send buffer, recv (discard)
       };
       
     }; // end GET

  WIMP(0);
};
