import std.stdio;
import std.conv;
import dtwainapi;
import std.format;
import core.sys.windows.windows : OutputDebugStringW;
import std.utf : toUTF16z;

extern(Windows) ptrint myCallback64(ptrint wParam, ptrint lParam, long userData)
{
    // Cast the dll handle back to the right type
    auto theDLL = cast(DTWAIN_DynamicDLL*) userData;

    // Get the name of the notification using the DTWAIN utility function to get string versions
    // of most of the TWAIN-related and DTWAIN-related constants
	char [256] szNotification;
    auto len = theDLL.DTWAIN_GetTwainNameFromConstantA(theDLL.DTWAIN_CONSTANT_DTWAIN_TN, // The constant type
													   wParam,  // The actual constant value
													   cast(char *)szNotification, // name is returned here
													   256); // maximum size of the output buffer 

    immutable log = format("Notification=%s, lParam=%s\n", szNotification[0 .. len-1], lParam);

    // Log this to the debug monitor (the Output Window if you are using the Visual Studio IDE)
    OutputDebugStringW(toUTF16z(log));
    return 1;
}

void main()
{
    // Load the DTWAIN DLL, depending on the environemnt
    string defaultDLL = "dtwain64u.dll"; // 64-bit
	static if (ptrint.sizeof == 4) 
        defaultDLL = "dtwain32u.dll";    // 32-bit
	auto dll = new DTWAIN_DynamicDLL(defaultDLL);

    // Initialize DTWAIN
    auto dtwain_handle = dll.DTWAIN_SysInitialize();
    if ( !dtwain_handle )
	{
        writeln("DTWAIN failed initialization");
        return;
	}

    // Select a TWAIN source by using the enhanced 
	// TWAIN Select Source dialog (we can center it on the screen)
    auto TwainSource = dll.DTWAIN_SelectSource2W(null, "This is a test", 0, 0, dll.DTWAIN_DLG_CENTER_SCREEN);

    if ( !TwainSource )
	{
        writeln("No source was selected");
        return;
	}

    // Display the source product name
    char [256] szBuffer;
    auto len = dll.DTWAIN_GetSourceProductNameA(TwainSource, cast(char*)szBuffer, 256);

    // Example usage of DTWAIN_ARRAY
    // Get the device capabilities that are supported by the selected source
    DTWAIN_ARRAY capArray = null;
    dll.DTWAIN_EnumSupportedCaps(TwainSource, &capArray);

    // Get the number of items in the array
    int arrayCount = dll.DTWAIN_ArrayGetCount(capArray);

    writeln("There are ", arrayCount, " capabilities defined for device ", szBuffer[0 .. len-1]);

    // Now display the names of the capabilities
    // We can either use DTWAIN_ArrayGetAtLong() to retrieve each value
    // iteratively, but since we are using D, we can get a pointer to the 
    // array buffer and use simple indexing instead of incurring the call
    // to DTWAIN_ArrayGetAtLong() each time
    int* ptrCapArray = cast(int *)dll.DTWAIN_ArrayGetBuffer(capArray, 0);
    for (int i = 0; i < arrayCount; ++i)
	{
        // Get the name of the capability
        len = dll.DTWAIN_GetNameFromCapA(*ptrCapArray, cast(char*)szBuffer, 256);
        writeln("Capability ", i + 1, ": ", szBuffer[0 .. len-1], "  Value: ", *ptrCapArray);
        ++ptrCapArray; // Go to next cap in the array
	}

    // Now demonstrate setting up a callback for notification processing.
    // We must enable the notification "engine" first
    dll.DTWAIN_EnableMsgNotify(true);

    // Now set the callback
    auto userDataPtr = cast(long)&dll; // pass handle to the DLL as the user data
    dll.DTWAIN_SetCallback64(&myCallback64, userDataPtr);

    // Now let's acquire a page from the device and save to a BMP file
    dll.DTWAIN_AcquireFileA(TwainSource, "testd.bmp", 
							dll.DTWAIN_BMP, dll.DTWAIN_USELONGNAME,
                           dll.DTWAIN_PT_DEFAULT, 1,1,1, null);

    // Now close down DTWAIN.  You *must* do this when done using DTWAIN, so that resources are freed, and that
    // any callbacks you have set do not fire when DTWAIN is closed.
    dll.DTWAIN_SysDestroy();
}
