/*
 *  Copyright (C) 2007-2008 CMM Sigma Andrzej Chmielowiec 
<cmmsigma@cmmsigma.eu>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2
 *  as published by the Free Software Foundation.
 *
 *  This program 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 General Public License for more details.
 */

// QtTelnet.cpp: implementation of the QtTelnet class.
//
//////////////////////////////////////////////////////////////////////

#include "QtTelnet.h"

QtTelnet::QtTelnet(QObject *parent) : QObject(parent)
{
	mTcpSocket = NULL;
	createSocket();
}

QtTelnet::~QtTelnet()
{
	delete mTcpSocket;
}


//////////////////////////////////////////////////////////////////////
// public methods
//////////////////////////////////////////////////////////////////////

void QtTelnet::connectToHost(const QString &host, quint16 port)
{
	if(NULL == mTcpSocket)
	{
		createSocket();
	}
	else
	{
		mTcpSocket->abort();
	}

	mTcpSocket->connectToHost(host, port);
}


void QtTelnet::login(const QString &username, const QString &password)
{

}


QTcpSocket *QtTelnet::socket() const
{
	return mTcpSocket;
}


void QtTelnet::close()
{
	mTcpSocket->close();
}


void QtTelnet::logout()
{
	// logout code
	emit loggedOut();
}


void QtTelnet::sendData(QString data)
{
	//tr::trace("* socket send: %s", qPrintable(data));

	data += "\r\n";
	mTcpSocket->write(data.toUtf8());
}

void QtTelnet::sendData(const QStringList &lines)
{
	foreach(QString line, lines)
	{
		//tr::trace("* socket send line: %s", qPrintable(line));

		line += "\r\n";
		mTcpSocket->write(line.toUtf8());
	}
}

//////////////////////////////////////////////////////////////////////
// socket slots
//////////////////////////////////////////////////////////////////////

void QtTelnet::socketConnected()
{
	emit connected();
}

void QtTelnet::socketReadyRead()
{
	if(false == mTcpSocket->canReadLine())
		return;

	QStringList lines;
	QString line;
	do
	{
		line = mTcpSocket->readLine();

		//tr::trace("* socket received line: %s", qPrintable(line));

		lines.append(line);
	} while(mTcpSocket->canReadLine());

	emit message(lines);
/*
	QByteArray buffer = mTcpSocket->readAll();

	QString data = QString(buffer);
	//tr::trace("* socket received: %s", qPrintable(data));
	emit message(data);
*/

/*	QStringList lines = data.split("\r\n", QString::SkipEmptyParts);

	foreach(QString line, lines)
	{
		mResponse.clear();

		processOptions(line);
		respondToOptions();

		if(false == mResponse.isEmpty())
		{
			sendData(mResponse);
		}
	}

	emit message(mNormalText);*/
}

void QtTelnet::socketError(QAbstractSocket::SocketError error)
{
	QString err;

	switch(error)
	{
		case QAbstractSocket::ConnectionRefusedError: err = tr("The connection was refused by the peer (or timed out)."); break;
		case QAbstractSocket::RemoteHostClosedError: err = tr("The remote host closed the connection. Note that the client socket (i.e., this socket) will be closed after the remote close notification has been sent."); break;
		case QAbstractSocket::HostNotFoundError: err = tr("The host address was not found."); break;
		case QAbstractSocket::SocketAccessError: err = tr("The socket operation failed because the application lacked the required privileges."); break;
		case QAbstractSocket::SocketResourceError: err = tr("The local system ran out of resources (e.g., too many sockets)."); break;
		case QAbstractSocket::SocketTimeoutError: err = tr("The socket operation timed out."); break;
		case QAbstractSocket::DatagramTooLargeError: err = tr("The datagram was larger than the operating system's limit (which can be as low as 8192 bytes)."); break;
		case QAbstractSocket::NetworkError: err = tr("An error occurred with the network (e.g., the network cable was accidentally plugged out)."); break;
		case QAbstractSocket::AddressInUseError: err = tr("The address specified to QUdpSocket::bind() is already in use and was set to be exclusive."); break;
		case QAbstractSocket::SocketAddressNotAvailableError: err = tr("The address specified to QUdpSocket::bind() does not belong to the host."); break;
		case QAbstractSocket::UnsupportedSocketOperationError: err = tr("The requested socket operation is not supported by the local operating system (e.g., lack of IPv6 support)."); break;
		case QAbstractSocket::ProxyAuthenticationRequiredError: err = tr("The socket is using a proxy, and the proxy requires authentication."); break;
		default: err = tr("An unidentified error occurred."); break;
	}
	
	mTcpSocket->abort();

	emit connectionError(err);
}

//////////////////////////////////////////////////////////////////////
// protected methods
//////////////////////////////////////////////////////////////////////

void QtTelnet::createSocket()
{
	if(NULL != mTcpSocket)
	{
		mTcpSocket->abort();
		delete mTcpSocket;
	}
	
	mTcpSocket = new QTcpSocket(this);
	connect(mTcpSocket, SIGNAL(readyRead()), this, SLOT(socketReadyRead()));
	connect(mTcpSocket, SIGNAL(connected()), this, SLOT(socketConnected()));
	connect(mTcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError)));
}


void QtTelnet::deleteSocket()
{
	if(NULL != mTcpSocket)
	{
		mTcpSocket->abort();
		delete mTcpSocket;
	}
}


void QtTelnet::processOptions(QString line)
{
	int ndx, ldx;
	QChar ch;
	bool scanDone = false;
	QString option;

	while(!line.isEmpty() && !scanDone)
	{
		ndx = line.indexOf(QChar(IAC));
		if(ndx != -1)
		{
			mNormalText += line.left(ndx);
			ch = line[ndx + 1];
			switch((int)ch.toAscii())
			{
				case DO:
				case DONT:
				case WILL:
				case WONT:
					option = line.mid(ndx, 3);
					line = line.mid(ndx + 3);
					mNormalText = line.left(ndx);
					mListOptions.append(option);
					break;
				case IAC:
					mNormalText = line.left(ndx);
					line = line.mid(ndx + 1);
					break;
				case SB:
					mNormalText = line.left(ndx);
					ldx = line.indexOf(QChar(SE));
					option = line.mid(ndx, ldx);
					mListOptions.append(option);
					line = line.mid(ldx);
					break;
			}
		}

		else
		{
			mNormalText = line;
			scanDone = true;
		}
	}
}


void QtTelnet::respondToOptions()
{
	QString option;
	
	while(!mListOptions.isEmpty())
	{
		option = mListOptions[0];

		mListOptions.removeFirst();

		arrangeReply(option);
	}
}


void QtTelnet::arrangeReply(QString option)
{
	QChar modifier, ch;
	bool defined = false;

	if(option.size() < 3)
		return;

	QChar verb = option[1];
	QChar value = option[2];

	switch((int)value.toAscii())
	{
		case 1:	// Echo
		case 3: // Suppress Go-Ahead
			defined = true;
			break;
	}

	mResponse += QChar(IAC);

	if(defined == true)
	{
		switch((int)verb.toAscii())
		{
			case DO:
				ch = QChar(WILL);
				mResponse += ch;
				mResponse += value;
				break;
			case DONT:
				ch = QChar(WONT);
				mResponse += ch;
				mResponse += value;
				break;
			case WILL:
				ch = QChar(DO);
				mResponse += ch;
				mResponse += value;
				break;
			case WONT:
				ch = QChar(DONT);
				mResponse += ch;
				mResponse += value;
				break;
			case SB:
				modifier = option[3];
				if(modifier == QChar(TELQUAL_SEND))
				{
					ch = QChar(SB);
					mResponse += ch;
					mResponse += option;
					mResponse += QChar(TELQUAL_IS);
					mResponse += QChar(IAC);
					mResponse += QChar(SE);
				}
				break;
		}
	}
	else
	{
		switch((int)value.toAscii())
		{
			case DO:
				ch = QChar(WONT);
				mResponse += ch;
				mResponse += value;
				break;
			case DONT:
				ch = QChar(WONT);
				mResponse += ch;
				mResponse += value;
				break;
			case WILL:
				ch = QChar(DONT);
				mResponse += ch;
				mResponse += value;
				break;
			case WONT:
				ch = QChar(DONT);
				mResponse += ch;
				mResponse += value;
				break;
		}
	}
}
