/*  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 "IONotifier.hxx"
#include "TCPListener.hxx"
#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),notifierOfIncomingConn(0),channelReadable(0)
{
    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");
    }
    notifierOfIncomingConn=new IONotifier(socketItself);
        //when notifierOfIncomingConn becomes readable it's because it is recieving an incoming connection, Accept it with accept method. The next statment connects this readable signal to accept slot
    notifierOfIncomingConn->readable.connect(slot(*this,&TCPListener::Accept));
    
}

TCPListener::~TCPListener() {
    if(comMode){
        cerr<<"deleting\n";
        
        this->ClosingChannel();
    }
    delete notifierOfIncomingConn;
    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{
//first of all, don't acccept new connections
        delete notifierOfIncomingConn;
        
        clientAddress=inet_ntoa(socketAddr.sin_addr);
        clientPort=ntohs(socketAddr.sin_port);
        fprintf(stderr, "TCPListener: client contacted from %s:%hd\n", clientAddress, clientPort);
//            fconfigure $channel -buffering line
// assume channel opens a O_SYNC 
        channelReadable=new IONotifier(channel);
        channelReadable->readable.connect(slot(this,&TCPListener::Receive));
        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
//      cerr << "Receive\n"
    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(){
    cerr<<"TCPListener: closing socket to client from "<<clientAddress<<":"<<clientPort<<"\n";
    delete channelReadable;
    close(channel);
    comMode=false;
//setup again
    notifierOfIncomingConn=new IONotifier(socketItself);
        //when notifierOfIncomingConn becomes readable it's because it is recieving an incoming connection, Accept it with accept method. The next statment connects this readable signal to accept slot
    notifierOfIncomingConn->readable.connect(slot(*this,&TCPListener::Accept));
    
}
//this public method should be capitalized for some consistency. Put the change should be propagated. class Make up!
void TCPListener::process (){
//to be overriden
    cerr<<"TCPListener: message Received ["<<receivedStream<<"] and processed from "<<clientAddress<<":"<<clientPort<<"\n";
    
}

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


