#include "csocket.h"

#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>

cTBSocket::cTBSocket(void):
		cTBFileNo() {
	memset(&m_LocalAddr, 0, sizeof(m_LocalAddr));
	memset(&m_RemoteAddr, 0, sizeof(m_RemoteAddr));
}

cTBSocket::~cTBSocket() {
}

bool cTBSocket::Connect(const char *Host, unsigned int Port) {
	int newfiled;
	socklen_t len;

	if (IsOpen())
		Close();
		
	if ((newfiled = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
		return false;

	if (!Open(newfiled))
		return false;

	m_LocalAddr.sin_family = AF_INET;
	m_LocalAddr.sin_port   = 0;
	m_LocalAddr.sin_addr.s_addr = INADDR_ANY;
	if (::bind(FileNo(), (struct sockaddr*)&m_LocalAddr, sizeof(m_LocalAddr)) 
			== -1)
		return false;

	m_RemoteAddr.sin_family = AF_INET;
	m_RemoteAddr.sin_port   = htons(Port);
	m_RemoteAddr.sin_addr.s_addr = inet_addr(Host);
	if (::connect(FileNo(), (struct sockaddr*)&m_RemoteAddr, 
			sizeof(m_RemoteAddr)) == -1) 
		return false;

	len = sizeof(struct sockaddr_in);
	if (::getpeername(FileNo(), (struct sockaddr*)&m_RemoteAddr, &len) == -1) 
		return false;

	if (::fcntl(FileNo(), F_SETFL, O_NONBLOCK) == -1)
		return false;

	return true;
}

bool cTBSocket::Listen(unsigned int Port, int BackLog) {
	int newfiled;
	int val;
	socklen_t len;

	if (IsOpen())
		Close();
	
	if ((newfiled = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
		return false;

	if (!Open(newfiled))
		return false;

	m_LocalAddr.sin_family = AF_INET;
	m_LocalAddr.sin_port   = htons(Port);
	m_LocalAddr.sin_addr.s_addr = INADDR_ANY;
	if (::bind(FileNo(), (struct sockaddr*)&m_LocalAddr, sizeof(m_LocalAddr)) 
			== -1)
		return false;

	len = sizeof(struct sockaddr_in);
	if (::getsockname(FileNo(), (struct sockaddr*)&m_LocalAddr, &len) == -1) 
		return false;
	
	val = 1;
	if (::setsockopt(FileNo(), SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) 
			== -1)
		return false;

	if (::listen(FileNo(), BackLog) == -1)
		return false;

	if (::fcntl(FileNo(), F_SETFL, O_NONBLOCK) == -1)
		return false;

	return true;
}

cTBSocket *cTBSocket::Accept(void) {
	int       newfiled;
	socklen_t addrlen;

	cTBSocket *newsocket;

	addrlen = sizeof(m_RemoteAddr);
	if ((newfiled = ::accept(FileNo(), (struct sockaddr*)&m_RemoteAddr, 
			&addrlen)) == -1)
		return 0L;

	newsocket = new cTBSocket;
	if (!newsocket->Open(newfiled))
		return 0L;

	memcpy(&newsocket->m_RemoteAddr, &m_RemoteAddr, sizeof(m_RemoteAddr));
	
	if (::fcntl(*newsocket, F_SETFL, O_NONBLOCK) == -1)
		return 0L;

	return newsocket;
}

bool cTBSocket::Close(void) {
	::shutdown(FileNo(), SHUT_RDWR);
	memset(&m_LocalAddr, 0, sizeof(m_LocalAddr));
	memset(&m_RemoteAddr, 0, sizeof(m_RemoteAddr));
	return cTBFileNo::Close();
}

bool cTBSocket::Shutdown(int how) {
	if (::shutdown(FileNo(), how) == -1)
		return false;
	return true;
}
