#include "imagerenderwidget.h"
#include "clientglobals.h"
#include "connectionprofiler.h"
#include "clientsocket.h"
#include "imagerenderglwidget.h"
#include "imagerenderqwidget.h"
#include <QPainter>
#include <QPaintEvent>
#include <QResizeEvent>
#include <QCursor>
#include <QApplication>
#include <QGLFormat>
#include <QTime>
#include <QTimer>
#include <QVBoxLayout>
#include <QSettings>
#include <QDebug>

/// XKeys
#define XK_MISCELLANY 1
#define XK_XKB_KEYS 1
#define XK_LATIN1 1
#define XK_KOREAN 1
#include "keysymdef.h"

// Taken from qemu source by Fabrice Bellard
const quint8 ImageRenderWidget::m_keyCodeMap[61] = {
    0,         /*  97 EVDEV - RO   ("Internet" Keyboards) */
    0,         /*  98 EVDEV - KATA (Katakana) */
    0,         /*  99 EVDEV - HIRA (Hiragana) */
    0x79,      /* 100 EVDEV - HENK (Henkan) */
    0x70,      /* 101 EVDEV - HKTG (Hiragana/Katakana toggle) */
    0x7b,      /* 102 EVDEV - MUHE (Muhenkan) */
    0,         /* 103 EVDEV - JPCM (KPJPComma) */
    0x9c,      /* 104 KPEN */
    0x9d,      /* 105 RCTL */
    0xb5,      /* 106 KPDV */
    0xb7,      /* 107 PRSC */
    0xb8,      /* 108 RALT */
    0,         /* 109 EVDEV - LNFD ("Internet" Keyboards) */
    0xc7,      /* 110 HOME */
    0xc8,      /* 111 UP */
    0xc9,      /* 112 PGUP */
    0xcb,      /* 113 LEFT */
    0xcd,      /* 114 RGHT */
    0xcf,      /* 115 END */
    0xd0,      /* 116 DOWN */
    0xd1,      /* 117 PGDN */
    0xd2,      /* 118 INS */
    0xd3,      /* 119 DELE */
    0,         /* 120 EVDEV - I120 ("Internet" Keyboards) */
    0,         /* 121 EVDEV - MUTE */
    0,         /* 122 EVDEV - VOL- */
    0,         /* 123 EVDEV - VOL+ */
    0,         /* 124 EVDEV - POWR */
    0,         /* 125 EVDEV - KPEQ */
    0,         /* 126 EVDEV - I126 ("Internet" Keyboards) */
    0,         /* 127 EVDEV - PAUS */
    0,         /* 128 EVDEV - ???? */
    0,         /* 129 EVDEV - I129 ("Internet" Keyboards) */
    0xf1,      /* 130 EVDEV - HNGL (Korean Hangul Latin toggle) */
    0xf2,      /* 131 EVDEV - HJCV (Korean Hangul Hanja toggle) */
    0x7d,      /* 132 AE13 (Yen)*/
    0xdb,      /* 133 EVDEV - LWIN */
    0xdc,      /* 134 EVDEV - RWIN */
    0xdd,      /* 135 EVDEV - MENU */
    0,         /* 136 EVDEV - STOP */
    0,         /* 137 EVDEV - AGAI */
    0,         /* 138 EVDEV - PROP */
    0,         /* 139 EVDEV - UNDO */
    0,         /* 140 EVDEV - FRNT */
    0,         /* 141 EVDEV - COPY */
    0,         /* 142 EVDEV - OPEN */
    0,         /* 143 EVDEV - PAST */
    0,         /* 144 EVDEV - FIND */
    0,         /* 145 EVDEV - CUT  */
    0,         /* 146 EVDEV - HELP */
    0,         /* 147 EVDEV - I147 */
    0,         /* 148 EVDEV - I148 */
    0,         /* 149 EVDEV - I149 */
    0,         /* 150 EVDEV - I150 */
    0,         /* 151 EVDEV - I151 */
    0,         /* 152 EVDEV - I152 */
    0,         /* 153 EVDEV - I153 */
    0,         /* 154 EVDEV - I154 */
    0,         /* 155 EVDEV - I156 */
    0,         /* 156 EVDEV - I157 */
    0,         /* 157 EVDEV - I158 */
};

ImageRenderWidget::ImageRenderWidget(QWidget *parent ) :
        QWidget( parent ), m_listening(false),
        m_isMouseGrabbed(false),
        m_useNativeKeySyms(false),
        m_useRelativePointer(false),
        m_connection(0)
{
    bool useOpenGL = false;
    QString file = QCoreApplication::arguments().value(1);
    if( !file.isEmpty() )
    {
        QSettings settings(file, QSettings::IniFormat, this);
        useOpenGL = settings.value("Display/OpenGL", false).toBool();
    }

    if( QGLFormat::hasOpenGL() && useOpenGL )
        m_view = new ImageRenderGLWidget(this);
    else
        m_view = new ImageRenderQWidget(this);

    connect( this, SIGNAL(mergeFramebufferChange(QRect,QImage)), m_view, SLOT(mergeFramebufferChange(QRect,QImage)));
    connect( this, SIGNAL(framebufferResize(QSize)), m_view, SLOT(handleFramebufferResize(QSize)));

    QVBoxLayout *verticalLayout = new QVBoxLayout(this);
    verticalLayout->addWidget(m_view);

    m_view->installEventFilter(QCoreApplication::instance());

    setFocusPolicy ( Qt::StrongFocus );
    setMouseTracking( true );

}

ImageRenderWidget::~ImageRenderWidget()
{

}

void ImageRenderWidget::updateImage()
{
    //qDebug("redraw request");
    m_view->update();
}

bool ImageRenderWidget::event( QEvent *event )
{
    //qDebug() << "Got:" << event->type();
    return eventFilter(this,event);
}

bool ImageRenderWidget::eventFilter(QObject *obj, QEvent *event)
{
    //qDebug() << "Caught:" << event->type();
    Q_UNUSED(obj);
    switch( event->type() )
    {
    case QEvent::KeyPress:
        keyPressEvent( static_cast<QKeyEvent*>(event) );
        break;
    case QEvent::KeyRelease:
        keyReleaseEvent( static_cast<QKeyEvent*>(event) );
        break;
    case QEvent::MouseMove:
        mouseMoveEvent( static_cast<QMouseEvent*>(event) );
        break;
    case QEvent::MouseButtonDblClick:
        mouseDoubleClickEvent( static_cast<QMouseEvent*>(event) );
        break;
    case QEvent::MouseButtonPress:
        mousePressEvent(  static_cast<QMouseEvent*>(event) );
        break;
    case QEvent::MouseButtonRelease:
        mouseReleaseEvent(  static_cast<QMouseEvent*>(event) );
        break;
    case QEvent::Wheel:
        wheelEvent( static_cast<QWheelEvent*>(event) );
        break;
    default:
        return QWidget::eventFilter(obj,event);
        break;
    }
    event->accept();
    return true;
}

void ImageRenderWidget::setCurrentConnection( ClientSocket *conn )
{
    m_connection = conn;
}

void ImageRenderWidget::handleFramebufferResize( const QSize &size )
{
    setMinimumSize(size);
    emit framebufferResize(size);
}

void ImageRenderWidget::listenForEvents(bool listen )
{
    if ( shouldCaptureEvent() )
        toggleMouseGrab();
    m_listening = listen;
}

void ImageRenderWidget::keyPressEvent ( QKeyEvent *event )
{
    if( toggleMouseGrab(event) )
        return;

    if( m_useNativeKeySyms )
        translateNativeKeyEvent(event);
    else
        translateKeyEvent(event);
}

void ImageRenderWidget::keyReleaseEvent ( QKeyEvent * event )
{
    if( m_useNativeKeySyms )
        translateNativeKeyEvent(event);
    else
        translateKeyEvent(event);
}

QPoint ImageRenderWidget::moveMouse( const QPoint &newPoint )
{
    if( m_isMouseGrabbed )
    {
        if( m_lastPos.isNull() )
        {
            m_lastPos = newPoint;
        }

        QPoint center = geometry().center();
        QPoint delta = newPoint - m_lastPos;
        m_lastPos = newPoint;
        bool needsAdjustment = false;

        if( (m_lastPos.x() < center.x() / 2) || (m_lastPos.x() > width() - (center.x() / 2 ) ))
        {
            m_lastPos.setX( center.x());
            needsAdjustment = true;
        }

        if( (m_lastPos.y() < center.y() / 2) || (m_lastPos.y() > height() - (center.y() / 2 ) ))
        {
            m_lastPos.setY( center.y());
            needsAdjustment = true;
        }

        if( needsAdjustment )
            QCursor::setPos(mapToGlobal(m_lastPos));

        return accelerate(delta);
    }

    return QPoint(0,0);

}

void ImageRenderWidget::processMouseEvent( QMouseEvent *event )
{
    if( !shouldCaptureEvent() )
        return;

    MouseEvent mouseEvent;
    if( m_useRelativePointer )
        mouseEvent.position = moveMouse(event->pos());
    else
        mouseEvent.position = event->pos();

    mouseEvent.buttonMask = MouseEvent::None;
    if ( event->buttons().testFlag( Qt::LeftButton ) )
        mouseEvent.buttonMask |= MouseEvent::Left;
    if ( event->buttons().testFlag( Qt::MidButton ) )
        mouseEvent.buttonMask |= MouseEvent::Middle;
    if ( event->buttons().testFlag( Qt::RightButton ) )
        mouseEvent.buttonMask |= MouseEvent::Right;

    m_connection->sendMouseEvent(mouseEvent);
    event->accept();
}

void ImageRenderWidget::mouseMoveEvent ( QMouseEvent * event )
{
    processMouseEvent(event);
}

void ImageRenderWidget::mousePressEvent ( QMouseEvent * event )
{
    processMouseEvent(event);
    if ( !m_isMouseGrabbed )
        toggleMouseGrab();
}

void ImageRenderWidget::mouseReleaseEvent ( QMouseEvent * event )
{
    processMouseEvent(event);
}

void ImageRenderWidget::mouseDoubleClickEvent ( QMouseEvent * event )
{
    if( !shouldCaptureEvent() )
        return;

    MouseEvent mouseEvent;
    if( m_useRelativePointer )
        mouseEvent.position = moveMouse(event->pos());
    else
        mouseEvent.position = event->pos();

    mouseEvent.buttonMask = MouseEvent::None;
    if ( event->buttons().testFlag( Qt::LeftButton ) )
        mouseEvent.buttonMask |= MouseEvent::Left;
    if ( event->buttons().testFlag( Qt::MidButton ) )
        mouseEvent.buttonMask |= MouseEvent::Middle;
    if ( event->buttons().testFlag( Qt::RightButton ) )
        mouseEvent.buttonMask |= MouseEvent::Right;

    m_connection->sendMouseEvent(mouseEvent);

    int buttonMask = mouseEvent.buttonMask;

    mouseEvent.buttonMask = MouseEvent::None;
    m_connection->sendMouseEvent(mouseEvent);

    mouseEvent.buttonMask = buttonMask;
    m_connection->sendMouseEvent(mouseEvent);

    mouseEvent.buttonMask = MouseEvent::None;
    m_connection->sendMouseEvent(mouseEvent);
}

void ImageRenderWidget::wheelEvent ( QWheelEvent * event )
{   
    if( !shouldCaptureEvent() )
        return;

    MouseEvent mouseEvent;

    if( m_useRelativePointer )
        mouseEvent.position = moveMouse(event->pos());
    else
        mouseEvent.position = event->pos();

    mouseEvent.buttonMask = MouseEvent::None;
    if ( event->delta() > 0 )
        mouseEvent.buttonMask = MouseEvent::WheelUp;
    else
        mouseEvent.buttonMask = MouseEvent::WheelDown;

    m_connection->sendMouseEvent(mouseEvent);
    event->accept();
}

bool ImageRenderWidget::shouldCaptureEvent()
{
    if( m_isMouseGrabbed && m_listening)
        return true;
    return false;
}

void ImageRenderWidget::toggleMouseGrab( )
{
    bool wasGrabbed = m_isMouseGrabbed;
    m_isMouseGrabbed = !m_isMouseGrabbed;

    if ( wasGrabbed && !m_isMouseGrabbed )
    {
        QApplication::restoreOverrideCursor();
        releaseMouse();
        releaseKeyboard();
    }
    else
    {
        QApplication::setOverrideCursor(Qt::BlankCursor);
        QCursor::setPos(mapToGlobal(geometry().center()));
        m_lastPos = geometry().center();
        grabMouse();
        grabKeyboard();
    }

    //window()->setWindowState(window()->windowState() ^ Qt::WindowFullScreen);
}

bool ImageRenderWidget::toggleMouseGrab( QKeyEvent *event )
{
    if ( (event->key() == Qt::Key_Home) &&
         (event->modifiers() == (Qt::AltModifier|Qt::ControlModifier)))
    {
        if ( m_isMouseGrabbed )
        {
            toggleMouseGrab();
            return true;
        }
    }
    return false;
}


void ImageRenderWidget::translateNativeKeyEvent( QKeyEvent *event )
{

    KeyEvent keyEvent = { 0, 0, 0 };
    if (event->isAutoRepeat() && (event->type() == QEvent::KeyRelease))
    {
       return;
    }

    keyEvent.isPressed = (event->type() == QEvent::KeyPress);
    quint32 keycode = event->nativeScanCode();

    if (keycode < 9) {
        keycode = 0;
    } else if (keycode < 97) {
        keycode -= 8; /* just an offset */
    } else if (keycode < 158) {
        keycode = m_keyCodeMap[keycode - 97];
    } else if (keycode == 208) { /* Hiragana_Katakana */
        keycode = 0x70;
    } else if (keycode == 211) { /* backslash */
        keycode = 0x73;
    } else {
        keycode = 0;
    }

    if ( keycode > 127 )
        keycode |= 0x80;

    keyEvent.scanCode = keycode;
    keyEvent.xkey = event->nativeVirtualKey();

    if( keyEvent.scanCode != 0 )
    {
        m_connection->sendNativeKeyEvent(keyEvent);
        event->accept();
    }
}

void ImageRenderWidget::translateKeyEvent( QKeyEvent *event )
{    

    KeyEvent keyEvent = { 0, 0, 0 };
    if (event->isAutoRepeat() && (event->type() == QEvent::KeyRelease))
    {
       return;
    }

    keyEvent.isPressed = (event->type() == QEvent::KeyPress);

    switch( event->key() )
    {
            case Qt::Key_Shift: keyEvent.xkey = XK_Shift_L; break;
            case Qt::Key_Control: keyEvent.xkey = XK_Control_L; break;
            case Qt::Key_Meta: keyEvent.xkey = XK_Meta_L; break;
            case Qt::Key_Alt: keyEvent.xkey = XK_Alt_L; break;
            case Qt::Key_Escape: keyEvent.xkey = XK_Escape; break;
            case Qt::Key_Tab: keyEvent.xkey = XK_Tab; break;
            case Qt::Key_Backtab: keyEvent.xkey = XK_Tab; break;
            case Qt::Key_Space: keyEvent.xkey = XK_space; break;
            case Qt::Key_Backspace: keyEvent.xkey = XK_BackSpace; break;
            case Qt::Key_Return: keyEvent.xkey = XK_Return; break;
            case Qt::Key_Insert: keyEvent.xkey = XK_Insert; break;
            case Qt::Key_Delete: keyEvent.xkey = XK_Delete; break;
            case Qt::Key_Pause: keyEvent.xkey = XK_Pause; break;
            case Qt::Key_Print: keyEvent.xkey = XK_Print; break;
            case Qt::Key_Home: keyEvent.xkey = XK_Home; break;
            case Qt::Key_End: keyEvent.xkey = XK_End; break;
            case Qt::Key_Left: keyEvent.xkey = XK_Left; break;
            case Qt::Key_Up: keyEvent.xkey = XK_Up; break;
            case Qt::Key_Right: keyEvent.xkey = XK_Right; break;
            case Qt::Key_Down: keyEvent.xkey = XK_Down; break;
            case Qt::Key_PageUp: keyEvent.xkey = XK_Prior; break;
            case Qt::Key_PageDown: keyEvent.xkey = XK_Next; break;
            case Qt::Key_CapsLock: keyEvent.xkey = XK_Caps_Lock; break;
            case Qt::Key_NumLock: keyEvent.xkey = XK_Num_Lock; break;
            case Qt::Key_ScrollLock: keyEvent.xkey = XK_Scroll_Lock; break;
            case Qt::Key_Super_L: keyEvent.xkey = XK_Super_L; break;
            case Qt::Key_Super_R: keyEvent.xkey = XK_Super_R; break;
            case Qt::Key_Menu: keyEvent.xkey = XK_Menu; break;
            case Qt::Key_Hyper_L: keyEvent.xkey = XK_Hyper_L; break;
            case Qt::Key_Hyper_R: keyEvent.xkey = XK_Hyper_R; break;
            case Qt::Key_Help: keyEvent.xkey = XK_Help; break;
            case Qt::Key_AltGr: keyEvent.xkey = XK_ISO_Level3_Shift; break;
            case Qt::Key_Multi_key: keyEvent.xkey = XK_Multi_key; break;
            case Qt::Key_SingleCandidate: keyEvent.xkey = XK_SingleCandidate; break;
            case Qt::Key_MultipleCandidate: keyEvent.xkey = XK_MultipleCandidate; break;
            case Qt::Key_PreviousCandidate: keyEvent.xkey = XK_PreviousCandidate; break;
            case Qt::Key_Mode_switch: keyEvent.xkey = XK_Mode_switch; break;
            case Qt::Key_Kanji: keyEvent.xkey = XK_Kanji; break;
            case Qt::Key_Muhenkan: keyEvent.xkey = XK_Muhenkan; break;
            case Qt::Key_Henkan: keyEvent.xkey = XK_Henkan; break;
            case Qt::Key_Romaji: keyEvent.xkey = XK_Romaji; break;
            case Qt::Key_Hiragana: keyEvent.xkey = XK_Hiragana; break;
            case Qt::Key_Katakana: keyEvent.xkey = XK_Katakana; break;
            case Qt::Key_Hiragana_Katakana: keyEvent.xkey = XK_Hiragana_Katakana; break;
            case Qt::Key_Zenkaku: keyEvent.xkey = XK_Zenkaku; break;
            case Qt::Key_Hankaku: keyEvent.xkey = XK_Hankaku; break;
            case Qt::Key_Zenkaku_Hankaku: keyEvent.xkey = XK_Zenkaku_Hankaku; break;
            case Qt::Key_Touroku: keyEvent.xkey = XK_Touroku; break;
            case Qt::Key_Massyo: keyEvent.xkey = XK_Massyo; break;
            case Qt::Key_Kana_Lock: keyEvent.xkey = XK_Kana_Lock; break;
            case Qt::Key_Kana_Shift: keyEvent.xkey = XK_Kana_Shift; break;
            case Qt::Key_Eisu_Shift: keyEvent.xkey = XK_Eisu_Shift; break;
            case Qt::Key_Eisu_toggle: keyEvent.xkey = XK_Eisu_toggle; break;
            case Qt::Key_Hangul: keyEvent.xkey = XK_Hangul; break;
            case Qt::Key_Hangul_Start: keyEvent.xkey = XK_Hangul_Start; break;
            case Qt::Key_Hangul_End: keyEvent.xkey = XK_Hangul_End; break;
            case Qt::Key_Hangul_Hanja: keyEvent.xkey = XK_Hangul_Hanja; break;
            case Qt::Key_Hangul_Jamo: keyEvent.xkey = XK_Hangul_Jamo; break;
            case Qt::Key_Hangul_Romaja: keyEvent.xkey = XK_Hangul_Romaja; break;
            case Qt::Key_Hangul_Jeonja: keyEvent.xkey = XK_Hangul_Jeonja; break;
            case Qt::Key_Hangul_Banja: keyEvent.xkey = XK_Hangul_Banja; break;
            case Qt::Key_Hangul_PreHanja: keyEvent.xkey = XK_Hangul_PreHanja; break;
            case Qt::Key_Hangul_PostHanja: keyEvent.xkey = XK_Hangul_PostHanja; break;
            case Qt::Key_Hangul_Special: keyEvent.xkey = XK_Hangul_Special; break;
            case Qt::Key_Dead_Grave: keyEvent.xkey = XK_dead_grave; break;
            case Qt::Key_Dead_Acute: keyEvent.xkey = XK_dead_acute; break;
            case Qt::Key_Dead_Circumflex: keyEvent.xkey = XK_dead_circumflex; break;
            case Qt::Key_Dead_Tilde: keyEvent.xkey = XK_dead_tilde; break;
            case Qt::Key_Dead_Macron: keyEvent.xkey = XK_dead_macron; break;
            case Qt::Key_Dead_Breve: keyEvent.xkey = XK_dead_breve; break;
            case Qt::Key_Dead_Abovedot: keyEvent.xkey = XK_dead_abovedot; break;
            case Qt::Key_Dead_Diaeresis: keyEvent.xkey = XK_dead_diaeresis; break;
            case Qt::Key_Dead_Abovering: keyEvent.xkey = XK_dead_abovering; break;
            case Qt::Key_Dead_Doubleacute: keyEvent.xkey = XK_dead_doubleacute; break;
            case Qt::Key_Dead_Caron: keyEvent.xkey = XK_dead_caron; break;
            case Qt::Key_Dead_Cedilla: keyEvent.xkey = XK_dead_cedilla; break;
            case Qt::Key_Dead_Ogonek: keyEvent.xkey = XK_dead_ogonek; break;
            case Qt::Key_Dead_Iota: keyEvent.xkey = XK_dead_iota; break;
            case Qt::Key_Dead_Voiced_Sound: keyEvent.xkey = XK_dead_voiced_sound; break;
            case Qt::Key_Dead_Semivoiced_Sound: keyEvent.xkey = XK_dead_semivoiced_sound; break;
            case Qt::Key_Dead_Belowdot: keyEvent.xkey = XK_dead_belowdot; break;
      }

      if( event->key() >= Qt::Key_F1 && event->key() <= Qt::Key_F35 )
      {
            keyEvent.xkey = XK_F1 + event->key() - Qt::Key_F1;
      }

      if( keyEvent.xkey == 0 )
      {
          QKeySequence key = QKeySequence(event->key());
          //qDebug(  "Got: %x %s", event->key(), key.toString().toLatin1().toHex().constData() );
          if(key.count() == 1)
          {
                  QString text = key.toString();
                  if( !event->modifiers().testFlag( Qt::ShiftModifier ) )
                  {
                        text = text.toUpper();
                  }
                  keyEvent.xkey = text.utf16()[0];
          }
      }

      if( event->modifiers().testFlag( Qt::ControlModifier ) &&
            event->modifiers().testFlag( Qt::ShiftModifier ) &&
            keyEvent.xkey >= 64 && keyEvent.xkey < 0xF000 )
          {
            KeyEvent modifierKeyEvent = { XK_ISO_Level3_Shift, 0, true };
            m_connection->sendKeyEvent(modifierKeyEvent);
            modifierKeyEvent.isPressed = false;
            m_connection->sendKeyEvent(modifierKeyEvent);
          }

      if( keyEvent.xkey == XK_Shift_L ||
          keyEvent.xkey == XK_Control_L ||
          keyEvent.xkey == XK_Meta_L ||
          keyEvent.xkey == XK_Alt_L )
      {
            if( keyEvent.isPressed )
                  m_mods[keyEvent.xkey] = true;
            else if( m_mods.contains( keyEvent.xkey ) )
                  m_mods.remove( keyEvent.xkey );
            else
                  unpressModifiers();
      }

      if( keyEvent.xkey != 0 )
      {
            m_connection->sendKeyEvent(keyEvent);
            event->accept();
      }
}

void ImageRenderWidget::unpressModifiers( )
{
    foreach( quint32 key, m_mods.keys() )
    {
        KeyEvent event = { key, 0, false };
        m_connection->sendKeyEvent(event);
    }
    m_mods.clear();
}

void ImageRenderWidget::enterEvent ( QEvent * event )
{
   Q_UNUSED(event);
   // capture all keys, including system keys here...
   grabMouse();
   releaseMouse();
   grabKeyboard();
}

void ImageRenderWidget::leaveEvent ( QEvent * event )
{
    Q_UNUSED(event);
   // release all keys, including system keys here...
   releaseKeyboard();
}

QPoint ImageRenderWidget::accelerate( const QPoint &pos ) const
{
    qreal accelerationFactor = 1;
    return (pos * accelerationFactor) + QPoint(0x7FFF,0x7FFF);
}

void ImageRenderWidget::enableNativeKeySyms()
{
    m_useNativeKeySyms = true;
    qDebug("Enable native keysyms");
}

void ImageRenderWidget::enableRelativePointer()
{
    m_useRelativePointer = true;
    qDebug("Enable relative mouse pointer");
}


