#ifndef n_nserverImageSink_H
#define n_nserverImageSink_H

/*
 *
 * nucleo/image/sink/nserverImageSink.H --
 *
 * Copyright (C) Nicolas Roussel
 *
 * See the file LICENSE for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 */

#include <nucleo/network/tcp/TcpServer.H>
#include <nucleo/network/http/ServerPush.H>
#include <nucleo/image/sink/ImageSink.H>
#include <nucleo/image/encoding/Conversion.H>

#include <queue>

#include <sys/socket.h>

namespace nucleo {

  class nserverImageSink : public ImageSink {

  private:

    class spsClient : public ReactiveObject {
    private:
	 int socket ;
	 ServerPush *serverpush ;
    public:
	 spsClient(int fd) {
	   socket = fd ;
	   serverpush = new ServerPush(socket) ;
	 }
	 bool push(const char *content_type, char *ptr, int size, char *header) {
	   serverpush->push(content_type, ptr, size, header) ;
	   return true ;
	 }
	 ~spsClient() {
	   shutdown(socket,2) ;
	   close(socket) ;
	   delete serverpush ;
	 }
    } ;

    Image::Encoding _encoding ;
    unsigned int _quality ;

    TcpServer *_server ;
    int _port, _backLog ;
    unsigned int _maxClients ;
    std::queue<spsClient *> _clients ;

  protected:

    void react(Observable*) {
	 TcpConnection *connection = _server->getNewClient() ;
	 while (connection) {
	   int fd = connection->getFd() ;
	   if (_clients.size()==_maxClients) {
		spsClient *client = _clients.front() ;
		_clients.pop() ;
		delete client ;
	   }
	   _clients.push(new spsClient(fd)) ;

	   connection = _server->getNewClient() ;
	 }
    }

  public:

    nserverImageSink(int port=0, unsigned int maxclients=10, int backlog=10) {
	 _server = 0 ;
	 _port = port ;
	 _backLog = backlog ;
	 _maxClients = maxclients ;
    }

    nserverImageSink(const URL &url) {
	 _server = 0 ;
	 _port = 0 ;

	 std::string port = url.getPort() ;
	 _port = atoi(port.c_str()) ;

	 std::string query=url.getQuery() ;

	 std::string encoding ;
	 _encoding = Image::JPEG ;    
	 if (URL::getQueryArg(query, "encoding", &encoding))
	   _encoding = Image::getEncodingByName(encoding) ;
    
	 _quality = 80 ;
	 URL::getQueryArg(query, "quality", &_quality) ;

	 _backLog = 10 ;
	 URL::getQueryArg(query, "backlog", &_backLog) ;

	 _maxClients = 10 ;
	 URL::getQueryArg(query, "maxclients", &_maxClients) ;
    }

    ~nserverImageSink(void) {
	 stop() ;
    }

    bool start(void) {
	 if (_server) return false ;
	 _server = new TcpServer(_port, _backLog, false) ; 
	 subscribeTo(_server) ;
	 std::cerr << "spServer listening on http://127.0.0.1:" << _server->getPortNumber() ;
	 std::cerr << " (backlog=" << _backLog << ", maxclients=" << _maxClients << ")" ;
	 std::cerr << std::endl ;
	 return true ;
    }

    bool isActive(void) {
	 return (_server!=0) ;
    }

    bool handle(Image *img) {
	 if (! _clients.size()) return false ;

	 // img->debug(std::cerr) ; std::cerr << " <" ;
	 
	 Image copy(*img) ;
	 if (!convertImage(&copy, _encoding, _quality)) return false ;

	 const char *content_type = "image/jpeg" ;
	 char *ptr = (char *)copy.getData() ;
	 int size = (int)copy.getSize() ;

	 _chrono.tick() ;

	 char header[256] ;
	 sprintf(header, "nucleo-framerate: %f",_chrono.average()) ;

	 for (unsigned int i=0; i<_clients.size(); ++i) {
	   spsClient *client = _clients.front() ;
	   _clients.pop() ;
	   try {
		client->push(content_type, ptr, size, header) ;
		_clients.push(client) ;
		// std::cerr << "+" << std::flush ;
	   } catch (std::runtime_error e) {
		// std::cerr << "-" << std::flush ;
	   }
	 }

	 // std::cerr << ">" << std::endl ;

	 return true ;
    }

    bool stop(void) {
	 if (!_server) return false ;

	 while (_clients.size()) {
	   spsClient *client = _clients.front() ;
	   _clients.pop() ;
	   delete client ;
	 }
	 delete _server ;
	 return true ;
    }
    
  } ;

}

#endif
