#include <assert.h>
#include <qpixmap.h>
#include <qsocket.h>
#include <kapp.h>
#include <klocale.h>
#include <qevent.h>
#include <qstring.h>
#include "krfbconnection.h"
#include "krfboptions.h"
#include "krfbserverinfo.h"
#include "krfbdecoder.h"
#include "krfbbuffer.h"
#include "krfbdecoder.moc"
#include <X11/keysym.h>

//
// Endian stuff
//
const int endianTest = 1;

#define Swap16IfLE(s) \
    (*(char *)&endianTest ? ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff)) : (s))

#define Swap32IfLE(l) \
    (*(char *)&endianTest ? ((((l) & 0xff000000) >> 24) | \
			     (((l) & 0x00ff0000) >> 8)  | \
			     (((l) & 0x0000ff00) << 8)  | \
			     (((l) & 0x000000ff) << 24))  : (l))

//
// The lengths of the messages we need to wait for
//
const int ServerInitLength = 24;
const int UpdateHeaderLength = 4;
const int RectHeaderLength = 12;
const int RectChunkSize = 4;
const int CopyRectPosLength = 4;
const int ServerCutLenLength = 7;

//
// Client -> Server Message Identifiers
//
static CARD8 SetPixelFormatId = 0;
//static CARD8 FixColourMapEntriesId = 1; // Not used
static CARD8 SetEncodingsId = 2;
static CARD8 UpdateRequestId = 3;
static CARD8 KeyEventId = 4;
static CARD8 PointerEventId = 5;

//
// Server -> Client Message Identifiers
//
static CARD8 UpdateId = 0;
static CARD8 BellId = 2;
static CARD8 ServerCutId = 3;

//
// Encoding identifiers
//
static CARD32 RawEncoding = Swap32IfLE( 0 );
static CARD32 CopyRectEncoding = Swap32IfLE(1 );
static CARD32 RreEncoding = Swap32IfLE( 2 );
static CARD32 CorreEncoding = Swap32IfLE( 4 );
static CARD32 HexTileEncoding = Swap32IfLE( 5 );


KRFBDecoder::KRFBDecoder( KRFBConnection *con )
  : QObject( con, "RFB Decoder" )
{
  assert( con );
  assert( con->state() == KRFBConnection::Connected );

  this->con = con;
  this->buf = 0;
  this->info = 0;
  this->format = 0;
  this->buttonMask = 0;
  currentState = Idle;
}

KRFBDecoder::~KRFBDecoder()
{
  if ( info )
    delete info;
  if ( format )
    delete format;
}

void KRFBDecoder::start()
{
  sendClientInit();
}

void KRFBDecoder::sendClientInit()
{
  con->write( &( con->options()->shared ), 1 );

  // Wait for server init
  warning( "Waiting for server init" );

  static QString statusMsg = i18n( "Waiting for server initialisation..." );
  emit status( statusMsg );

  currentState = AwaitingServerInit;
  connect( con, SIGNAL( gotEnoughData() ), SLOT( gotServerInit() ) );
  con->waitForData( ServerInitLength );
}

void KRFBDecoder::gotServerInit()
{
  warning( "Got server init" );
  disconnect( con, SIGNAL( gotEnoughData() ), this, SLOT( gotServerInit() ) );

  if ( info )
    delete info;
  info = new KRFBServerInfo;
  CHECK_PTR( info );

  con->read( &(info->width), 2 );
  info->width = Swap16IfLE( info->width );
  con->read( &info->height, 2 );
  info->height = Swap16IfLE( info->height );

  con->read( &(info->bpp), 1 );
  con->read( &(info->depth), 1 );
  con->read( &(info->bigEndian), 1 );
  con->read( &(info->trueColor), 1 );

  con->read( &(info->redMax), 2 );
  info->redMax = Swap16IfLE( info->redMax );
  con->read( &(info->greenMax), 2 );
  info->greenMax = Swap16IfLE( info->greenMax );
  con->read( &(info->blueMax), 2 );
  info->blueMax = Swap16IfLE( info->blueMax );

  con->read( &(info->redShift), 1 );
  con->read( &(info->greenShift), 1 );
  con->read( &(info->blueShift), 1 );

  con->read( info->padding, 3 );

  con->read( &(info->nameLength), 4 );
  info->nameLength = Swap32IfLE( info->nameLength );

  warning( "Width = %d, Height = %d", info->width, info->height );
  warning( "Bpp = %d, Depth = %d, Big = %d, True = %d",
	   info->bpp, info->depth, info->bigEndian, info->trueColor );
  warning( "RedMax = %d, GreenMax = %d, BlueMax = %d",
	   info->redMax, info->greenMax, info->blueMax );
  warning( "RedShift = %d, GreenShift = %d, BlueShift = %d",
	   info->redShift, info->greenShift,info-> blueShift );

  buf->resize( info->width, info->height );

  // Wait for desktop name
  warning( "Waiting for desktop name" );

  static QString statusMsg = i18n( "Waiting for desktop name..." );
  emit status( statusMsg );

  currentState = AwaitingDesktopName;
  connect( con, SIGNAL( gotEnoughData() ), SLOT( gotDesktopName() ) );
  con->waitForData( info->nameLength );
}

void KRFBDecoder::gotDesktopName()
{
  assert( info );
  assert( currentState == AwaitingDesktopName );

  warning( "Got desktop name" );

  disconnect( con, SIGNAL( gotEnoughData() ), 
	      this, SLOT( gotDesktopName() ) );

  char *buf = new char[ info->nameLength + 1 ];
  CHECK_PTR( buf );

  con->read( buf, info->nameLength );
  buf[ info->nameLength ] = '\0';
  info->name = buf;

  warning( "Desktop: %s", info->name.data() );

  delete buf;

  // Get the format we'll really use and tell the server
  decidePixelFormat();
  sendPixelFormat();
  sendAllowedEncodings();
  currentState = Idle;

  static QString statusMsg = i18n( "Active" );
  emit status( statusMsg );

  sendUpdateRequest( false );
}

void KRFBDecoder::decidePixelFormat()
{
  assert( info );

  if ( format )
    delete format;
  format = new KRFBPixelFormat;
  CHECK_PTR( format );

  // What depth do we want?
  //
  // We'll use the minimum of the remote and local depths, UNLESS an
  // eight bit session has been specifically requested by the user.
  int screenDepth = QPixmap::defaultDepth();
  int bestDepth = ( screenDepth > info->depth ) ? info->depth : screenDepth;
  int chosenDepth;

  if ( con->options()->colors256 )
    chosenDepth = 8;
  else
    chosenDepth = bestDepth;

  warning( "Screen depth=%d, server depth=%d, best depth=%d, " \
	   "eight bit %d, chosenDepth=%d",
	   screenDepth,
	   info->depth,
	   bestDepth, 
	   con->options()->colors256, chosenDepth );

  format->depth = chosenDepth;

  // If we're using the servers native depth
  if ( chosenDepth == info->depth ) {
    // Use the servers native format
    format->bpp = info->bpp;
    //    format->bigEndian = info->bigEndian;
    format->bigEndian = true;
    format->trueColor = info->trueColor;
    format->redMax = info->redMax;
    format->greenMax = info->greenMax;
    format->blueMax = info->blueMax;
    format->redShift = info->redShift;
    format->greenShift = info->greenShift;
    format->blueShift = info->blueShift;
  }
  else {
    if ( chosenDepth == 8 ) {
      format->bpp = 8;
      format->bigEndian = true;
      format->trueColor = true;
      format->redMax = 7;
      format->greenMax = 7;
      format->blueMax = 3;
      format->redShift = 0;
      format->greenShift = 3;
      format->blueShift = 6;
    }
  }

  format->redMax = Swap16IfLE( format->redMax );
  format->greenMax = Swap16IfLE( format->greenMax );
  format->blueMax = Swap16IfLE( format->blueMax );
}

void KRFBDecoder::sendPixelFormat()
{
  static char padding[3];
  con->write( &SetPixelFormatId, 1 );
  con->write( padding, 3 );

  con->write( &(format->bpp), 1 );
  con->write( &(format->depth), 1 );
  con->write( &(format->bigEndian), 1 );
  con->write( &(format->trueColor), 1 );

  con->write( &(format->redMax), 2 );
  con->write( &(format->greenMax), 2 );
  con->write( &(format->blueMax), 2 );

  con->write( &(format->redShift), 1 );
  con->write( &(format->greenShift), 1 );
  con->write( &(format->blueShift), 1 );
  con->write( format->padding, 3 ); // Padding
}

void KRFBDecoder::sendAllowedEncodings()
{
  static CARD8 padding[1];
  con->write( &SetEncodingsId, 1 );
  con->write( padding, 1 );

  static CARD16 noEncodings = con->options()->encodings();
  noEncodings = Swap16IfLE( noEncodings );
  con->write( &noEncodings, 2 );

  if ( con->options()->corre )
    con->write( &CorreEncoding, 4 );
  if ( con->options()->hexTile )
    con->write( &HexTileEncoding, 4 );
  if ( con->options()->rre )
    con->write( &RreEncoding, 4 );
  if ( con->options()->copyrect )
    con->write( &CopyRectEncoding, 4 );
  // We always support this
  con->write( &RawEncoding, 4 );
}

void KRFBDecoder::sendUpdateRequest( bool incremental )
{
  if ( currentState != Idle )
    return;

  con->write( &UpdateRequestId, 1 );
  con->write( &incremental, 1 );

  static CARD16 x = 0, y = 0;
  static CARD16 w = Swap16IfLE( info->width );
  static CARD16 h = Swap16IfLE( info->height );

  con->write( &x, 2 );
  con->write( &y, 2 );
  con->write( &w, 2 );
  con->write( &h, 2 );

  // Now wait for the update
  currentState = AwaitingUpdate;
  connect( con, SIGNAL( gotEnoughData() ), SLOT( gotUpdateHeader() ) );
  con->waitForData( UpdateHeaderLength );
}

void KRFBDecoder::gotUpdateHeader()
{
  assert( currentState == AwaitingUpdate );

  //  warning( "Got update header" );

  disconnect( con, SIGNAL( gotEnoughData() ), 
	      this, SLOT( gotUpdateHeader() ) );

  CARD8 msgType;
  con->read( &msgType, 1 );

  if ( msgType != UpdateId ) {
    // We might have a bell or server cut
    if ( msgType == ServerCutId ) {
      oldState = currentState;
      gotServerCut();
    }
    else if ( msgType == BellId ) {
      oldState = currentState;
      gotBell();
    }
    else {
      int msg = msgType;
      QString protocolError = i18n( "Protocol Error: Message Id %1 was "
                                    "found when expecting an update "
                                    "message." ).arg( msg );
      currentState = Error;
      emit error( protocolError );
    }
    return;    
  }

  CARD8 padding;
  con->read( &padding, 1 );

  con->read( &noRects, 2 );
  noRects = Swap16IfLE( noRects );

  //  warning( "Expecting %d rects", noRects );

  // Now wait for the data
  currentState = AwaitingRectHeader;
  connect( con, SIGNAL( gotEnoughData() ), SLOT( gotRectHeader() ) );
  con->waitForData( RectHeaderLength );
}

void KRFBDecoder::gotRectHeader()
{
  assert( currentState == AwaitingRectHeader );

  //  warning( "Got rect header" );

  disconnect( con, SIGNAL( gotEnoughData() ), 
	      this, SLOT( gotRectHeader() ) );

  con->read( &x, 2 );
  x = Swap16IfLE( x );
  con->read( &y, 2 );
  y = Swap16IfLE( y );

  con->read( &w, 2 );
  w = Swap16IfLE( w );
  con->read( &h, 2 );
  h = Swap16IfLE( h );

  con->read( &encoding, 4 );

  //  CARD32 encodingLocal = Swap32IfLE( encoding );
  //  warning( "Rect: x=%d, y= %d, w=%d, h=%d, encoding=%ld", 
  //	   x, y, w, h, encodingLocal );

  //
  // Each encoding needs to be handled differently. Some require
  // waiting for more data, but others like a copyrect do not.
  // Our constants have already been byte swapped, so we use
  // the remote value as is.
  //
  if ( encoding == RawEncoding ) {
      //    warning( "Raw encoding" );
    handleRawRect();
  }
  else if ( encoding == CopyRectEncoding ) {
    warning( "CopyRect encoding" );
    handleCopyRect();
  }
  else if ( encoding == RreEncoding ) {
    warning( "RRE encoding" );
    handleRRERect();
  }
  else if ( encoding == CorreEncoding ) {
    warning( "CoRRE encoding" );
    handleCoRRERect();
  }
  else if ( encoding == HexTileEncoding ) {
    warning( "HexTile encoding" );
    handleHexTileRect();
  }
  else {
    int msg = Swap32IfLE( encoding );
    QString protocolError = i18n( "Protocol Error: An unknown encoding was "
				  "used by the server %1" ).arg( msg );
    currentState = Error;
    warning( "Unknown encoding, %d", msg );
    emit error( protocolError );
    return;
  }
}

//
// Raw Encoding
//

void KRFBDecoder::handleRawRect()
{
  // We need something a bit cleverer here to handle large
  // rectanges nicely. The chunking should be based on the
  // overall size (but has to be in complete lines).

    //  warning( "Handling a raw rect chunk" );

    //  CARD32 lineCount = w * format->bpp / 8;

  if ( h > RectChunkSize ) {
    //    if ( con->sock->size() / lineCount ) {
    //      getRawRectChunk( con->sock->size() / lineCount );
    //    }
    //    else {
      getRawRectChunk( RectChunkSize );
      //   }
  }
  else {
    getRawRectChunk( h );
  }
}

void KRFBDecoder::getRawRectChunk( int lines )
{
  this->lines = lines;
  CARD32 count = lines * w * format->bpp / 8;

  // Wait for server init
  //  warning( "Waiting for raw rect chunk, %ld", count );

  currentState = AwaitingRawRectChunk;
  connect( con, SIGNAL( gotEnoughData() ), SLOT( gotRawRectChunk() ) );
  con->waitForData( count );
}

void KRFBDecoder::gotRawRectChunk()
{
  assert( currentState == AwaitingRawRectChunk );

  disconnect( con, SIGNAL( gotEnoughData() ), 
              this, SLOT( gotRawRectChunk() ) );

  //  warning( "Got raw rect chunk" );

  //
  // Read the rect data and copy it to the buffer.
  //
  
  // TODO: Replace this!
  int count = lines * w * format->bpp / 8;
  char *hack = new char[ count ];
  con->read( hack, count );
  buf->drawRawRectChunk( hack, x, y, w, lines );
  delete hack;
  // /TODO:

  h = h - lines;
  y = y + lines;

  if ( h > 0 ) {
    handleRawRect();
  }
  else {
    noRects--;

    //    warning( "There are %d rects left", noRects );

    if ( noRects ) {
      currentState = AwaitingRectHeader;
      connect( con, SIGNAL( gotEnoughData() ), SLOT( gotRectHeader() ) );
      con->waitForData( RectHeaderLength );
    }
    else
      currentState = Idle;
  }
}

//
// Copy Rectangle Encoding
//

void KRFBDecoder::handleCopyRect()
{
  currentState = AwaitingCopyRectPos;
  connect( con, SIGNAL( gotEnoughData() ), SLOT( gotCopyRectPos() ) );
  con->waitForData( CopyRectPosLength );
}

void KRFBDecoder::gotCopyRectPos()
{
  disconnect( con, SIGNAL( gotEnoughData() ),
              this, SLOT( gotCopyRectPos() ) );

  CARD16 srcX;
  CARD16 srcY;

  con->read( &srcX, 2 );
  con->read( &srcY, 2 );

  srcX = Swap16IfLE( srcX );
  srcY = Swap16IfLE( srcY );

  buf->copyRect( srcX, srcY, x, y, w, h );

  noRects--;

  //    warning( "There are %d rects left", noRects );

  if ( noRects ) {
    currentState = AwaitingRectHeader;
    connect( con, SIGNAL( gotEnoughData() ), SLOT( gotRectHeader() ) );
    con->waitForData( RectHeaderLength );
  }
  else
    currentState = Idle;
}

void KRFBDecoder::handleRRERect()
{
  warning( "RRE not implemented" );
}

void KRFBDecoder::handleCoRRERect()
{
  warning( "CoRRE not implemented" );
}

void KRFBDecoder::handleHexTileRect()
{
  warning( "HexTile not implemented" );
}

void KRFBDecoder::sendMouseEvent( QMouseEvent *e )
{
  // Deal with the buttons
  if ( e->type() != QEvent::MouseMove ) {
    buttonMask = 0;
    if ( e->type() == QEvent::MouseButtonPress ) {
      if ( e->button() & LeftButton )
        buttonMask |= 0x01;
      if ( e->button() & MidButton )
        buttonMask |= 0x04;
      if ( e->button() & RightButton )
        buttonMask |= 0x02;
    }
    else if ( e->type() == QEvent::MouseButtonRelease ) {
      if ( e->button() & LeftButton )
        buttonMask &= 0x06;
      if ( e->button() & MidButton )
        buttonMask |= 0x03;
      if ( e->button() & RightButton )
        buttonMask |= 0x05;
    }
  }

  CARD16 x = Swap16IfLE( e->x() );
  CARD16 y = Swap16IfLE( e->y() );

  con->write( &PointerEventId, 1 );
  con->write( &buttonMask, 1 );
  con->write( &x, 2 );
  con->write( &y, 2 );
}


void KRFBDecoder::sendCutEvent( const QString & )
{

}

void KRFBDecoder::gotServerCut()
{
  warning( "Got server cut" );

  currentState = AwaitingServerCutLength;
  connect( con, SIGNAL( gotEnoughData() ), SLOT( gotServerCutLength() ) );
  con->waitForData( ServerCutLenLength );
}

void KRFBDecoder::gotServerCutLength()
{
  assert( currentState = AwaitingServerCutLength );
  disconnect( con, SIGNAL( gotEnoughData() ),
              this, SLOT( gotServerCutLength() ) );

  CARD8 padding[3];
  con->read( padding, 3 );

  con->read( &serverCutTextLen, 4 );
  serverCutTextLen = Swap32IfLE( serverCutTextLen );

  currentState = AwaitingServerCutText;
  connect( con, SIGNAL( gotEnoughData() ), SLOT( gotServerCutText() ) );
  con->waitForData( serverCutTextLen );
}

void KRFBDecoder::gotServerCutText()
{
  assert( currentState = AwaitingServerCutText );

  disconnect( con, SIGNAL( gotEnoughData() ),
              this, SLOT( gotServerCutText() ) );

  char *cutbuf = new char[ serverCutTextLen + 1 ];
  CHECK_PTR( cutbuf );

  con->read( cutbuf, serverCutTextLen );
  cutbuf[ serverCutTextLen ] = '\0';

  warning( "Server cut: %s", cutbuf );

  delete cutbuf;

  // Now wait for the update (again)
  if ( oldState == AwaitingUpdate ) {
    currentState = AwaitingUpdate;
    connect( con, SIGNAL( gotEnoughData() ), SLOT( gotUpdateHeader() ) );
    con->waitForData( UpdateHeaderLength );
  }
  else if ( oldState == Idle ) {
    currentState = Idle;
  }
  else {
    warning( "Async handled in weird state" );
    currentState = oldState;
  };
}

void KRFBDecoder::gotBell()
{
  warning( "Got server bell" );
  buf->soundBell();

  // Now wait for the update (again)
  if ( oldState == AwaitingUpdate ) {
    currentState = AwaitingUpdate;
    connect( con, SIGNAL( gotEnoughData() ), SLOT( gotUpdateHeader() ) );
    con->waitForData( UpdateHeaderLength );
  }
  else if ( oldState == Idle ) {
    currentState = Idle;
  }
  else {
    warning( "Async handled in weird state" );
    currentState = oldState;
  };
}

void KRFBDecoder::sendKeyPressEvent( QKeyEvent *event )
{
  KeySym key;
  key = toKeySym( event );
  key = Swap32IfLE( key );

  CARD8 mask = true;

  CARD16 padding = 0;
  con->write( &KeyEventId, 1 );
  con->write( &mask, 1 );
  con->write( &padding, 2 );
  con->write( &key, 4 );
}

void KRFBDecoder::sendKeyReleaseEvent( QKeyEvent *event )
{
  KeySym key;
  key = toKeySym( event );
  key = Swap32IfLE( key );

  CARD8 mask = false;

  CARD16 padding = 0;
  con->write( &KeyEventId, 1 );
  con->write( &mask, 1 );
  con->write( &padding, 2 );
  con->write( &key, 4 );
}

KeySym KRFBDecoder::toKeySym( QKeyEvent *k )
{
  int ke = 0;

  ke = k->ascii();
  // Markus: Crappy hack. I dont know why lower case letters are
  // not defined in qkeydefs.h. The key() for e.g. 'l' == 'L'. 
  // This sucks. :-(

  if ( (ke == 'a') || (ke == 'b') || (ke == 'c') || (ke == 'd') 
       || (ke == 'e') || (ke == 'f') || (ke == 'g') || (ke == 'h')
       || (ke == 'i') || (ke == 'j') || (ke == 'k') || (ke == 'l')
       || (ke == 'm') || (ke == 'n') || (ke == 'o') || (ke == 'p')
       || (ke == 'q') || (ke == 'r') || (ke == 's') || (ke == 't')
       || (ke == 'u') || (ke == 'v') ||( ke == 'w') || (ke == 'x')
       || (ke == 'y') || (ke == 'z') ) {
    ke = k->key();
    ke = ke + 0x20;
    return ke;
  }

  // qkeydefs = xkeydefs! :-)
  if ( ( k->key() >= 0x0a0 ) && k->key() <= 0x0ff )
      return k->key();

  if ( ( k->key() >= 0x20 ) && ( k->key() <= 0x7e ) )
    return k->key();
  
  // qkeydefs != xkeydefs! :-(
  // This is gonna suck :-(

  switch( k->key() ) {
  case SHIFT:
    return XK_Shift_L;
  case CTRL:
    return XK_Control_L;
  case ALT:
    return XK_Alt_L;

  case Key_Escape:
    return  XK_Escape;
  case Key_Tab:
    return XK_Tab;
  case Key_Backspace:
    return XK_BackSpace;
  case Key_Return:
    return XK_Return;
  case Key_Enter:
    return XK_Return;
  case Key_Insert:
    return XK_Insert;
  case Key_Delete:
    return XK_Delete;
  case Key_Pause:
    return XK_Pause;
  case Key_Print:
    return XK_Print;
  case Key_SysReq:
    return XK_Sys_Req;
  case Key_Home:
    return XK_Home;
  case Key_End:
    return XK_End;
  case Key_Left:
     return XK_Left;
  case Key_Up:
     return XK_Up;
  case Key_Right:
     return XK_Right;
  case Key_Down:
     return XK_Down;
  case Key_Prior:
     return XK_Prior;
  case Key_Next:
     return XK_Next;

  case Key_Shift:
    return XK_Shift_L;
  case Key_Control:
    return XK_Control_L;
  case Key_Meta:
    return XK_Meta_L;
  case Key_Alt:
    return XK_Alt_L;
  case Key_CapsLock:
    return XK_Caps_Lock;
  case Key_NumLock:
    return XK_Num_Lock;
  case Key_ScrollLock:
    return XK_Scroll_Lock;

  case Key_F1:
    return XK_F1;
  case Key_F2:
    return XK_F2;
  case Key_F3:
    return XK_F3;
  case Key_F4:
    return XK_F4;
  case Key_F5:
    return XK_F5;
  case Key_F6:
    return XK_F6;
  case Key_F7:
    return XK_F7;
  case Key_F8:
    return XK_F8;
  case Key_F9:
    return XK_F9;
  case Key_F10:
    return XK_F10;
  case Key_F11:
    return XK_F11;
  case Key_F12:
    return XK_F12;
  case Key_F13:
    return XK_F13;
  case Key_F14:
    return XK_F14;
  case Key_F15:
    return XK_F15;
  case Key_F16:
    return XK_F16;
  case Key_F17:
    return XK_F17;
  case Key_F18:
    return XK_F18;
  case Key_F19:
    return XK_F19;
  case Key_F20:
    return XK_F20;
  case Key_F21:
    return XK_F21;
  case Key_F22:
    return XK_F22;
  case Key_F23:
    return XK_F23;
  case Key_F24:
    return XK_F24;

  case Key_unknown:
    return 0;
  default:
    return 0;
  }

  // Puhhhhh done. :-)
  return 0;
}
