/*

この作品は、クリエイティブ・コモンズの帰属ライセンスの下でライセンスされています。
この使用許諾条件を見るには、http://creativecommons.org/licenses/by/2.0/jp/ をチェックするか、
クリエイティブ･コモンズに郵便にてお問い合わせください。
住所は：559 Nathan Abbott Way, Stanford, California 94305, USA です。

by ◆F99a.q8oVE http://f99aq.s57.xrea.com
*/

#ifndef CA83BFD9_BEE7_41E2_9170_E44F00E33221_SOCKET_H_INCLUDED
#define CA83BFD9_BEE7_41E2_9170_E44F00E33221_SOCKET_H_INCLUDED

#include <string>
#include <list>
#include <ctime>
#include <boost/shared_ptr.hpp>

#include <winsock2.h>

class TCPSocket {
public:
	explicit TCPSocket(SOCKET socket = INVALID_SOCKET)
		: socket_((socket != INVALID_SOCKET) ? socket : ::socket(AF_INET, SOCK_STREAM, 0)) {}
	virtual ~TCPSocket() { close(); }
	bool valid() const { return socket_ != INVALID_SOCKET; }	// SOCKETが正しいかを返す
	void nonblocking() const;	// None Blocking Modeへ
	bool connect(unsigned long naddr, unsigned short port) const;
	bool connect(const char * const host, unsigned short port) const;
	bool listen(unsigned short port, unsigned long bindaddr) const;
	bool getpeername(sockaddr * const name) const;
	bool readable(long sec, long usec) const;	// sec.usecブロックし受信可能かを返す
	SOCKET accept(sockaddr * const addr = NULL) const;
	virtual int recv(char * const buf, int len) const;
	virtual int send(const char * const buf, int len) const;
	SOCKET get() const { return socket_; }
	SOCKET release() { SOCKET tmp = socket_; socket_ = INVALID_SOCKET; return tmp; }

private:
	void close();

	TCPSocket(const TCPSocket&);	// このクラスはコピー禁止
	const TCPSocket& operator=(const TCPSocket&);

	SOCKET socket_;

	//friend class TCPServer;
};

class TimedTCPSocket : public TCPSocket {
public:
	explicit TimedTCPSocket(SOCKET socket = INVALID_SOCKET) : TCPSocket(socket)
	{ time(&lastrecved); time(&lastsent); }
	virtual int recv(char * const buf, int len) const
	{ time(&lastrecved); return TCPSocket::recv(buf, len); }
	virtual int send(const char * const buf, int len) const
	{ time(&lastsent); return TCPSocket::send(buf, len); }
	bool recvTimeout(int timeout) const;
	bool sendTimeout(int timeout) const;

private:
	mutable time_t lastrecved, lastsent;
};

template <typename Sock>
class TCPSocketBuffer {
public:
	explicit TCPSocketBuffer(SOCKET sock = INVALID_SOCKET) : sock_(new Sock(sock)) {}
	//const Sock& operator*() { return sock_; }
	//Sock& operator*() { return sock_; }
	bool valid() const { return sock_->valid(); }
	void nonblocking() const { return sock_->nonblocking(); }
	bool connect(unsigned long naddr, unsigned short port) const
	{ return sock_->connect(naddr, port); }
	bool connect(const char * const host, unsigned short port) const
	{ return sock_->connect(host, port); }
	bool listen(unsigned short port, unsigned long bindaddr) const
	{ return sock_->listen(port, bindaddr); }
	bool getpeername(sockaddr * const name) const
	{ return sock_->getpeername(name); }
	bool readable(long sec, long usec) const
	{ return sock_->readable(sec, usec); }
	SOCKET accept(sockaddr * const addr = NULL) const
	{ return sock_->accept(addr); }
	int send(const char * const buf, int len) const
	{ return sock_->send(buf, len); }
	int recv();
	SOCKET get() const { return sock_->get(); }
	SOCKET release() { return sock_->release(); }
	std::string& getBuffer() { return buffer; }
	Sock& getSocket() { return *sock_; }

private:
	static const int bufferSize = 8 * 1024;
	boost::shared_ptr<Sock> sock_;
	std::string buffer;
};

template <typename Sock>
int TCPSocketBuffer<Sock>::recv()
{
	char buf[bufferSize];
	int res = sock_->recv(buf, sizeof buf);
	if (res > 0) {
		buffer.append(buf, res);
	}
	return res;
}

/*!
	@brief TCPServer Class
*/
template <typename SockBuf, typename Processor>
class TCPServer {
public:
	explicit TCPServer(Processor& processor, unsigned maxConnection)
		: processor_(processor), maxConnection_(maxConnection) { FD_ZERO(&org_fd); processor_.setServer(this); }
	bool start(unsigned short listenport, unsigned long bindaddr = INADDR_ANY);
	void process();
	void push(SOCKET s);
	typename std::list<SockBuf>::iterator pop(typename std::list<SockBuf>::iterator& it);
	size_t numofConnection() const { return sockets.size(); }
	Processor& getProcessor() { return processor_; }

private:
	int select(long sec, long usec);
	bool fd_isset(SOCKET s) const { return FD_ISSET(s, &fd) != 0; }
	bool accept(sockaddr* addr);

	std::list<SockBuf> sockets;
	const TCPSocket listen;
	fd_set org_fd, fd;
	Processor& processor_;
	const unsigned maxConnection_;
};

template <typename SockBuf, typename Processor>
bool TCPServer<SockBuf, Processor>::start(unsigned short listenport, unsigned long bindaddr)
{
	if (listen.listen(listenport, bindaddr)) {
#pragma warning(push, 3)
		FD_SET(listen.get(), &org_fd);
#pragma warning(pop)

		processor_.startProcessor();
		return true;
	} else {
		return false;
	}
}

template <typename SockBuf, typename Processor>
int TCPServer<SockBuf, Processor>::select(long sec, long usec)
{
	timeval waitval = {sec, usec};
	fd = org_fd;
	return ::select(FD_SETSIZE, &fd, NULL, NULL, &waitval);
}

template <typename SockBuf, typename Processor>
bool TCPServer<SockBuf, Processor>::accept(sockaddr* addr)
{
	SOCKET new_socket = listen.accept(addr);
	if (new_socket != INVALID_SOCKET) {
		push(new_socket);
		return true;
	} else {
		return false;
	}
}

template <typename SockBuf, typename Processor>
void TCPServer<SockBuf, Processor>::push(SOCKET s)
{
#pragma warning(push, 3)
	FD_SET(s, &org_fd);
#pragma warning(pop)
	sockets.push_back(SockBuf(s));
}

template <typename SockBuf, typename Processor>
typename std::list<SockBuf>::iterator
TCPServer<SockBuf, Processor>::pop(typename std::list<SockBuf>::iterator& it)
{
#pragma warning(push, 3)
	FD_CLR(it->get(), &org_fd);
#pragma warning(pop)
	return sockets.erase(it);
}

template <typename SockBuf, typename Processor>
void TCPServer<SockBuf, Processor>::process()
{
	if (select(1, 0) > 0) {
		if (sockets.size() <= maxConnection_ && fd_isset(listen.get())) {
			if (accept(NULL)) {
				processor_.acceptDetected();
			}
		}

		for (std::list<SockBuf>::iterator it(sockets.begin());
			it != sockets.end();
			)
		{
			SockBuf& sockbuf(*it);
			if (!fd_isset(sockbuf.get())) {
				++it;
				continue;
			}

			if (sockbuf.recv() > 0) {	// 受信
				if (!processor_.recvDetected(sockbuf)) {
					it = pop(it);
					processor_.disconnected();
				} else {
					++it;
				}
			} else {
				it = pop(it);
				processor_.disconnected();
			}
		}
	}

	processor_.dosomething(sockets);
}

#endif /* CA83BFD9_BEE7_41E2_9170_E44F00E33221_SOCKET_H_INCLUDED */
