/***************************************************************************
                          SIMPLY POWERFUL TOOLKIT (SPTK)
                          CSocket.cpp  -  description
                             -------------------
    begin                : July 10 2002
    copyright            : (C) 2002-2006 by Alexey Parshin
    email                : alexeyp@gmail.com
 ***************************************************************************/

/***************************************************************************
   This library is free software; you can redistribute it and/or modify it 
   under the terms of the GNU Library General Public License as published by
   the Free Software Foundation; either version 2 of the License, or (at 
   your option) any later version.

   This library 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 Library
   General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; if not, write to the Free Software Foundation, 
   Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.

   Please report all bugs and problems to "alexeyp@gmail.com"
 ***************************************************************************/

#ifndef __CSOCKET_H__
#define __CSOCKET_H__

#ifndef _WIN32
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

   /// A socket handle is an integer
typedef int SOCKET;           

   /// A value to indicate an invalid handle
#define INVALID_SOCKET -1     

#else
   //#define _WINSOCKAPI_
#include <windows.h>
#include <winsock.h>
#endif

#include <sptk3/CException.h>
#include <sptk3/CStrings.h>
#include <sptk3/CBuffer.h>

namespace sptk {

   /// @addtogroup utility Utility Classes
   /// @{

   /// Buffered Socket reader.
	class CSocketReader : protected CBuffer {
		int     m_socket;       ///< Socket handle
		int     m_readOffset;   ///< Current offset in the read buffer

      /// Performs buffered read from the opened socket
      /// into a character buffer of limited size
      /// @param dest char *, destination buffer
      /// @param sz int, size of the destination buffer
      /// @param readLine bool, true if we want to read one line (ended with CRLF) only
		int bufferedRead(char *dest,int sz,bool readLine);

	public:

      /// Constructor
      /// @param bufferSize int, the desirable size of the internal buffer
		CSocketReader(int bufferSize=4096);

      /// Connects the reader to the socket handle
      /// @param socket int, socket handle
		void open(int socket);

      /// Disconnects the reader from the socket handle
		void close() { m_socket = 0; }

      /// Performs the buffered read
      /// @param dest char *, destination buffer
      /// @param sz int, size of the destination buffer
      /// @param readLine bool, true if we want to read one line (ended with CRLF) only
		int  read(char *dest,int sz,bool readLine=false);

      /// Performs the buffered read of '\n'-terminated string
      /// @param dest CBuffer&, destination buffer
		int  readLine(CBuffer& dest);
	};

   /// @brief Generic socket.
   ///
   /// Allows to establish a network connection
   /// to the host by name and port address
	class CSocket {
	protected:
		SOCKET              m_sockfd;       ///< Socket internal (OS) handle
		int                 m_domain;       ///< Socket domain type
		int                 m_type;         ///< Socket type
		int                 m_protocol;     ///< Socket protocol
		std::string         m_host;         ///< Host name
		int                 m_port;         ///< Port number
		fd_set              m_inputs;       ///< The set of socket descriptors for reading
		fd_set              m_outputs;      ///< The set of socket descriptors for writing
		CSocketReader       m_reader;       ///< Socket buffered reader
		CBuffer             m_stringBuffer; ///< Buffer to read a line

	protected:

#ifdef _WIN32
		static int  m_socketCount;          ///< Total number of the objects of this class
		static bool m_inited;               ///< Were the sockets inited?
		static void init();                 ///< WinSock initialization
		static void cleanup();              ///< WinSock cleanup
#endif

      /// Reads a single char from the socket
		char getChar();                 

	public:
      /// Constructor
		CSocket(int domain=AF_INET, int type=SOCK_STREAM, int protocol=0);

      /// Destructor
		virtual ~CSocket();

      /// Returns socket handle
		int  handle() const { return m_sockfd; }

      /// Attaches socket handle
		void attach(int socketHandle);

      /// Sets the host name
		void host(std::string hostName);

      /// Returns the host name
		std::string host() const { return m_host; }

      /// Sets the port number
		void port(int portNumber);

      /// Returns the current port number
      /// @returns port number
		int port() const { return m_port; }

      /// Opens the socket connection by address.
      /// @param addr struct sockaddr_in&, defines socket address/port information
      /// @param forBind bool, true for the server socket and false for the client socket
		void open_addr(struct sockaddr_in& addr,bool forBind=false);

      /// Opens the client socket connection by host and port
		virtual void open(std::string hostName="", int port=0);

      /// Opens the server socket connection on port (binds/listens)
		void listen(int portNumber=0);

      /// In server mode, waits for the incoming connection.
      /// When incoming connection is made, exits returning the connection info
      /// @param clientSocketFD int&, connected client socket FD
      /// @param clientInfo sockaddr_in&, connected client info
		void accept(int& clientSocketFD,struct sockaddr_in& clientInfo);

      /// Closes the socket connection 
		virtual void close();

      /// Returns the current socket state
      /// @returns true if socket is opened
		bool active() const { return m_sockfd != INVALID_SOCKET; }

      /// Calls Unix fcntl() or Windows ioctlsocket()
		int  control(int flag, unsigned long *check);

      /// Sets socket option value
		int  setOption(int level,int option,int  value);

      /// Gets socket option value
		int  getOption(int level,int option,int& value);

      /// Reads one line (terminated with CRLF) from the socket
      /// into existing memory buffer. The output string should fit
      /// the buffer or returned incomplete.
      /// @param buffer char *, the destination buffer
      /// @param size int, the destination buffer size
      /// @returns the number of bytes read from the socket
		int readLine(char *buffer,int size);

      /// Reads one line (terminated with CRLF) from the socket
      /// into existing memory buffer. The memory buffer is 
      /// extended to fit the string.
      /// @param buffer CBuffer&, the destination buffer
      /// @returns the number of bytes read from the socket
		int readLine(CBuffer& buffer);

      /// Reads one line (terminated with CRLF) from the socket
      /// into string
      /// @param s std::string&, the destination string
      /// @returns the number of bytes read from the socket
		int readLine(std::string& s);

      /// Reads data from the socket
      /// @param buffer char *, the memory buffer
      /// @param size int, the memory buffer size
      /// @returns the number of bytes read from the socket
		int read(char *buffer,int size);

      /// Reads data from the socket into memory buffer
      /// @param buffer const CBuffer&, the memory buffer
      /// @returns the number of bytes read from the socket
		int read(CBuffer& buffer);

      /// Writes data to the socket
      /// @param buffer const char *, the memory buffer
      /// @param size int, the memory buffer size
      /// @returns the number of bytes written to the socket
		int write(const char *buffer,int size);

      /// Writes data to the socket
      /// @param buffer const CBuffer&, the memory buffer
      /// @returns the number of bytes written to the socket
		int write(const CBuffer& buffer);

      /// Reports true if socket is ready for reading from it
		bool readyToRead(int waitmsec);

      /// Reports true if socket is ready for writing to it
		bool readyToWrite();

      /// Stream std::string input
		const CSocket& operator << ( const std::string& );

      /// Stream std::string output
		const CSocket& operator >> ( std::string& );
	};
	/// @}
}
#endif
