// C++ Socket Wrapper
// SocketW base socket header 
//
// Started 020316
//
// License: LGPL v2.1+ (see the file LICENSE)
// (c)2002-2003 Anders Lindström

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

#include "sw_internal.h"

#include <unistd.h>
#include <string>

// Set error handling mode
// throw_errors == true  : Throws the error class on unhandled errors
// throw_errors == false : Exit on unhandled errors
// verbose == true       : Prints the error message to stderr on unhandled errors
//
// Default is throw_errors == false and verbose == true
void sw_setThrowMode(bool throw_errors);
void sw_setVerboseMode(bool verbose);
bool sw_getThrowMode(void);
bool sw_getVerboseMode(void);


// Abstract base class for streaming sockets
class DECLSPEC SWBaseSocket
{
public:	
	SWBaseSocket();
	virtual ~SWBaseSocket();
	
	// Error types
	// ok           - operation succesful
	// fatal        - unspecified error
	// notReady     - you should call the function again
	//                indicates that the function would block (if nowait/nonblocking)
	// portInUse    - this port is used by another socket ( on listen() )
	// notConnected - socket not connected (or valid)
	// msgTooLong   - the message size it too big for send()
	// terminated   - connection terminated (by peer)
	// noResponse   - can't connect() to peer
	// timeout      - a read/write operation timed out (only if a timeout value is set and if in blocking mode)
	// interrupted  - operation was interrupted by a nonblocked signal
	enum base_error{ok, fatal, notReady, portInUse, notConnected, msgTooLong, terminated, noResponse, timeout, interrupted};
	
	class DECLSPEC SWBaseError
	{
	public:
		SWBaseError();
		SWBaseError(base_error e);
		
		virtual ~SWBaseError(){;}
		
		virtual std::string get_error();
		virtual SWBaseSocket* get_failedClass(void);
		
		virtual bool operator==(SWBaseError e);
		virtual bool operator!=(SWBaseError e);
		
		virtual void set_errorString(std::string msg);
		virtual void set_failedClass(SWBaseSocket *pnt);
	protected:
		friend class SWBaseSocket;
		
		// The base error type
		base_error be;
		
		// Human readable error string
		std::string error_string;
		
		// A pointer to the class causing the error
		SWBaseSocket *failed_class;
	};
	
	
	// Note: If no SWBaseError class is provided with a method call (==NULL),
	// SocketW will print the error to stderr and exit or throw on errors.
	
	// Note: All bool functions returns true on success.
	
	// Block mode
	// blocking    - everythings blocks until completly done
	// noWait      - operations block but only once
	//               useful with blocking w. select()
	// nonblocking - don't block (you should use select())
	enum block_type{nonblocking, noWait, blocking};
	
	
	// Connection methods
	// qLimit - the maximum length the queue of pending connections.
	// Accept returns a new socket class connected with peer (should be
	// freed with delete) or NULL on failure. You can cast the class to
	// the correct type if you need to ( eg. (SWInetSocket *)mysocket ).
	virtual bool listen(int qLimit = 5, SWBaseError *error = NULL);
	virtual SWBaseSocket* accept(SWBaseError *error = NULL);
	// bind() and connect() are implemented in child classes
	
	// do the disconnect ritual (signal peer, wait for close singal and close socket)
	virtual bool disconnect(SWBaseError *error = NULL);
	
	// force close socket
	virtual bool close_fd();  //use with care, disconnect() is cleaner

	// Direct I/O (raw)
	// Can send/recv less bytes than specified!
	// Returns the actual amount of bytes sent/recv on sucess
	// and an negative integer on failure.
	virtual int send(const char *buf, int bytes, SWBaseError *error = NULL);
	virtual int sendmsg(const std::string msg, SWBaseError *error = NULL);
	virtual int recv(char *buf, int bytes, SWBaseError *error = NULL);
	virtual std::string recvmsg(int bytes = 256, SWBaseError *error = NULL);
	
	// Forced I/O
	// Force system to send/recv the specified amount of bytes.
	// On nowait/nonblocking: might return with notReady and then you
	// MUST call the same method again (eg. wait with select() to know when) 
	// with the same parameters until the operation is finished.
	// Returns 'bytes' when finished, negative integer on failure and
	// 'notReady'. In the 'notReady' case, -(return value) is the amount of
	// bytes sent/recv so far.
	virtual int fsend(const char *buf, int bytes, SWBaseError *error = NULL);
	virtual int fsendmsg(const std::string msg, SWBaseError *error = NULL);
	virtual int frecv(char *buf, int bytes, SWBaseError *error = NULL);
	
	// Tools
	// get_fd() - get socket descriptor, can be used with select()
	// returns -1 on failure.
	// get_host/peer fills the provided structures with info about the
	// host/peer (see man unix & ip).
	// SWInetSocket has some more tools for TCP/IP sockets.
	virtual int get_fd(SWBaseError *error);
	virtual bool get_host(sockaddr *host, SWBaseError *error = NULL);
	virtual bool get_peer(sockaddr *peer, SWBaseError *error = NULL);
	
	// Set recv timeout (only in blocking mode).
	// set_timeout(0,0) means wait forever (default).
	// This affects the functions recv(), send(), accept() and disconnect()
	// and others that use those, i.e. all frecvmsg().
	void set_timeout(Uint32 sec, Uint32 usec){ tsec = sec, tusec = usec; }
	
	// Error handling
	virtual void print_error(); //prints the last error if any to stderr
	virtual std::string get_error(){return error_string;}  //returns a human readable error string

protected:
	// get a new socket if myfd < 0
	virtual void get_socket()=0;
	
	// create a new class for accept() using socketdescriptor
	virtual SWBaseSocket* create(int socketdescriptor, SWBaseError *error)=0;

	// reset state
	virtual void reset();

	// wait for I/O (with timeout)
	enum io_type{read, write, except, rw, all};
	virtual bool waitIO(io_type &type, SWBaseError *error);
	bool waitRead(SWBaseError *error);
	bool waitWrite(SWBaseError *error);

	// internal error handling
	virtual void handle_errno(SWBaseError *error, std::string msg);
	virtual void no_error(SWBaseError *error);
	virtual void set_error(SWBaseError *error, SWBaseError name, std::string msg);

	// our socket descriptor
	int myfd;
	
	// last error
	std::string error_string;

	// data for fsend
	bool fsend_ready;
	int fsend_total;
	int fsend_bytesleft;
	
	// data for frecv
	bool frecv_ready;
	int frecv_total;
	int frecv_bytesleft;
		
	// have we recived a shutdown signal?
	bool recv_close;

	//blocking mode (set by child classes)
	block_type block_mode;
	
	//timeout for waitIO()
	int tsec;
	int tusec;
};


#endif /* sw_base_H */