/*
 * Still Image class installer
 *
 * Copyright (C) 2005 Damjan Jovanovic
 * Copyright (C) 2006 Igor Farinic
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#define COBJMACROS
#include "sti_internal.h"

WINE_DEFAULT_DEBUG_CHANNEL(sti);

/* Method declarations */

static HRESULT WINAPI UNKNOWN_QueryInterface(IUnknown* This, REFIID riid,
                                             void** ppvObject);
static ULONG   WINAPI UNKNOWN_AddRef(IUnknown* This);
static ULONG   WINAPI UNKNOWN_Release(IUnknown* This);
static HRESULT WINAPI STIDEVCTL_QueryInterface(IStiDeviceControl* This,
                                               REFIID riid, void** ppvObject);
static ULONG   WINAPI STIDEVCTL_AddRef(IStiDeviceControl* This);
static ULONG   WINAPI STIDEVCTL_Release(IStiDeviceControl* This);
static HRESULT WINAPI STIDEVCTL_Initialize(IStiDeviceControl *This,
                                           DWORD dwDeviceType, DWORD dwMode,
                                           LPCWSTR pwszPortName, DWORD dwFlags);
/*static HRESULT WINAPI STIDEVCTL_RawReadData(IStiDevice* This, LPVOID lpBuffer,
                                            DWORD* lpdwNumberOfBytes,
                                            OVERLAPPED* lpOverlapped);*/
static HRESULT WINAPI STIDEVCTL_RawReadData(IStiDeviceControl *pDevCtl,
                                            LPVOID lpBuffer,
                                            DWORD *lpdwNumberOfBytes,
                                            OVERLAPPED *lpOverlapped);
/*static HRESULT WINAPI STIDEVCTL_RawWriteData(IStiDevice* This, LPVOID lpBuffer,
                                             DWORD nNumberOfBytes,
                                             OVERLAPPED* lpOverlapped);
*/
static HRESULT WINAPI STIDEVCTL_RawWriteData(IStiDeviceControl *pDevCtl,
                                             LPVOID lpBuffer,
                                             DWORD nNumberOfBytes,
                                             OVERLAPPED *lpOverlapped);
/*static HRESULT WINAPI STIDEVCTL_RawReadCommand(IStiDevice* This, LPVOID lpBuffer,

                                               DWORD* lpdwNumberOfBytes,
                                               OVERLAPPED* lpOverlapped);
*/
static HRESULT WINAPI STIDEVCTL_RawReadCommand(IStiDeviceControl *pDevCtl,
                                               LPVOID lpBuffer,
                                               DWORD *lpdwNumberOfBytes,
                                               OVERLAPPED *lpOverlapped);


/*static HRESULT WINAPI STIDEVCTL_RawWriteCommand(IStiDevice* This,
                                                LPVOID lpBuffer,
                                                DWORD nNumberOfBytes,
                                                OVERLAPPED* lpOverlapped);*/
static HRESULT WINAPI STIDEVCTL_RawWriteCommand(IStiDeviceControl *pDevCtl,
                                                LPVOID lpBuffer,
                                                DWORD nNumberOfBytes,
                                                OVERLAPPED *lpOverlapped);

/*static HRESULT WINAPI STIDEVCTL_RawDeviceControl(IStiDevice *This,
                                                 STI_RAW_CONTROL_CODE EscapeFunction,
                                                 LPVOID lpInData, DWORD cbInDataSize,
                                                 LPVOID pOutData, DWORD dwOutDataSize,
                                                 DWORD* pdwActualData);*/
static HRESULT WINAPI STIDEVCTL_RawDeviceControl(IStiDeviceControl *pDevCtl,
                                                  STI_RAW_CONTROL_CODE EscapeFunction,
                                                  LPVOID lpInData, DWORD cbInDataSize,
                                                  LPVOID pOutData, DWORD dwOutDataSize,
                                                  DWORD *pdwActualData);
/*static HRESULT WINAPI STIDEVCTL_GetLastError(IStiDevice *This,
                                             LPDWORD lpdwLastError);*/
static HRESULT WINAPI STIDEVCTL_GetLastError(IStiDeviceControl *pDevCtl,
                                             LPDWORD lpdwLastError);
static HRESULT WINAPI STIDEVCTL_GetMyDevicePortName(IStiDeviceControl *This,
                                                    LPWSTR lpszDevicePath,
                                                    DWORD cwDevicePathSize);
static HRESULT WINAPI STIDEVCTL_GetMyDeviceHandle(IStiDeviceControl *This,
                                                  LPHANDLE lph);
static HRESULT WINAPI STIDEVCTL_GetMyDeviceOpenMode(IStiDeviceControl *This,
                                                    LPDWORD pdwOpenMode);
static HRESULT WINAPI STIDEVCTL_WriteToErrorLog(IStiDeviceControl *This,
                                                DWORD dwMessageType,
                                                LPCWSTR pszMessage,
                                                DWORD dwErrorCode);

/* Virtual method tables */

static IUnknownVtbl UnknownVtbl = {
    UNKNOWN_QueryInterface,
    UNKNOWN_AddRef,
    UNKNOWN_Release
};

static IStiDeviceControlVtbl StiDeviceControlVtbl = {
    STIDEVCTL_QueryInterface,
    STIDEVCTL_AddRef,
    STIDEVCTL_Release,
    STIDEVCTL_Initialize,
    STIDEVCTL_RawReadData,
    STIDEVCTL_RawWriteData,
    STIDEVCTL_RawReadCommand,
    STIDEVCTL_RawWriteCommand,
    STIDEVCTL_RawDeviceControl,
    STIDEVCTL_GetLastError,
    STIDEVCTL_GetMyDevicePortName,
    STIDEVCTL_GetMyDeviceHandle,
    STIDEVCTL_GetMyDeviceOpenMode,
    STIDEVCTL_WriteToErrorLog
};

/* Methods */

static HRESULT WINAPI UNKNOWN_QueryInterface(IUnknown* pUnk, REFIID riid,
                                                        void** ppvObject)
{
    StiDeviceControl *This = IMPL(StiDeviceControl, iUnknown, pUnk);
    TRACE("(%p, %p, %p)\n", pUnk, riid, ppvObject);
    *ppvObject = 0;
    if (IsEqualIID(riid, &IID_IUnknown))
        *ppvObject = &This->iUnknown;
    else if (IsEqualIID(riid, &IID_IStiDeviceControl))
        *ppvObject = &This->iStiDeviceControl;
    else
        return E_NOINTERFACE;
    UNKNOWN_AddRef(pUnk);
    return NOERROR;
}

static ULONG WINAPI UNKNOWN_AddRef(IUnknown* pUnk)
{
    StiDeviceControl *This = IMPL(StiDeviceControl, iUnknown, pUnk);
    TRACE("(%p)\n", pUnk);
    return ++This->m_refCount;
}

static ULONG WINAPI UNKNOWN_Release(IUnknown* pUnk)
{
    StiDeviceControl *This = IMPL(StiDeviceControl, iUnknown, pUnk);
    TRACE("(%p)\n", pUnk);
    --This->m_refCount;
    if (This->m_refCount == 0) {
        free(This);
        return 0;
    } else
        return This->m_refCount;
}

static HRESULT WINAPI STIDEVCTL_QueryInterface(IStiDeviceControl* pDevCtl,
                                                          REFIID riid, void** ppvObject)
{
    StiDeviceControl *This = IMPL(StiDeviceControl, iStiDeviceControl, pDevCtl);
    TRACE("(%p, %p, %p)\n", pDevCtl, riid, ppvObject);
    if (This->m_punkOuter)
        return This->m_punkOuter->lpVtbl->QueryInterface(This->m_punkOuter, riid,
                                                         ppvObject);
    else
        return UNKNOWN_QueryInterface(&This->iUnknown, riid, ppvObject);
}

static ULONG WINAPI STIDEVCTL_AddRef(IStiDeviceControl* pDevCtl)
{
    StiDeviceControl *This = IMPL(StiDeviceControl, iStiDeviceControl, pDevCtl);
    TRACE("(%p)\n", pDevCtl);
    if (This->m_punkOuter)
        return This->m_punkOuter->lpVtbl->AddRef(This->m_punkOuter);
    else
        return UNKNOWN_AddRef(&This->iUnknown);
}

static ULONG WINAPI STIDEVCTL_Release(IStiDeviceControl* pDevCtl)
{
    StiDeviceControl *This = IMPL(StiDeviceControl, iStiDeviceControl, pDevCtl);
    TRACE("(%p)\n", pDevCtl);
    if (This->m_punkOuter)
        return This->m_punkOuter->lpVtbl->Release(This->m_punkOuter);
    else
        return UNKNOWN_Release(&This->iUnknown);
}

static HRESULT WINAPI STIDEVCTL_Initialize(IStiDeviceControl *pDevCtl,
                                           DWORD dwDeviceType, DWORD dwMode,
                                           LPCWSTR pwszPortName, DWORD dwFlags)
{
    StiDeviceControl *This = IMPL(StiDeviceControl, iStiDeviceControl, pDevCtl);
    TRACE("(%p, %lu, %lu, %s, %lu)\n", pDevCtl, dwDeviceType, dwMode,
          debugstr_w(pwszPortName), dwFlags);
    This->m_openMode = dwMode;
    lstrcpynW(This->m_portName, pwszPortName, STI_MAX_INTERNAL_NAME_LENGTH);
    return STI_OK;
}

static HRESULT WINAPI STIDEVCTL_RawReadData(IStiDeviceControl *pDevCtl,
                                            LPVOID lpBuffer,
                                            DWORD *lpdwNumberOfBytes,
                                            OVERLAPPED *lpOverlapped)
{
    DWORD ret;
    //StiDevice *This = IMPL(StiDevice, iStiDevice, pDevCtl);
    StiDeviceControl *This = IMPL(StiDeviceControl, iStiDeviceControl, pDevCtl);
    TRACE("(%p, %p, %p, %p)\n", pDevCtl, lpBuffer, lpdwNumberOfBytes, lpOverlapped);
    ret = ReadFile(This->m_fileHandle, lpBuffer, *lpdwNumberOfBytes, lpdwNumberOfBytes,
                   lpOverlapped);
    return ret;
}

static HRESULT WINAPI STIDEVCTL_RawWriteData(IStiDeviceControl *pDevCtl,
                                             LPVOID lpBuffer,
                                             DWORD nNumberOfBytes,
                                             OVERLAPPED *lpOverlapped)
{
    DWORD ret;
    //StiDevice *This = IMPL(StiDevice, iStiDevice, pDevCtl);
    StiDeviceControl *This = IMPL(StiDeviceControl, iStiDeviceControl, pDevCtl);
    TRACE("(%p, %p, %lu, %p)\n", pDevCtl, lpBuffer, nNumberOfBytes, lpOverlapped);
    ret = WriteFile(This->m_fileHandle, lpBuffer, nNumberOfBytes, &nNumberOfBytes,
                    lpOverlapped);
    return ret;
}

static HRESULT WINAPI STIDEVCTL_RawReadCommand(IStiDeviceControl *pDevCtl,
                                               LPVOID lpBuffer,
                                               DWORD *lpdwNumberOfBytes,
                                               OVERLAPPED *lpOverlapped)
{
    DWORD ret;
    //StiDevice *This = IMPL(StiDevice, iStiDevice, pDevCtl);
    StiDeviceControl *This = IMPL(StiDeviceControl, iStiDeviceControl, pDevCtl);
    TRACE("(%p, %p, %p, %p)\n", pDevCtl, lpBuffer, lpdwNumberOfBytes, lpOverlapped);
    ret = ReadFile(This->m_fileHandle, lpBuffer, *lpdwNumberOfBytes, lpdwNumberOfBytes,
                   lpOverlapped);
    return ret;
}

static HRESULT WINAPI STIDEVCTL_RawWriteCommand(IStiDeviceControl *pDevCtl,
                                                LPVOID lpBuffer,
                                                DWORD nNumberOfBytes,
                                                OVERLAPPED *lpOverlapped)
{
    DWORD ret;
    //StiDevice *This = IMPL(StiDevice, iStiDevice, pDevCtl);
    StiDeviceControl *This = IMPL(StiDeviceControl, iStiDeviceControl, pDevCtl);
    TRACE("(%p, %p, %lu, %p)\n", pDevCtl, lpBuffer, nNumberOfBytes, lpOverlapped);
    ret = WriteFile(This->m_fileHandle, lpBuffer, nNumberOfBytes, &nNumberOfBytes,
                    lpOverlapped);
    return ret;
}

static HRESULT WINAPI STIDEVCTL_RawDeviceControl(IStiDeviceControl *pDevCtl,
                                                  STI_RAW_CONTROL_CODE EscapeFunction,
                                                  LPVOID lpInData, DWORD cbInDataSize,
                                                  LPVOID pOutData, DWORD dwOutDataSize,
                                                  DWORD *pdwActualData)
{
    HRESULT ret;
    //StiDevice *This = IMPL(StiDevice, iStiDevice, pDevCtl);
    StiDeviceControl *This = IMPL(StiDeviceControl, iStiDeviceControl, pDevCtl);
    TRACE("(%p, %lu, %p, %lu, %p, %lu, %p)\n", pDevCtl, EscapeFunction,
          lpInData, cbInDataSize, pOutData, dwOutDataSize, pdwActualData);
    ret = DeviceIoControl(This->m_fileHandle, EscapeFunction,
                          lpInData, cbInDataSize,
                          pOutData, dwOutDataSize,
                          pdwActualData,
			  NULL);
    return ret;
}

static HRESULT WINAPI STIDEVCTL_GetLastError(IStiDeviceControl *pDevCtl,
                                             LPDWORD lpdwLastError)
{
    //StiDevice *This = IMPL(StiDevice, iStiDevice, pDevCtl);
    StiDeviceControl *This = IMPL(StiDeviceControl, iStiDeviceControl, pDevCtl);
    TRACE("(%p, %p)\n", pDevCtl, lpdwLastError);
    *lpdwLastError = This->m_lastError;
    return STI_OK;
}

static HRESULT WINAPI STIDEVCTL_GetMyDeviceOpenMode(IStiDeviceControl *pDevCtl,
                                                    LPDWORD pdwOpenMode)
{
    StiDeviceControl *This = IMPL(StiDeviceControl, iStiDeviceControl, pDevCtl);
    TRACE("(%p, %p)\n", pDevCtl, pdwOpenMode);
    *pdwOpenMode = This->m_openMode;
    return STI_OK;
}

static HRESULT WINAPI STIDEVCTL_GetMyDevicePortName(IStiDeviceControl *pDevCtl,
                                                    LPWSTR lpszDevicePath,
                                                    DWORD cwDevicePathSize)
{
    StiDeviceControl *This = IMPL(StiDeviceControl, iStiDeviceControl, pDevCtl);
    TRACE("(%p, %p, %lu)\n", pDevCtl, lpszDevicePath, cwDevicePathSize);
    lstrcpynW(lpszDevicePath, This->m_portName, cwDevicePathSize);
    if (strlenW(This->m_portName)*sizeof(WCHAR) >= cwDevicePathSize)
        return STIERR_OUTOFMEMORY;
    else
        return STI_OK;
}

static HRESULT WINAPI STIDEVCTL_GetMyDeviceHandle(IStiDeviceControl *pDevCtl,
                                                  LPHANDLE lph)
{
    StiDeviceControl *This = IMPL(StiDeviceControl, iStiDeviceControl, pDevCtl);
    TRACE("(%p, %p)\n", pDevCtl, lph);
    *lph = This->m_fileHandle;
    return STI_OK;
}

static HRESULT WINAPI STIDEVCTL_WriteToErrorLog(IStiDeviceControl *This,
                                                DWORD dwMessageType,
                                                LPCWSTR pszMessage,
                                                DWORD dwErrorCode)
{
	return STI_OK;
}

HRESULT CreateStiDeviceControl(IUnknown *punkOuter, void **ppv)
{
    StiDeviceControl *stiDevCtl = (StiDeviceControl*) malloc(sizeof(StiDeviceControl));
    *ppv = 0;
    if (!stiDevCtl)
        return E_OUTOFMEMORY;
    memset(stiDevCtl, 0, sizeof(StiDeviceControl));
    stiDevCtl->iUnknown.lpVtbl = &UnknownVtbl;
    stiDevCtl->iStiDeviceControl.lpVtbl = &StiDeviceControlVtbl;
    stiDevCtl->m_refCount = 1;
    stiDevCtl->m_punkOuter = punkOuter;
    *ppv = &stiDevCtl->iUnknown;
    return NOERROR;
}
