#include <assert.h>
#include <qsocket.h>
#include <qtimer.h>
#include <klocale.h>
#include <string.h>

#include "krfbconnection.h"
#include "krfblogin.h"
#include "krfboptions.h"
#include "krfbdecoder.h"
#include "krfbbuffer.h"
#include "krfbconnection.moc"

KRFBConnection::KRFBConnection( QObject *parent )
    : QObject( parent, "KRFBConnection" )
{
  portBase_ = 5900;
  currentState_ = Disconnected;
  sock = 0;
  minData_ = 0;
  options_ = new KRFBOptions();
  updater = 0;
}

KRFBConnection::~KRFBConnection()
{
  if ( ( currentState_ != Disconnected )
       && ( currentState_ != Disconnecting )
       && sock )
    sock->close();

  delete options_;
}

void KRFBConnection::connectTo( const QCString &host, int display )
{
  assert( currentState_ == Disconnected );

  this->host_= host;
  this->display_ = display;

  sock = new QSocket( this, "rfbSocket" );
  CHECK_PTR( sock );

  // Connect to something to notice connection or error
  connect( sock, SIGNAL( error( int ) ), SLOT( gotSocketError( int ) ) );
  connect( sock, SIGNAL( connected() ), SLOT( gotSocketConnection() ) );

  warning( "Connecting..." );

  currentState_ = Connecting;
  sock->connectToHost( host_, portBase_ + display_ );
}

void KRFBConnection::disconnect()
{
    warning( "Disconnecting from server" );

  if ( ( currentState_ != Disconnected )
       && ( currentState_ != Disconnecting )
       && sock ) {
    currentState_ = Disconnecting;

    connect( sock, SIGNAL( delayedCloseFinished() ), SLOT( disconnected() ) );
    sock->close();

    if ( sock->state() != QSocket::Closing )
      disconnected();
  }
}

void KRFBConnection::disconnected()
{
  currentState_ = Disconnected;
  delete sock;
  sock = 0;

  warning( "KRFBConnection disconnected" );

  delete this;
}

void KRFBConnection::gotSocketConnection()
{
  currentState_ = LoggingIn;

  warning( "Connected, logging in..." );

  static QString statusMsg = i18n( "Connected" );
  emit statusChanged( statusMsg );

  // Do some login stuff
  login = new KRFBLogin( this );
  CHECK_PTR( login );
}

void KRFBConnection::gotRFBConnection()
{
  currentState_ = Connected;
  
  warning( "Logged into server" );

  static QString statusMsg = i18n( "Logged in" );
  emit statusChanged( statusMsg );

  // Create the decoder and start doing stuff
  decoder = new KRFBDecoder( this );
  CHECK_PTR( decoder );

  buffer_ = new KRFBBuffer( decoder, this, "RFB Buffer" );
  CHECK_PTR( buffer_ );
  decoder->setBuffer( buffer_ );

  connect( decoder, SIGNAL( status( const QString & ) ),
           this, SIGNAL( statusChanged( const QString & ) ) );
  emit loggedIn();

  decoder->start();

  updater = new QTimer;
  connect( updater, SIGNAL( timeout() ), SLOT( updateTimer() ) );
  updater->start( 250 );
}

void KRFBConnection::gotSocketError( int errno )
{
  currentState_ = Error;

  // Do some error handling stuff
  warning( "KRFBConnection: Socket error %d", errno );

  static QString refused = i18n( "Connection Refused" );
  static QString host = i18n( "Host not found" );
  static QString read = i18n( "Read Error: QSocket reported an error reading\n"
			      "data, the remote host has probably dropped the\n"
			      "connection." );
  static QString confused = i18n( "QSocket reported an invalid error code" );

  QString msg;
  switch ( errno ) {
  case QSocket::ErrConnectionRefused:
    msg = refused;
    break;
  case QSocket::ErrHostNotFound:
    msg = host;
    break;
  case QSocket::ErrSocketRead:
    msg = read;
    break;
  default:
    msg = confused;
  };

  emit error( msg );
}

void KRFBConnection::gotMoreData()
{
  assert( minData_ > 0 );

  if ( sock->size() >= minData_ ) {
    minData_ = 0;
    QObject::disconnect( sock, SIGNAL( readyRead() ), this, SLOT( gotMoreData() ) );
    emit gotEnoughData();
  }
}

void KRFBConnection::waitForData( unsigned int sz )
{
  assert( minData_ == 0 );
  assert( sz > 0 );
  assert( currentState_ != Error );
   
  if ( sock->size() >= sz ) {
      //    warning( "No need to wait for data" );
    emit gotEnoughData();
  }
  else {
      //    warning( "Waiting for %u bytes", sz );
    
    minData_ = sz;
    connect( sock, SIGNAL( readyRead() ), SLOT( gotMoreData() ) );
  }
}

int KRFBConnection::read( void *buf, int sz )
{
  return sock->readBlock( (char *)  buf, sz );
}

int KRFBConnection::write( void *buf, int sz )
{
  return sock->writeBlock( (const char *) buf, sz );
}

KRFBConnection::State KRFBConnection::state() const
{
  return currentState_;
}

void KRFBConnection::setPortBase( int base )
{
  portBase_ = base;
}

int KRFBConnection::portBase() const
{
  return portBase_;
}

void KRFBConnection::setPassword( const QCString &pass )
{
  this->pass_ = pass;
}

void KRFBConnection::updateTimer()
{
  decoder->sendUpdateRequest( true );
}

void KRFBConnection::refresh()
{
  decoder->sendUpdateRequest( false );
}
