#define WIN32_MEAN_AND_LEAN

/* XS code for Win32::GUI::DropFiles
 * $Id: DropFiles.xs,v 1.1 2006/04/25 21:38:18 robertemay Exp $
 * (c) Robert May, 2006
 * Released under the same terms as Perl
 */

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "ppport.h"

#include "windows.h"
#include "shellapi.h"

/* void newSVpvnW(SV* s, WCHAR* w, UINT c)
 *  - s [OUT] pointer to SV. Will be set to point to a newly created SV,
 *    with ref count 1.
 *  - w [IN]  pointer to WCHAR buffer.
 *  - c [IN]  number of characters (NOT bytes) to be copied from WCHAR.  Do
 *    not include any NULL termination. If c = -1, then length will be
 *    calculated, assuming w is NULL terminated.
 */
/* TODO: This macro probably better written as a function that returns the
 * pointer to the SV, and it is more 'perl like' if c==0 indicates the length
 * should be calculated. It would be good to get rid of the duplicated SvPVX() calls.
 */
#define newSVpvnW(s, w, c) \
    { UINT b = WideCharToMultiByte(CP_UTF8, 0, w, c, NULL, 0, NULL, NULL); \
      s = newSV(b); SvPOK_on(s); SvUTF8_on(s); SvCUR_set(s,b); \
      WideCharToMultiByte(CP_UTF8, 0, w, c, SvPVX(s), b, NULL, NULL); \
      *(SvPVX(s) + b) = 0; sv_utf8_downgrade(s, 1); }

/* BOOL INVALID_HANDLE(HDROP h)
 * Attempt to determine if a HDROP handle is valid
 * Returns TRUE  if handle is invalid
 * Returns FALSE if handle is valid
 * TODO: can we do better than this?
 */
BOOL INVALID_HANDLE(HDROP h) {
    if(GlobalLock((HGLOBAL)h)) {
        GlobalUnlock((HGLOBAL)h);
        return 0;
    }
    return 1;
}

#ifndef W32G_NO_WIN9X
/* BOOL IsWin9X()
 * Returns TRUE  if OS Version is Win95/98/ME
 * Returns FLASE if OS Version is NT/2K/XP/2003 or higher
 */
/* TODO: Better to cache the value to prevent the overhead of
 * GetVersion() on each call.  Eventually this needs extracting
 * somewhere central, so that we don't have repeat implementations
 * all over the place.  ??Can we efficiently access the Win32::IsWin95
 * function??
 */
BOOL IsWin9X() {
    return (GetVersion() & 0x80000000);
}
#endif

MODULE = Win32::GUI::DropFiles        PACKAGE = Win32::GUI::DropFiles

PROTOTYPES: ENABLE

     ##########################################################################
     # (@)WIN32API:DragQueryFile(HDROP, [ITEM])
     # See Dropfiles.pm for documentation
void DragQueryFile(handle, ...)
    HDROP handle
PREINIT:
    UINT count, item, cch;
    SV* sv;
PPCODE:
    /* Shell32.dll crashes if we pass an invalid handle
     * to DragQueryFile, so ensure we have one
     */
    if(INVALID_HANDLE(handle)) {
        SetLastError(ERROR_INVALID_HANDLE); /* set $^E */
        errno = EINVAL;                     /* set $! */
        XSRETURN_UNDEF;                     /* and return undef */
    }
#ifndef W32G_NO_WIN9X
    if(IsWin9X())
        count = DragQueryFileA(handle, 0xFFFFFFFF, NULL, 0);
    else
#endif
        count = DragQueryFileW(handle, 0xFFFFFFFF, NULL, 0);

    if(items == 1) {
        mXPUSHu(count);
        XSRETURN(1);
    } else if (items == 2) {
        item = (UINT)SvIV(ST(1));
        if(item < count) {  /* item is in range */
#ifndef W32G_NO_WIN9X
            if(IsWin9X()) {
                CHAR buffer[MAX_PATH];

                cch = DragQueryFileA(handle, item, buffer, MAX_PATH);
                sv = newSVpvn(buffer,cch);
            } else {
#endif
                WCHAR wbuffer[MAX_PATH];

                cch = DragQueryFileW(handle, item, wbuffer, MAX_PATH);
                newSVpvnW(sv, wbuffer, cch);
#ifndef W32G_NO_WIN9X
            }
#endif
            XPUSHs(sv_2mortal(sv));
            XSRETURN(1);
        } else {                               /* item is out of range */
            SetLastError(ERROR_INVALID_INDEX); /* set $^E */
            errno = EINVAL;                    /* set $! */
            XSRETURN_UNDEF;                    /* and return undef */
        }
    } else {
        croak("Usage: DragQueryHandle(handle);\n   or: DragQueryHandle(handle, index);");
    }

     ##########################################################################
     # (@)WIN32API:DragQueryPoint(HDROP)
     # See Dropfiles.pm for documentation
void DragQueryPoint(handle)
    HDROP handle
PREINIT:
    POINT pt;
    UV client;
PPCODE:
    /* DragQueryPoint returns garbage if passed
     * an invalid handle, so ensure we have one
     */
    if(INVALID_HANDLE(handle)) {
        SetLastError(ERROR_INVALID_HANDLE); /* set $^E */
        errno = EINVAL;                     /* set $! */
        XSRETURN_UNDEF;                     /* and return undef */
    }
    client = (UV)DragQueryPoint(handle, &pt);
    mXPUSHi(pt.x);
    mXPUSHi(pt.y);
    mXPUSHu(client);
    XSRETURN(3);

     ##########################################################################
     # (@)WIN32API:DragFinish(HDROP)
     # See Dropfiles.pm for documentation
void
DragFinish(handle)
    HDROP handle