#include <qplatformdefs.h>
#include <QtOpenGL>
#include "corewrapper.h"
#include <QElapsedTimer>

#ifdef TESTSUITE
#include "TestSuiteAI.h"
#include "GameOptions.h"
#include "MTGDeck.h"
#endif
#include "DebugRoutines.h"

#if (defined FORCE_GLES)
#undef GL_ES_VERSION_2_0
#undef GL_VERSION_2_0
#define GL_VERSION_ES_CM_1_1 1
#ifndef GL_OES_VERSION_1_1
#define glOrthof glOrtho
#define glClearDepthf glClearDepth
#endif
#endif

#if (defined FORCE_GLES)
#undef GL_ES_VERSION_2_0
#undef GL_VERSION_2_0
#define GL_VERSION_ES_CM_1_1 1
#define glOrthof glOrtho
#define glClearDepthf glClearDepth
#endif

#define ACTUAL_SCREEN_WIDTH (SCREEN_WIDTH)
#define ACTUAL_SCREEN_HEIGHT (SCREEN_HEIGHT)
#define ACTUAL_RATIO ((GLfloat)ACTUAL_SCREEN_WIDTH / (GLfloat)ACTUAL_SCREEN_HEIGHT)

// in pixels
#define kHitzonePliancy 50

// tick value equates to ms
#define kTapEventTimeout 250

// swipe duration
#define kSwipeEventMinDuration 250
// swipe distance in pixel (from top to down)
#define kSwipeMinDistance 200


QElapsedTimer WagicCore::g_startTimer;

WagicCore::WagicCore(super *parent) :
    super(parent), m_engine(0), m_app(0), m_launcher(0), m_active(false)
#ifdef Q_WS_MAEMO_5
  , dBusConnection(QDBusConnection::systemBus()), dBusInterface(0)
#endif //Q_WS_MAEMO_5
{
#ifdef QT_WIDGET
#if (defined Q_WS_MAEMO_5)
    setAttribute(Qt::WA_Maemo5AutoOrientation);
    setAttribute(Qt::WA_Maemo5NonComposited);
#endif //Q_WS_MAEMO_5
    setAttribute(Qt::WA_AcceptTouchEvents);
  //  setAttribute(Qt::WA_InputMethodEnabled);
    setMouseTracking(true);
    grabGesture(Qt::TapAndHoldGesture);
    resize(ACTUAL_SCREEN_WIDTH, ACTUAL_SCREEN_HEIGHT);
#else
    setWidth(480);
    setHeight(272);
    setFlag(QGraphicsItem::ItemHasNoContents, false);
#endif //QT_WIDGET
    g_startTimer.restart();
    m_lastTickCount = g_startTimer.elapsed();

#ifdef Q_WS_MAEMO_5
    dBusInterface = new QDBusInterface(MCE_SERVICE, MCE_REQUEST_PATH,
                                        MCE_REQUEST_IF, dBusConnection);

    // Handle screen state on / off
    dBusConnection.connect(MCE_SERVICE, MCE_SIGNAL_PATH, MCE_SIGNAL_IF, MCE_DISPLAY_SIG, this, SLOT(displayStateChanged(const QDBusMessage &)));
#endif
}


WagicWrapper::WagicWrapper()
{
    m_launcher = new JGameLauncher();
    u32 flags = m_launcher->GetInitFlags();
    if ((flags&JINIT_FLAG_ENABLE3D)!=0)
    {
        JRenderer::Set3DFlag(true);
    }

    JGECreateDefaultBindings();

    m_engine = JGE::GetInstance();
    m_app = m_launcher->GetGameApp();
    m_app->Create();
    m_engine->SetApp(m_app);
    JRenderer::GetInstance()->Enable2D();
}

WagicWrapper::~WagicWrapper()
{
    if(m_launcher)
    {
        delete m_launcher;
        m_launcher = NULL;
    }

    if(m_engine)
        m_engine->SetApp(NULL);

    if (m_app)
    {
      m_app->Destroy();
      delete m_app;
      m_app = NULL;
    }

    JGE::Destroy();
    m_engine = NULL;
}


int WagicCore::runTestSuite()
{
    int result = 0;
#ifdef TESTSUITE
    WagicWrapper* wagicCore =  new WagicWrapper();
    MTGCollection()->loadFolder("sets/primitives/");
    MTGCollection()->loadFolder("sets/", "_cards.dat");
    options.reloadProfile();
    TestSuite testSuite("test/_tests.txt");
    result = testSuite.run();
    delete wagicCore;
#endif
    DebugTrace("TestSuite done: failed test: " << result);
    return result;
}

void WagicCore::initApp()
{
    if(!m_engine)
    {
        m_launcher = new JGameLauncher();
        u32 flags = m_launcher->GetInitFlags();
        if ((flags&JINIT_FLAG_ENABLE3D)!=0)
        {
            JRenderer::Set3DFlag(true);
        }

        JGECreateDefaultBindings();

        m_engine = JGE::GetInstance();
        m_app = m_launcher->GetGameApp();
        m_app->Create();
        m_engine->SetApp(m_app);
        JRenderer::GetInstance()->Enable2D();
        setActive(true);
    }
}

WagicCore::~WagicCore()
{
#ifdef Q_WS_MAEMO_5
    if(dBusInterface)
        delete dBusInterface;
#endif //Q_WS_MAEMO_5

    if(m_launcher)
    {
        delete m_launcher;
        m_launcher = NULL;
    }

    if(m_engine)
        m_engine->SetApp(NULL);

    if (m_app)
    {
      m_app->Destroy();
      delete m_app;
      m_app = NULL;
    }

    JGE::Destroy();

    m_engine = NULL;
}

void WagicCore::pixelInput(int x, int y)
{
    if(m_engine)
        m_engine->LeftClicked(x, y);
}

void WagicCore::timerEvent( QTimerEvent* )
{
    qint64 tickCount;
    quint32 dt;
    tickCount = g_startTimer.elapsed();
    dt = (tickCount - m_lastTickCount);
    m_lastTickCount = tickCount;

    if(!m_engine)
        return;
    if(m_engine->IsDone())
        QApplication::instance()->quit();

    m_engine->SetDelta((float)dt / 1000.0f);
    m_engine->Update((float)dt / 1000.0f);

    done();
    update();
}

void WagicCore::setActive(bool active)
{
    if(!m_engine) return;

    if(!m_active && active)
    {
        m_engine->Resume();
    #if (defined Q_WS_MAEMO_5) || defined(MEEGO_EDITION_HARMATTAN) || (defined Q_WS_ANDROID)
        // 30 fps max on mobile
        m_timerId = startTimer(33);
    #else
        // 200 fps max on desktop
        m_timerId = startTimer(5);
    #endif //Q_WS_MAEMO_5
        m_active = active;
        emit activeChanged();
    }
    else if(m_active && !active)
    {
        m_engine->Pause();
    #if (defined Q_WS_MAEMO_5) || defined(MEEGO_EDITION_HARMATTAN) || (defined Q_WS_ANDROID)
        killTimer(m_timerId);
    #endif
        m_active = active;
        emit activeChanged();
    }
}

void WagicCore::initializeGL()
{
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);		// Black Background (yes that's the way fuckers)
  #if (defined GL_ES_VERSION_2_0) || (defined GL_VERSION_2_0)
  #if (defined GL_ES_VERSION_2_0)
    glClearDepthf(1.0f);					// Depth Buffer Setup
  #else
    glClearDepth(1.0f);					// Depth Buffer Setup
  #endif// (defined GL_ES_VERSION_2_0)

    glDepthFunc(GL_LEQUAL);				// The Type Of Depth Testing (Less Or Equal)
    glEnable(GL_DEPTH_TEST);				// Enable Depth Testing

  #else
  #if (defined GL_VERSION_ES_CM_1_1 || defined GL_OES_VERSION_1_1)
    glClearDepthf(1.0f);					// Depth Buffer Setup
  #else
    glClearDepth(1.0f);					// Depth Buffer Setup
  #endif

    glDepthFunc(GL_LEQUAL);				// The Type Of Depth Testing (Less Or Equal)
    glEnable(GL_DEPTH_TEST);				// Enable Depth Testing
    glShadeModel(GL_SMOOTH);				// Select Smooth Shading
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);	// Set Perspective Calculations To Most Accurate

    glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);		// Set Line Antialiasing
    glEnable(GL_LINE_SMOOTH);				// Enable it!
    glEnable(GL_TEXTURE_2D);
  #endif

    glEnable(GL_CULL_FACE);				// do not calculate inside of poly's
    glFrontFace(GL_CCW);					// counter clock-wise polygons are out

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glEnable(GL_SCISSOR_TEST);				// Enable Clipping
}

#ifndef QT_WIDGET
void WagicCore::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    painter->beginNativePainting();

    initApp();

    resizeGL ( boundingRect().size().width(), boundingRect().size().height());

    initializeGL();

    paintGL();

    painter->endNativePainting();
}
#endif //QT_WIDGET

void WagicCore::paintGL()
{
    if(m_engine)
        m_engine->Render();
}


void WagicCore::resizeGL(int width, int height)
{
    if ((GLfloat)width / (GLfloat)height <= ACTUAL_RATIO)
    {
      m_viewPort.setLeft(0);
      m_viewPort.setTop(-((width/ACTUAL_RATIO)-height)/2);
      m_viewPort.setRight(width);
      m_viewPort.setBottom(-((width/ACTUAL_RATIO)-height)/2 + width / ACTUAL_RATIO);
    }
    else
    {
      m_viewPort.setLeft(-(height*ACTUAL_RATIO-width)/2);
      m_viewPort.setTop(0);
      m_viewPort.setRight(-(height*ACTUAL_RATIO-width)/2 + height * ACTUAL_RATIO);
      m_viewPort.setBottom(height);
    }

    glViewport(m_viewPort.left(), m_viewPort.top(), m_viewPort.right()-m_viewPort.left(), m_viewPort.bottom()-m_viewPort.top());

    JRenderer::GetInstance()->SetActualWidth(m_viewPort.right()-m_viewPort.left());
    JRenderer::GetInstance()->SetActualHeight(m_viewPort.bottom()-m_viewPort.top());
    glScissor(0, 0, width, height);

#if (!defined GL_ES_VERSION_2_0) && (!defined GL_VERSION_2_0)

    glMatrixMode (GL_PROJECTION);										// Select The Projection Matrix
    glLoadIdentity ();													// Reset The Projection Matrix

#if (defined GL_VERSION_ES_CM_1_1 || defined GL_OES_VERSION_1_1)
    glOrthof(0.0f, (float) (m_viewPort.right()-m_viewPort.left())-1.0f, 0.0f, (float) (m_viewPort.bottom()-m_viewPort.top())-1.0f, -1.0f, 1.0f);
#else
    glOrtho(0.0f, (float) (m_viewPort.right()-m_viewPort.left())-1.0f, 0.0f, (float) (m_viewPort.bottom()-m_viewPort.top())-1.0f, -1.0f, 1.0f);
#endif

    glMatrixMode (GL_MODELVIEW);										// Select The Modelview Matrix
    glLoadIdentity ();													// Reset The Modelview Matrix

    glDisable (GL_DEPTH_TEST);
#endif
}

void WagicCore::keyPressEvent(QKeyEvent *event)
{
  switch(event->key())
  {
#if (defined Q_WS_MAEMO_5)
  case Qt::Key_F7:
    /* interrupt please */
    m_engine->HoldKey_NoRepeat(JGE_BTN_SEC);
    break;
  case Qt::Key_F8:
    /* next phase please */
    m_engine->HoldKey_NoRepeat(JGE_BTN_PREV);
    break;
#endif // Q_WS_MAEMO_5
  case Qt::Key_F:
    JGEToggleFullscreen();
    break;
  default:
    m_engine->HoldKey_NoRepeat((LocalKeySym)event->key());
  }
  event->accept();
  super::keyPressEvent(event);
}

void WagicCore::keyReleaseEvent(QKeyEvent *event)
{
    switch(event->key())
    {
#if (defined Q_WS_MAEMO_5)
    case Qt::Key_F7:
      /* interrupt please */
      m_engine->ReleaseKey(JGE_BTN_SEC);
      break;
    case Qt::Key_F8:
      /* next phase please */
      m_engine->ReleaseKey(JGE_BTN_PREV);
      break;
  #endif // Q_WS_MAEMO_5
    default:
      m_engine->ReleaseKey((LocalKeySym)event->key());
    }

    event->accept();
    super::keyReleaseEvent(event);
}

#ifdef QT_WIDGET
void WagicCore::wheelEvent(QWheelEvent *event)
#else
void WagicCore::wheelEvent ( QGraphicsSceneWheelEvent * event)
#endif
{
    if(event->orientation() == Qt::Vertical)
        m_engine->Scroll(0, 3*event->delta());
    else

        m_engine->Scroll(3*event->delta(), 0);

    event->accept();
}

#ifdef QT_WIDGET
void WagicCore::tapAndHoldTriggered(QTapAndHoldGesture* gesture)
{
    if (gesture->state() == Qt::GestureFinished) {
        m_engine->HoldKey_NoRepeat(JGE_BTN_MENU);
    }
}

void WagicCore::mousePressEvent(QMouseEvent *event)
{
  if(event->button() == Qt::LeftButton)
  {
    m_lastPos = event->pos();
    // this is intended to convert window coordinate into game coordinate.
    // this is correct only if the game and window have the same aspect ratio, otherwise, it's just wrong
    int actualWidth = (int) JRenderer::GetInstance()->GetActualWidth();
    int actualHeight = (int) JRenderer::GetInstance()->GetActualHeight();

    if (lastPosy() >= m_viewPort.top() &&
      lastPosy() <= m_viewPort.bottom() &&
      lastPosx() <= m_viewPort.right() &&
      lastPosx() >= m_viewPort.left()) {
      m_engine->LeftClicked(
                  ((lastPosx()-m_viewPort.left())*SCREEN_WIDTH)/actualWidth,
                  ((lastPosy()-m_viewPort.top())*SCREEN_HEIGHT)/actualHeight);
#if (!defined Q_WS_MAEMO_5) && (!defined MEEGO_EDITION_HARMATTAN) && (!defined Q_WS_ANDROID)
      m_engine->HoldKey_NoRepeat(JGE_BTN_OK);
#else
      mMouseDownX = lastPosx();
      mMouseDownY = lastPosy();
      mLastFingerDownTime = g_startTimer.elapsed();
#endif
    } else if(lastPosy()<m_viewPort.top()) {
      m_engine->HoldKey_NoRepeat(JGE_BTN_MENU);
    } else if(lastPosy()>m_viewPort.bottom()) {
      m_engine->HoldKey_NoRepeat(JGE_BTN_NEXT);
    }
    event->accept();
  }
  else if(event->button() == Qt::RightButton)
  { /* next phase please */
    m_engine->HoldKey_NoRepeat(JGE_BTN_PREV);
    event->accept();
  }
  else if(event->button() == Qt::MidButton)
  { /* interrupt please */
    m_engine->HoldKey_NoRepeat(JGE_BTN_SEC);
    event->accept();
  }
  else
  {
    super::mousePressEvent(event);
  }
}

void WagicCore::mouseReleaseEvent(QMouseEvent *event)
{
  if(event->button() == Qt::LeftButton)
  {
    m_lastPos = event->pos();

    if (lastPosy() >= m_viewPort.top() &&
      lastPosy() <= m_viewPort.bottom() &&
      lastPosx() <= m_viewPort.right() &&
      lastPosx() >= m_viewPort.left()) {
#if (defined Q_WS_MAEMO_5) || (defined MEEGO_EDITION_HARMATTAN) || (defined Q_WS_ANDROID)
      if(g_startTimer.elapsed() - mLastFingerDownTime <= kTapEventTimeout )
      {
        if(abs(mMouseDownX - lastPosx()) < kHitzonePliancy &&
           abs(mMouseDownY - lastPosy()) < kHitzonePliancy)
        {
          m_engine->HoldKey_NoRepeat(JGE_BTN_OK);
        }
      }
      else if (g_startTimer.elapsed() - mLastFingerDownTime >= kSwipeEventMinDuration)
      { // Let's swipe
        m_engine->Scroll(lastPosx()-mMouseDownX, lastPosy()-mMouseDownY);
      }
#else
//#if (!defined Q_WS_MAEMO_5) && (!defined MEEGO_EDITION_HARMATTAN)
      m_engine->ReleaseKey(JGE_BTN_OK);
#endif
      m_engine->ReleaseKey(JGE_BTN_MENU);
    } else if(lastPosy() < m_viewPort.top()) {
      m_engine->ReleaseKey(JGE_BTN_MENU);
    } else if(lastPosy() > m_viewPort.bottom()) {
      m_engine->ReleaseKey(JGE_BTN_NEXT);
    }
    event->accept();
  }
  else if(event->button() == Qt::RightButton)
  { /* next phase please */
    m_engine->ReleaseKey(JGE_BTN_PREV);
    event->accept();
  }
  else if(event->button() == Qt::MidButton)
  { /* interrupt please */
    m_engine->ReleaseKey(JGE_BTN_SEC);
    event->accept();
  }
  else
  {
    super::mouseReleaseEvent(event);
  }
}

void WagicCore::mouseMoveEvent(QMouseEvent *event)
{
  int actualWidth = (int) JRenderer::GetInstance()->GetActualWidth();
  int actualHeight = (int) JRenderer::GetInstance()->GetActualHeight();

  m_lastPos = event->pos();

  if (lastPosy() >= m_viewPort.top() &&
    lastPosy() <= m_viewPort.bottom() &&
    lastPosx() <= m_viewPort.right() &&
    lastPosx() >= m_viewPort.left()) {
    m_engine->LeftClicked(
                ((lastPosx()-m_viewPort.left())*SCREEN_WIDTH)/actualWidth,
                ((lastPosy()-m_viewPort.top())*SCREEN_HEIGHT)/actualHeight);
    event->accept();
  } else {
    super::mouseMoveEvent(event);
  }
}

void WagicCore::showEvent ( QShowEvent * )
{
  setActive(true);
}

void WagicCore::hideEvent ( QHideEvent * )
{
  setActive(false);
}

bool WagicCore::event(QEvent *event)
{
    if (event->type() == QEvent::Gesture)
        return gestureEvent(static_cast<QGestureEvent*>(event));
#if (defined Q_WS_MAEMO_5) || (defined MEEGO_EDITION_HARMATTAN) || (defined Q_WS_ANDROID)
    else if (event->type() == QEvent::WindowActivate)
    {
        showEvent(NULL);
    }
    else if (event->type() == QEvent::WindowDeactivate)
    {
        hideEvent(NULL);
    }
#endif

    return QGLWidget::event(event);
}

bool WagicCore::gestureEvent(QGestureEvent* event)
{
    if (QGesture *tapAndHold = event->gesture(Qt::TapAndHoldGesture))
        tapAndHoldTriggered(static_cast<QTapAndHoldGesture *>(tapAndHold));

    return true;
}

void WagicCore::start(int)
{
#if (defined Q_WS_MAEMO_5) || (defined MEEGO_EDITION_HARMATTAN) || (defined Q_WS_ANDROID)
    showFullScreen();
#else
    show();
#endif
    initApp();
}

#endif //QT_WIDGET

#ifdef Q_WS_MAEMO_5
void WagicCore::displayStateChanged(const QDBusMessage &message)
{
  QString state = message.arguments().at(0).toString();
  if (!state.isEmpty()) {
    if (state == MCE_DISPLAY_ON_STRING && isActiveWindow()) {
       setActive(true);
    }
    else if (state == MCE_DISPLAY_OFF_STRING) {
        setActive(false);
    }
  }
}
#endif
