/*  Copyright (C) 2001 Marc Casaldaliga Albisu <casaldaliga@ifae.es>
================================================================
 
  This code is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.
 
  This code is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
 
  You should have received a copy of the GNU General Public License
  along with Emacs (which is required to make this stuff work); if
  not, write to the Free Software Foundation, Inc., 675 Mass Ave,
  Cambridge, MA 02139, USA.
==================================================================
*/ 

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> 
#include "socket_functions.h"
#include "TCPListener.H"
#include <string>
//for signals/slots (Callbacks in C++, see http://libsigc.sourceforge.net/
#include <sigc++/signal_system.h>
using namespace SigC;
//for perror
#include <stdio.h>
//for close
#include <unistd.h>


TCPListener::TCPListener (int port_) 
        :port(port_),newReceived(false),comMode(false)//,destroy(false)
{
    socketItself=make_socket(port);
        //specify that socketItself it's willing to listen only at one client at a time
    
    if( listen(socketItself,1)<0){
        perror("in listen");
    }
    pthread_create(&thread,NULL,&TCPListener::Listening,this);
    pthread_mutex_init(&mutex,NULL);
}
void * TCPListener::Listening( void* arg)
{
    TCPListener* self=(TCPListener *) arg;
#ifdef DEBUG
    printf("TCPListener: Created Listening thread\n");
    
#endif
    fd_set active_fd_set, read_fd_set;
    int i;
    size_t size;


                /* Initialize the set of active sockets. */
    FD_ZERO (&active_fd_set);
    FD_SET (self->socketItself, &active_fd_set);

    while (1)
    { 
//        pthread_mutex_lock(&(self->mutex));
//        if(self->destroy) break;
//        pthread_mutex_unlock(&(self->mutex));

            /* Block until input arrives on one or more active sockets. */
        read_fd_set = active_fd_set;
        if (select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL) < 0)
        {
            perror("TCPListener: select");
            exit (EXIT_FAILURE);
        }

            /* Service all the sockets with input pending. */
        for (i = 0; i < FD_SETSIZE; ++i) {
            
            if (FD_ISSET (i, &read_fd_set))
            {
                if (i == self->socketItself)
                {
                /* Connection request on original socket. */
#ifdef DEBUG
                    printf("TCPListener: Accepting connections\n");
#endif
                    self->Accept();
                        //monitor the created channel
                    FD_SET (self->channel, &active_fd_set);
                        //but since we don't more than two clients, don't monitor incoming_connections_fd
                    FD_CLR (self->socketItself, &active_fd_set);
                }
                else
                {
                    
                        /* Data arriving on an already-connected socket. */
#ifdef DEBUG
                    printf("TCPListener: Received\n");
#endif
                   
        
                    self->Receive();
                    if(!self->comMode){ //if connection has been closed to client (after the Receive), until new connection is open don't monitor this
                        FD_CLR (self->channel, &active_fd_set);
//monitor incoming_connections_fd back again
                        FD_SET (self->socketItself, &active_fd_set);
                    }
                    
                }
            }
        }
    
    }
#ifdef DEBUG
    printf("TCPListener: Listening thread finishing\n");
    
#endif

}


TCPListener::~TCPListener() {
    if(comMode){
#ifdef DEBUG
        printf("TCPListener: deleting\n");
#endif
        this->ClosingChannel();
    }
//      pthread_mutex_lock(&mutex);
//      destroy=true;
//      pthread_mutex_unlock(&mutex); 
//      pthread_join(thread,NULL);
    pthread_cancel(thread);
    
    close(socketItself);

}
    
void TCPListener::Accept () {
    size_t size=sizeof(socketAddr);
    channel=accept(socketItself, (struct sockaddr *) &socketAddr, &size);
    if(channel<0){
        perror("in Accept");
//            exit (EXIT_FAILURE);
    }else{
        clientAddress=inet_ntoa(socketAddr.sin_addr);
        clientPort=ntohs(socketAddr.sin_port);
#ifdef DEBUG
        fprintf(stderr, "TCPListener: client contacted from %s:%hd\n", clientAddress, clientPort);
#endif
//            fconfigure $channel -buffering line
        comMode=true;
        
    }
}


//this next method is only internally called when something arrives at channel (be careful, it can be the connection has closed)
    
void TCPListener::Receive () { //Syncronous receive. If you are not sure there will be data to read, schedule this receive with IONotifier or something. Otherwise it will block
#ifdef DEBUG
    printf("TCPListener: Receive from TCPListener %d\n", this);
#endif
    int nbytes;
    nbytes = read (channel, receivedStream, MAXMSG);
    if (nbytes < 0){
            /* Read error. */
        perror("TCPListener: read");
        exit (EXIT_FAILURE);
    }else if (nbytes == 0){
        
            /* End-of-file. */
        receivedStream[0]='0';
        receivedStream[1]='\0';
        this->ClosingChannel();
    }else{
            /* Data read. */
            //look for '\n' and chomp, in anycase read doesn't supply the \n so add it
        if(receivedStream[nbytes-1]=='\n'){
            receivedStream[nbytes-1]='\0';
#ifdef DEBUG        
            printf("TCPListener: chomp\n");
#endif
        }else{
            receivedStream[nbytes]='\0';
        }
#ifdef DEBUG        
        printf("TCPListener: got message: `%s'\n", receivedStream);
#endif
        this->process();
        newReceived=true;
    }
}

void TCPListener::ClosingChannel(){
#ifdef DEBUG
    printf("TCPListener: closing socket to client from %d:%d\n",clientAddress,clientPort);
#endif 
    
    close(channel);
    comMode=false;

}
//this public method should be capitalized for some consistency. Put the change should be propagated. class Make up!
void TCPListener::process (){
//to be overriden
#ifdef DEBUG
    printf("TCPListener: message Received [%s] and processed from client %s:%d \n",receivedStream,clientAddress,clientPort);
#endif   
    
}

string TCPListener::ReturnNew () {
    if (newReceived) {
        newReceived=false;
        return string(receivedStream);
    }else{
        return string("0");
    }
}


