/**
 *  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.
 *
 *  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.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include "commthread.h"
#include "messages.h"

//----------------------------------------------------------------------------------

CommThread::CommThread( const os::Messenger &cTarget ):os::Looper( "CommThread" )
{
	m_cTarget = cTarget;
	m_eState = S_STOP;
	sockfd = -1;
}

void CommThread::SendReceiveLoop( void )
{
	if( !SendReceiveLoop( m_eState ) )
		sleep( 1 );
	SendReceiveLoop();
}

/** CREDIT: BeOS DirKeeper::Idle(), see directoryview.cpp in libatheos
 *
 * return true if bgnet.Select() is ready for reading and
 * bgnet.ReadLine() has bytes actually read into the buffer
 */
bool CommThread::SendReceiveLoop( state_t eState )
{
	m_eState = eState;

	switch ( m_eState )
	{
		case S_START:
		{
			if( bgnet.Select( sockfd ) == 0 ) {
				printf( "(Read) Timeout occured! No data after 2.5 seconds. \n" );
				return ( false );
			}

			static char buf[PIPE_BUF];
			if( bgnet.Receive( sockfd, buf, PIPE_BUF ) > 0 ) {
				os::String cName( buf );
				if( !cName.empty() && !PingPong( cName ) )
					SendMessage( cName );
				m_eState = S_START;
				return ( true );
			} else {
				m_eState = S_STOP;
				return( false );
			}
			break;
		}
		case S_STOP:
			return ( false );
	}

	return ( false );
}

void CommThread::HandleMessage( os::Message *pcMessage )
{
	switch ( pcMessage->GetCode() )
	{
		case MSG_TOLOOPER_START:
		{
			// skip bgnet.Connect() if already connected
			if( m_eState == S_START )
				break;

			if( (sockfd = bgnet.Connect( GetHost().c_str(), atoi( GetPort().c_str() ) )) > 0 )
			{
				// authenticate to the server
				os::String cTempString = os::String().Format( "NICK %s\r\n", GetNickname().c_str() );
				bgnet.SendAll( sockfd, cTempString.c_str(), cTempString.Length() );
				cTempString.Format( "USER %s 0 * :%s\r\n", GetUsername().c_str(), GetRealname().c_str() );
				bgnet.SendAll( sockfd, cTempString.c_str(), cTempString.Length() );

				// start SendReceiveLoop() looper
				m_eState = S_START;
				SendReceiveLoop();
			}
			break;
		}
		case MSG_TOLOOPER_STOP:
		{
			// block bgnet.Disconnect() if not connected
			if( m_eState == S_START )
			{
				bgnet.Disconnect( sockfd );
				m_eState = S_STOP;
			}
			break;
		}
		default:
		{
			os::Looper::HandleMessage( pcMessage );
			break;
		}
	}
}

// skip bgnet.Send() if not connected
void CommThread::Send( const os::String cSend )
{
	if( m_eState == S_STOP )
		return;

	bgnet.SendAll( sockfd, cSend.c_str(), cSend.Length() );
}

// return true if ping command found
bool CommThread::PingPong( const os::String cData )
{
	std::string str( cData );

	if( str.at( 0 ) != ':' )
	{
		if( str.find( "PING", 0 ) != std::string::npos )
		{
			std::cout << "Ping? Pong!" << std::endl;
			Send( os::String().Format( "PONG%s", str.substr( 4 ).c_str() ) );
			return ( true );
		}
	}

	return ( false );
}

// send server data to the MainView textview
void CommThread::SendMessage( const os::String& cName )
{
	try
	{
		os::Message cMsg( MSG_FROMLOOPER_NEW_MESSAGE );
		cMsg.AddString( "name", cName );
		m_cTarget.SendMessage( &cMsg, m_cTarget );
	}
	catch( ... ) { }
}

// return true if connected to a server
bool CommThread::IsConnected( void )
{
	return (m_eState == S_STOP) ? false:true;
}