/*
 * bsocket.cpp - TQSocket wrapper based on Bytestream with SRV DNS support
 * Copyright (C) 2003  Justin Karneges
 *
 * 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.
 *
 * 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#include "bsocket.h"

#include <tqcstring.h>
#include <tqsocket.h>
#include <tqdns.h>
#include <tqguardedptr.h>
#include "safedelete.h"
#ifndef NO_NDNS
#include "ndns.h"
#endif
#include "srvresolver.h"

#ifdef BS_DEBUG
#include <stdio.h>
#endif

#define READBUFSIZE 65536

// CS_NAMESPACE_BEGIN

class BSocket::Private
{
public:
	Private()
	{
		qsock = 0;
	}

	TQSocket *qsock;
	int state;

#ifndef NO_NDNS
	NDns ndns;
#endif
	SrvResolver srv;
	TQString host;
	int port;
	SafeDelete sd;
};

BSocket::BSocket(TQObject *parent)
:ByteStream(parent)
{
	d = new Private;
#ifndef NO_NDNS
	connect(&d->ndns, TQ_SIGNAL(resultsReady()), TQ_SLOT(ndns_done()));
#endif
	connect(&d->srv, TQ_SIGNAL(resultsReady()), TQ_SLOT(srv_done()));

	reset();
}

BSocket::~BSocket()
{
	reset(true);
	delete d;
}

void BSocket::reset(bool clear)
{
	if(d->qsock) {
		d->qsock->disconnect(this);

		if(!clear && d->qsock->isOpen()) {
			// move remaining into the local queue
			TQByteArray block(d->qsock->bytesAvailable());
			d->qsock->readBlock(block.data(), block.size());
			appendRead(block);
		}

		d->sd.deleteLater(d->qsock);
		d->qsock = 0;
	}
	else {
		if(clear)
			clearReadBuffer();
	}

	if(d->srv.isBusy())
		d->srv.stop();
#ifndef NO_NDNS
	if(d->ndns.isBusy())
		d->ndns.stop();
#endif
	d->state = Idle;
}

void BSocket::ensureSocket()
{
	if(!d->qsock) {
		d->qsock = new TQSocket;
		d->qsock->setReadBufferSize(READBUFSIZE);
		connect(d->qsock, TQ_SIGNAL(hostFound()), TQ_SLOT(qs_hostFound()));
		connect(d->qsock, TQ_SIGNAL(connected()), TQ_SLOT(qs_connected()));
		connect(d->qsock, TQ_SIGNAL(connectionClosed()), TQ_SLOT(qs_connectionClosed()));
		connect(d->qsock, TQ_SIGNAL(delayedCloseFinished()), TQ_SLOT(qs_delayedCloseFinished()));
		connect(d->qsock, TQ_SIGNAL(readyRead()), TQ_SLOT(qs_readyRead()));
		connect(d->qsock, TQ_SIGNAL(bytesWritten(int)), TQ_SLOT(qs_bytesWritten(int)));
		connect(d->qsock, TQ_SIGNAL(error(int)), TQ_SLOT(qs_error(int)));
	}
}

void BSocket::connectToHost(const TQString &host, TQ_UINT16 port)
{
	reset(true);
	d->host = host;
	d->port = port;
#ifdef NO_NDNS
	d->state = Connecting;
	do_connect();
#else
	d->state = HostLookup;
	d->ndns.resolve(d->host);
#endif
}

void BSocket::connectToServer(const TQString &srv, const TQString &type)
{
	reset(true);
	d->state = HostLookup;
	d->srv.resolve(srv, type, "tcp");
}

int BSocket::socket() const
{
	if(d->qsock)
		return d->qsock->socket();
	else
		return -1;
}

void BSocket::setSocket(int s)
{
	reset(true);
	ensureSocket();
	d->state = Connected;
	d->qsock->setSocket(s);
}

int BSocket::state() const
{
	return d->state;
}

bool BSocket::isOpen() const
{
	if(d->state == Connected)
		return true;
	else
		return false;
}

void BSocket::close()
{
	if(d->state == Idle)
		return;

	if(d->qsock) {
		d->qsock->close();
		d->state = Closing;
		if(d->qsock->bytesToWrite() == 0)
			reset();
	}
	else {
		reset();
	}
}

void BSocket::write(const TQByteArray &a)
{
	if(d->state != Connected)
		return;
#ifdef BS_DEBUG
	TQCString cs;
	cs.resize(a.size()+1);
	memcpy(cs.data(), a.data(), a.size());
	TQString s = TQString::fromUtf8(cs);
	fprintf(stderr, "BSocket: writing [%d]: {%s}\n", a.size(), cs.data());
#endif
	d->qsock->writeBlock(a.data(), a.size());
}

TQByteArray BSocket::read(int bytes)
{
	TQByteArray block;
	if(d->qsock) {
		int max = bytesAvailable();
		if(bytes <= 0 || bytes > max)
			bytes = max;
		block.resize(bytes);
		d->qsock->readBlock(block.data(), block.size());
	}
	else
		block = ByteStream::read(bytes);

#ifdef BS_DEBUG
	TQCString cs;
	cs.resize(block.size()+1);
	memcpy(cs.data(), block.data(), block.size());
	TQString s = TQString::fromUtf8(cs);
	fprintf(stderr, "BSocket: read [%d]: {%s}\n", block.size(), s.latin1());
#endif
	return block;
}

int BSocket::bytesAvailable() const
{
	if(d->qsock)
		return d->qsock->bytesAvailable();
	else
		return ByteStream::bytesAvailable();
}

int BSocket::bytesToWrite() const
{
	if(!d->qsock)
		return 0;
	return d->qsock->bytesToWrite();
}

TQHostAddress BSocket::address() const
{
	if(d->qsock)
		return d->qsock->address();
	else
		return TQHostAddress();
}

TQ_UINT16 BSocket::port() const
{
	if(d->qsock)
		return d->qsock->port();
	else
		return 0;
}

TQHostAddress BSocket::peerAddress() const
{
	if(d->qsock)
		return d->qsock->peerAddress();
	else
		return TQHostAddress();
}

TQ_UINT16 BSocket::peerPort() const
{
	if(d->qsock)
		return d->qsock->port();
	else
		return 0;
}

void BSocket::srv_done()
{
	if(d->srv.failed()) {
#ifdef BS_DEBUG
		fprintf(stderr, "BSocket: Error resolving hostname.\n");
#endif
		error(ErrHostNotFound);
		return;
	}

	d->host = d->srv.resultAddress().toString();
	d->port = d->srv.resultPort();
	do_connect();
	//TQTimer::singleShot(0, this, TQ_SLOT(do_connect()));
	//hostFound();
}

void BSocket::ndns_done()
{
#ifndef NO_NDNS
	if(d->ndns.result()) {
		d->host = d->ndns.resultString();
		d->state = Connecting;
		do_connect();
		//TQTimer::singleShot(0, this, TQ_SLOT(do_connect()));
		//hostFound();
	}
	else {
#ifdef BS_DEBUG
		fprintf(stderr, "BSocket: Error resolving hostname.\n");
#endif
		error(ErrHostNotFound);
	}
#endif
}

void BSocket::do_connect()
{
#ifdef BS_DEBUG
	fprintf(stderr, "BSocket: Connecting to %s:%d\n", d->host.latin1(), d->port);
#endif
	ensureSocket();
	d->qsock->connectToHost(d->host, d->port);
}

void BSocket::qs_hostFound()
{
	//SafeDeleteLock s(&d->sd);
}

void BSocket::qs_connected()
{
	d->state = Connected;
#ifdef BS_DEBUG
	fprintf(stderr, "BSocket: Connected.\n");
#endif
	SafeDeleteLock s(&d->sd);
	connected();
}

void BSocket::qs_connectionClosed()
{
#ifdef BS_DEBUG
	fprintf(stderr, "BSocket: Connection Closed.\n");
#endif
	SafeDeleteLock s(&d->sd);
	reset();
	connectionClosed();
}

void BSocket::qs_delayedCloseFinished()
{
#ifdef BS_DEBUG
	fprintf(stderr, "BSocket: Delayed Close Finished.\n");
#endif
	SafeDeleteLock s(&d->sd);
	reset();
	delayedCloseFinished();
}

void BSocket::qs_readyRead()
{
	SafeDeleteLock s(&d->sd);
	readyRead();
}

void BSocket::qs_bytesWritten(int x)
{
#ifdef BS_DEBUG
	fprintf(stderr, "BSocket: BytesWritten [%d].\n", x);
#endif
	SafeDeleteLock s(&d->sd);
	bytesWritten(x);
}

void BSocket::qs_error(int x)
{
#ifdef BS_DEBUG
	fprintf(stderr, "BSocket: Error.\n");
#endif
	SafeDeleteLock s(&d->sd);

	// connection error during SRV host connect?  try next
	if(d->state == HostLookup && (x == TQSocket::ErrConnectionRefused || x == TQSocket::ErrHostNotFound)) {
		d->srv.next();
		return;
	}

	reset();
	if(x == TQSocket::ErrConnectionRefused)
		error(ErrConnectionRefused);
	else if(x == TQSocket::ErrHostNotFound)
		error(ErrHostNotFound);
	else if(x == TQSocket::ErrSocketRead)
		error(ErrRead);
}

// CS_NAMESPACE_END

#include "bsocket.moc"
