
#include "common.h"
#include "ManipulationEvents.h"
#include "GlassView.h"
#include "ViewContainer.h"

// Required for some _CLSID and _IID declarations
#include <manipulations_i.c>

namespace {

template <class IFace>
HRESULT Connect(_IManipulationEvents* sink, IFace *cp)
{
    ASSERT(cp);

    HRESULT hr = S_OK;

    IConnectionPointContainer* spConnectionContainer;
    hr = cp->QueryInterface(IID_IConnectionPointContainer,
            (LPVOID*)&spConnectionContainer);
    if (!spConnectionContainer){
        // TODO: report an error and return
        return hr;
    }

    IConnectionPoint* pConnPoint;
    hr = spConnectionContainer->FindConnectionPoint(
            __uuidof(_IManipulationEvents), &pConnPoint);
    if (!pConnPoint) {
        spConnectionContainer->Release();
        // TODO: report an error and return
        return hr;
    }

    DWORD dwCookie;
    hr = pConnPoint->Advise(sink, &dwCookie);
    
    spConnectionContainer->Release();
    pConnPoint->Release();

    return hr;
}

} // namespace


ManipulationEventSink::ManipulationEventSink(IManipulationProcessor *manip, 
                                             ViewContainer *window, HWND hwnd)
    : m_window(window)
    , m_hwnd(hwnd)
    , m_isInertia(false)
{
    Connect(this, manip);
}

ManipulationEventSink::ManipulationEventSink(IInertiaProcessor *inertia, 
                                             ViewContainer *window, HWND hwnd)
    : m_window(window)
    , m_hwnd(hwnd)
    , m_isInertia(true)
{
    Connect(this, inertia);
}

ManipulationEventSink::~ManipulationEventSink()
{
}

HRESULT STDMETHODCALLTYPE ManipulationEventSink::ManipulationStarted(
    FLOAT x, FLOAT y)
{
    // NOP by design.
    return S_OK;
}

HRESULT STDMETHODCALLTYPE ManipulationEventSink::ManipulationDelta(
    FLOAT x, FLOAT y, FLOAT deltaX, FLOAT deltaY,
    FLOAT scaleDelta, FLOAT expansionDelta, FLOAT rotationDelta,
    FLOAT cumulativeDeltaX, FLOAT cumulativeDeltaY,
    FLOAT cumulativeScale, FLOAT cumulativeExpansion, FLOAT cumulativeRotation)
{
    // TBD: set to 'true' if source device is a touch screen 
    // and to 'false' if source device is a touch pad.
    // So far assume source device on Windows is always a touch screen.
    const bool isDirect = true;

    m_window->NotifyGesturePerformed(m_hwnd, isDirect, m_isInertia, 
        x, y, deltaX, deltaY,
        scaleDelta, expansionDelta, rotationDelta,
        cumulativeDeltaX, cumulativeDeltaY,
        cumulativeScale, cumulativeExpansion, cumulativeRotation);
    return S_OK;
}

HRESULT STDMETHODCALLTYPE ManipulationEventSink::ManipulationCompleted(
    FLOAT x, FLOAT y,
    FLOAT cumulativeDeltaX, FLOAT cumulativeDeltaY,
    FLOAT cumulativeScale, FLOAT cumulativeExpansion, FLOAT cumulativeRotation)
{
    // NOP by design.
    return S_OK;
}

HRESULT STDMETHODCALLTYPE ManipulationEventSinkWithInertia::ManipulationStarted(
    FLOAT x, FLOAT y)
{
    m_window->StopTouchInputInertia(m_hwnd);
    return S_OK;
}

HRESULT STDMETHODCALLTYPE ManipulationEventSinkWithInertia::ManipulationCompleted(
    FLOAT x, FLOAT y,
    FLOAT cumulativeTranslationX, FLOAT cumulativeTranslationY,
    FLOAT cumulativeScale, FLOAT cumulativeExpansion, FLOAT cumulativeRotation)
{
    m_window->StartTouchInputInertia(m_hwnd);
    return S_OK;
}
