#include "GUIInputRZA2M_M13.h"

extern "C"
{
#include <ctype.h>

#include "r_typedefs.h"
#include "compiler_settings.h"
}

#define INVALID_AXIS_VALUE -1

static int_t an_tp_evt_id[6];

#include "eC_String.h"
#include "GUITimer.h"
#include "GUITrace.h"

#include "GUIMemLeakWatcher.h"
#include "WindowsLeakWatcher.h"

#define QUEUED_EVENTS 1

static CGUIInputRZA2M* s_pkInputDevice = NULL;

// callback for touch-events
extern "C" void touchevent_callback(int_t nId, TP_TouchEvent_st* psTouchEvt)
{
    if (NULL != s_pkInputDevice)
    {
        s_pkInputDevice->AddEvent(psTouchEvt->sFinger[0]);
    }
}

CGUIInputRZA2M::CGUIInputRZA2M(
    const eC_Int& iTouchScreenX,
    const eC_Int& iTouchScreenY,
    const eC_Int& iTouchScreenWidth,
    const eC_Int& iTouchScreenHeight,
    const eC_Int& iTouchFilterDelta,
    const eC_UInt& uiIntPriority,
    const eC_Int& iTaskPriority) :
    m_bEventReady(false),
    m_iTouchScreenX(iTouchScreenX),
    m_iTouchScreenY(iTouchScreenY),
    m_iTouchScreenWidth(iTouchScreenWidth),
    m_iTouchScreenHeight(iTouchScreenHeight),
    m_iTouchFilterDelta(iTouchFilterDelta),
    m_iLastDraggedX(INVALID_AXIS_VALUE),
    m_iLastDraggedY(INVALID_AXIS_VALUE),
	m_eLastTouchEvent(TPEVT_ENTRY_NONE),
    m_iLastPositionX(INVALID_AXIS_VALUE),
    m_iLastPositionY(INVALID_AXIS_VALUE),
    m_uiIntPriority(uiIntPriority),
    m_iTaskPriority(iTaskPriority)
{
    if (true == m_kSemaphore.Create("Semaphore"))
    {
        GUILOG(GUI_TRACE_DEBUG, "CGUIInputRZA2M: Semaphore created\n");
    }
    else
    {
        GUILOG(GUI_TRACE_DEBUG, "CGUIInputRZA2M: Semaphore NOT created\n");
    }

    s_pkInputDevice = this;
}

CGUIInputRZA2M::~CGUIInputRZA2M()
{
}

void CGUIInputRZA2M::CreateInstance(
    const eC_Int& iTouchScreenX,
    const eC_Int& iTouchScreenY,
    const eC_Int& iTouchScreenWidth,
    const eC_Int& iTouchScreenHeight,
    const eC_Int& iTouchFilterDelta,
    const eC_UInt& uiIntPriority,
    const eC_Int& iTaskPriority)
{
    if (!CGUIComponentManager::GetInstance().HasInputMedia())
    {
        CGUIComponentManager::GetInstance().SetInputMedia(new CGUIInputRZA2M(
            iTouchScreenX, iTouchScreenY,
            iTouchScreenWidth, iTouchScreenHeight,
            iTouchFilterDelta, uiIntPriority, iTaskPriority));        
    }
}

void CGUIInputRZA2M::AddEvent(TP_TouchFinger_st kTouchEvent)
{
    eC_Bool bCreateEvent = false;

    if (
        (m_eLastTouchEvent != kTouchEvent.eState) ||
        (abs(m_iLastPositionX - kTouchEvent.unPosX) > m_iTouchFilterDelta) ||
        (abs(m_iLastPositionY - kTouchEvent.unPosY) > m_iTouchFilterDelta)
        )
    {
        switch (kTouchEvent.eState)
        {
        case TPEVT_ENTRY_UP:
            m_iLastDraggedX = m_iLastDraggedY = INVALID_AXIS_VALUE;
            m_iLastPositionX = m_iLastPositionY = INVALID_AXIS_VALUE;
            break;

        case TPEVT_ENTRY_DOWN:
            m_iLastPositionX = kTouchEvent.unPosX;
            m_iLastPositionY = kTouchEvent.unPosY;
            break;

        case TPEVT_ENTRY_MOVE:
            m_iLastPositionX = kTouchEvent.unPosX;
            m_iLastPositionY = kTouchEvent.unPosY;
            break;

        default:
            break;
        }
        m_eLastTouchEvent = kTouchEvent.eState;
        bCreateEvent = true;
    }

    if (true == bCreateEvent)
    {
        // add event
#if QUEUED_EVENTS == 1
        m_kSemaphore.Enter();
        m_kEventQueue.AddAtEnd(kTouchEvent);
        m_kSemaphore.Leave();
#else
        m_kLastEvent = kTouchEvent;
        m_bEventReady = true;
#endif
        StopIdle();
    }
}

CGUIEvent* CGUIInputRZA2M::GetEvent(eC_UInt uiIdleTime)
{
    CGUIEvent *pEvent = NULL;
    const eC_UInt uiSleepInterval = 10;
    const eC_UInt uiSleepCycles = uiIdleTime / uiSleepInterval;
    // wait for driver until new event arrives or Idle time reached
    if (uiIdleTime)
    {
        for (eC_UInt uiSleep = 0; uiSleep < uiSleepCycles; ++uiSleep)
        {
            eC_Wait(uiSleepInterval);
            
            if (m_bStopIdle)
                break;
#if QUEUED_EVENTS == 1
            if (m_kEventQueue.GetQuantity())
                break;
#else
            if (m_bEventReady)
                break;
#endif
        }
    }

    m_bStopIdle = false;
    GUI_TRY
    {
#if QUEUED_EVENTS == 1
        if (m_kEventQueue.GetQuantity() > 0)
        {
            m_kSemaphore.Enter();

            // Get the last event
            TP_TouchFinger_st kTouchEvent;
            memset(&kTouchEvent, 0, sizeof(TP_TouchFinger_st));
            m_kEventQueue.RemoveFront(kTouchEvent);

            if (kTouchEvent.eState == TPEVT_ENTRY_MOVE)
            {
                TP_TouchFinger_st kNextEvent;
                while (m_kEventQueue.GetFirst(kNextEvent))
                {
                    if (kNextEvent.eState == TPEVT_ENTRY_MOVE)
                    {
                        m_kEventQueue.RemoveFront(kNextEvent);
                        kTouchEvent = kNextEvent;
                    }
                    else
                    {
                        break;
                    }
                }
            }

            m_kSemaphore.Leave();

            switch (kTouchEvent.eState)
            {
            case TPEVT_ENTRY_UP:
                pEvent = new CGUIMouseEvent(ET_LBUTTONUP, kTouchEvent.unPosX, kTouchEvent.unPosY);
                break;

            case TPEVT_ENTRY_DOWN:
                pEvent = new CGUIMouseEvent(ET_LBUTTONDOWN, kTouchEvent.unPosX, kTouchEvent.unPosY, true);
                break;

            case TPEVT_ENTRY_MOVE:
                pEvent = new CGUIMouseEvent(ET_MOUSEMOVE, kTouchEvent.unPosX, kTouchEvent.unPosY, true, false);
                break;

            default:
                break;
            }
        }
#else
        if (true == m_bEventReady)
        {
            switch (m_kLastEvent.eState)
            {
            case TPEVT_ENTRY_UP:
                pEvent = new CGUIMouseEvent(ET_LBUTTONUP, m_kLastEvent.unPosX, m_kLastEvent.unPosY);
                break;

            case TPEVT_ENTRY_DOWN:
                pEvent = new CGUIMouseEvent(ET_LBUTTONDOWN, m_kLastEvent.unPosX, m_kLastEvent.unPosY, true);
                break;

            case TPEVT_ENTRY_MOVE:
                pEvent = new CGUIMouseEvent(ET_MOUSEMOVE, m_kLastEvent.unPosX, m_kLastEvent.unPosY, true, false);
                break;

            default:
                break;
            }
            m_bEventReady = false;
        }
#endif
    }
    GUI_CATCH_ALL
    {
        GUILOG(GUI_TRACE_ERROR, "CGUIInputRZA2M: something went wrong while acquiring event.\n");
    }
    return pEvent;
}
