/***************************************************************************
                          casyncconn.h  -  description
                             -------------------
    begin                : Wed Jun 11 2003
    copyright            : (C) 2003 by Daniel Muller
    email                : dan at verliba dot cz
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program 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.                                   *
 *                                                                         *
 ***************************************************************************/

#ifndef CASYNCCONN_H
#define CASYNCCONN_H

// buffer sizes, for more users, use larger buffers
#define MAX_MESS_SIZE 0x2FFFF           // max length of message that we accept
#define MAX_SEND_SIZE 0x0FFFFF         // max size of output buffer
#define MAX_SEND_FILL_SIZE 0x0AFFFF    // on this level, incoming daa is blocked
#define MAX_SEND_UNBLOCK_SIZE 0x07FFFF // under this level it's unblocked again


#include "cobj.h"
#include "ctime.h"
#include "cconnbase.h"

#include <string>

namespace nServer {

//class cSelector;
class cAsyncSocketServer;
class cAsyncConn;

class cConnFactory
{
public:
	virtual ~cConnFactory(){};
	virtual cAsyncConn * CreateConn(tSocket sd=0);
	virtual void DeleteConn(cAsyncConn * &);
};

namespace nEnums
{
/** Type of socket connection
  */
typedef enum tConnType
{
	eCT_LISTEN, //< listening tcp connection
	eCT_CLIENT, //< client connection connected to me( server)
	eCT_SERVER  //< i'm client connected to another sever
};

enum
{
	AC_LS_NO_LINE,
	AC_LS_PARTLY,
	AC_LS_LINE_DONE,
	AC_LS_ERROR
};
};

using namespace ::nServer::nEnums;
using namespace std;
using namespace nUtils;


/**a network connection for asynchronous (rather non-blocking) server
  *@author Daniel Muller
  */

class cAsyncConn : public cConnBase, public cObj
{
public:
	cAsyncConn(int sd=0, cAsyncSocketServer *s=NULL, tConnType ct= eCT_CLIENT);
	cAsyncConn(const string & host, int port);
	virtual ~cAsyncConn();
	/** close connection to peer */
	void Close();
	/** flush as much from output buffer as possible to the iochannel */
	void Flush();
	/** register the connection into given selector */
	//int Register(cSelector&, int);
	//void UnRegister(cSelector&);
	/** socket descriptor */
	tSocket mSockDesc;
private: // Private attributes
	/** string for line */
	string *mxLine;
	/** line status */
	int meLineStatus;
	/** msBuffer End Position */
	int mBufEnd;
	/** msBuffer read Position */
	int mBufReadPos;
	/** registered flags in selector */
	int mRegFlag;
	/** time to close the connection */
	cTime mCloseAfter;
public:
	/** indicate whether server should take care of this connection */
	bool ok;
	/** indicates, if data is allowed to insert into the buffer, you can set it as you wish */
	bool mWritable;
	/** flags externall to poll, but used by poller in a way */
	int mExtraPoll;
	/** read property for meLineStatus */
	int LineStatus(){return meLineStatus;}
	/** you can provide a string for reading a line by this function
		<precond>
		LineStatus() == AC_LS_NO_LINE
		</precond> */
	void SetLineToRead(string *,char , int max=-1);
	/** clears the line status */
	void ClearLine();
	/** return the line's pointer and keep it */
	string * GetLine();
	/** No descriptions */
	void CloseNice(int msec=0);
	/** reads a line from the msBuffer preaviously filled by ReadAll into private members */
	int ReadLineLocal();
public:
	/** external pointer to the server class */
	cAsyncSocketServer * mxServer;
	cConnFactory * mxFactory;
protected: // Protected methods
	/** static buffer as intermediate place to stock data when reading parts of lines */
	static char *msBuffer;
	/** send buffer, what is not sent immediately, is sent later from this buffer */
	string mBufSend;
	/** line spearator */
	char mSeparator;
	/** byte count already in the line */
	int mLineSize;
	/** ip address */
	string mAddrHost;
	string mAddrIP;
	int mAddrPort;
	unsigned long mMaxBuffer;
	/** byte count limit for the line */
	unsigned mLineSizeMax;
	/** the ip address - binary format */
	unsigned long mIp;
	/** connestion type */
	tConnType mType;
	/** send *len bytes from the msBufSend */
	int SendAll(const char *buf, size_t &len);
	/** create a socket */
	tSocket CreateSock();
	/** binds given socket to a port */
	int BindSocket(int sock, int port, const char *addr=NULL);
	/** listen on given socket */
	int ListenSock(int sock);
	/** set socket for non-blocking */
	tSocket NonBlockSock(int sock);
	/** return new socket */
	tSocket AcceptSock();
	/** function called before closing nicely */
	virtual int OnCloseNice(void);
public:
	/** retrun IP address */
	const string & AddrIP(){return mAddrIP;}
	const string & AddrHost(){return mAddrHost;}
	const int AddrPort(){return mAddrPort;}
	/** reads all available data from the socket and stores it into a static member buffer */
	int ReadAll(void);
	/** tells if msBuffer has some more data available or not
	return value: true if not */
	int BufferEmpty(){ return mBufEnd == mBufReadPos;};
	/** No descriptions */
	int ListenOnPort(int port, const char *ia=NULL);
	/** create new connection from the listening port */
	virtual cAsyncConn * Accept();
	/** Read property of tConnType mType. */
	virtual const tConnType& getType();
	/** this is called every period of time */
	virtual int OnTimer(cTime &now);
	/** this function is going to be executed periodicaly every N seconds by the  function of the same name in server */
	int OnTimerBase(cTime &now);
	/** immediately close the connection */
	void CloseNow();
	/** this is called when write buffer gets empty */
	virtual void OnFlushDone();
	/** write given data or store into buffer */
	int Write(const string &data);
	/** register the connection into given selector */
	//void UnRegister(cPoller & );
	/** register the connection into given selector */
	//int Register(cPoller & , int );
	/** No descriptions */
	tConnType GetType(){return mType;};
	/** connect to given host (ip) on port */
	int Connect(const string &, int);
	bool DNSLookup();
	bool DNSResolveReverse(const string &ip, string &host);
	virtual operator tSocket()const {return mSockDesc;}
	cTime mTimeLastIOAction;
protected:
	/** remove 'cut' first bytes from the string and ensure the memory deallocation
		it is very slow method because of double copying of the data
	*/
	void StrCutLeft(string &, int );

	/** remove 'cut' first bytes from the str1 and make a copy in str2
		and ensure the memory deallocation
		it is very slow method because of double copying of the data
	*/
	void StrCutLeft(const string &str1, string &str2, int cut);

};

/*!
    \fn cAsyncConn::StrCutLeft(string &, int cut)
*/

inline void cAsyncConn::StrCutLeft(string &str, int cut)
{
	string tmp;
	if(cut > str.length()) cut = str.length();
	// copy the concerned part to timp
	tmp = str.data()+cut;
	// empty string
	str = mEmpty;
	// copy tmp to string
	str = tmp;
}

inline void cAsyncConn::StrCutLeft(const string &str1, string &str2, int cut)
{
	string tmp;
	if(cut > str1.size()) cut = str1.size();
	// copy the concerned part to tmp
	tmp = str1.data()+cut;
	// empty string
	str2 = mEmpty;
	// copy tmp to string
	str2 = tmp;
}

};

#endif
