module lglcd;

debug import std.stdio;
import std.string;
import win32.windows;

private HMODULE libHandle;
private bool _isAvailable = false;

bool isAvailable() { return _isAvailable; }

const LGLCD_INVALID_CONNECTION = -1; ///
const LGLCD_INVALID_DEVICE = -1; ///

const LGLCDBUTTON_BUTTON0 = 0x00000001; ///
const LGLCDBUTTON_BUTTON1 = 0x00000002; ///
const LGLCDBUTTON_BUTTON2 = 0x00000004; ///
const LGLCDBUTTON_BUTTON3 = 0x00000008; ///

const LGLCD_BMP_FORMAT_160x43x1 = 0x00000001; ///
const LGLCD_BMP_WIDTH = 160; ///
const LGLCD_BMP_HEIGHT = 43; ///

const LGLCD_PRIORITY_IDLE_NO_SHOW = 0; ///
const LGLCD_PRIORITY_BACKGROUND = 64; ///
const LGLCD_PRIORITY_NORMAL = 128; ///
const LGLCD_PRIORITY_ALERT = 255; ///
uint  LGLCD_SYNC_UPDATE(uint priority) {    return 0x80000000 | priority;} ///
uint  LGLCD_SYNC_COMPLETE_WITHIN_FRAME(uint priority) { return 0xC0000000 | priority; } ///
uint  LGLCD_ASYNC_UPDATE(uint priority) { return priority; } ///
const LGLCD_LCD_FOREGROUND_APP_NO = 0; ///
const LGLCD_LCD_FOREGROUND_APP_YES = 1; ///

extern(Windows)
{
    alias DWORD function(int, DWORD, PVOID) lgLcdOnSoftButtonsCB; ///
    alias DWORD function(int, PVOID) lgLcdOnConfigureCB; ///
}

///
struct lgLcdDeviceDesc
{
    DWORD Width; ///
    DWORD Height; ///
    DWORD Bpp; ///
    DWORD NumSoftButtons; ///
}

///
struct lgLcdBitmapHeader
{
    DWORD Format = LGLCD_BMP_FORMAT_160x43x1; ///
}

///
struct lgLcdBitmap160x43x1
{
    lgLcdBitmapHeader   hdr; ///
    ubyte[6880]         pixels = 0x00;///
}

///
struct lgLcdConfigureContext
{
    lgLcdOnConfigureCB  configCallback; ///
    PVOID               configContext; ///
}

///
struct lgLcdConnectContextA
{
    LPCSTR                  appFriendlyName; ///
    BOOL                    isPersistent; ///
    BOOL                    isAutostartable; ///
    lgLcdConfigureContext   onConfigure; ///
    int                     connection; ///
}

///
struct lgLcdConnectContextW
{
    LPCWSTR                 appFriendlyName; ///
    BOOL                    isPersistent; ///
    BOOL                    isAutostartable; ///
    lgLcdConfigureContext   onConfigure; ///
    int                     connection; ///
}

alias lgLcdConnectContextW lgLcdConnectContext; ///

///
struct lgLcdSoftbuttonsChangedContext
{
    lgLcdOnSoftButtonsCB    softbuttonsChangedCallback; ///
    PVOID                   softbuttonsChangedContext; ///
}

///
struct lgLcdOpenContext
{
    int                             connection; ///
    int                             index; ///
    lgLcdSoftbuttonsChangedContext  onSoftbuttonsChanged; ///
    int                             device; ///
}

extern(Windows)
{
    DWORD function () lgLcdInit;
    DWORD function () lgLcdDeInit;
    DWORD function (lgLcdConnectContextW*) lgLcdConnectW;
    DWORD function (lgLcdConnectContextA*) lgLcdConnectA;
    DWORD function (int) lgLcdDisconnect;
    DWORD function (int, int, lgLcdDeviceDesc*) lgLcdEnumerate;
    DWORD function (lgLcdOpenContext*) lgLcdOpen;
    DWORD function (int) lgLcdClose;
    DWORD function (int, DWORD*) lgLcdReadSoftButtons;
    DWORD function (int, lgLcdBitmapHeader*, DWORD) lgLcdUpdateBitmap;
}

private void init_lglcd()
{
    scope(failure)
    {
        debug writefln("error: ", GetLastError());
    }

    auto libHandle = LoadLibraryA(toStringz("lglcd.dll"));

    if (libHandle is null)
        throw new Exception(`could not load dynamic library lglcd.dll`);

    lgLcdInit = cast(typeof(lgLcdInit)) GetProcAddress(libHandle, `lgLcdInit`);

    if (lgLcdInit is null) throw new Exception(`could not load procedure lgLcdInit`);
    lgLcdDeInit = cast(typeof(lgLcdDeInit)) GetProcAddress(libHandle, `lgLcdDeInit`);
    if (lgLcdDeInit is null) throw new Exception(`could not load procedure lgLcdDeInit`);
    lgLcdConnectW = cast(typeof(lgLcdConnectW)) GetProcAddress(libHandle, `lgLcdConnectW`);
    if (lgLcdConnectW is null) throw new Exception(`could not load procedure lgLcdConnectW`);
    lgLcdConnectA = cast(typeof(lgLcdConnectA)) GetProcAddress(libHandle, `lgLcdConnectA`);
    if (lgLcdConnectA is null) throw new Exception(`could not load procedure lgLcdConnectA`);
    lgLcdDisconnect = cast(typeof(lgLcdDisconnect)) GetProcAddress(libHandle, `lgLcdDisconnect`);
    if (lgLcdDisconnect is null) throw new Exception(`could not load procedure lgLcdDisconnect`);
    lgLcdEnumerate = cast(typeof(lgLcdEnumerate)) GetProcAddress(libHandle, `lgLcdEnumerate`);
    if (lgLcdEnumerate is null) throw new Exception(`could not load procedure lgLcdEnumerate`);
    lgLcdOpen = cast(typeof(lgLcdOpen)) GetProcAddress(libHandle, `lgLcdOpen`);
    if (lgLcdOpen is null) throw new Exception(`could not load procedure lgLcdOpen`);
    lgLcdClose = cast(typeof(lgLcdClose)) GetProcAddress(libHandle, `lgLcdClose`);
    if (lgLcdClose is null) throw new Exception(`could not load procedure lgLcdClose`);
    lgLcdReadSoftButtons = cast(typeof(lgLcdReadSoftButtons)) GetProcAddress(libHandle, `lgLcdReadSoftButtons`);
    if (lgLcdReadSoftButtons is null) throw new Exception(`could not load procedure lgLcdReadSoftButtons`);
    lgLcdUpdateBitmap = cast(typeof(lgLcdUpdateBitmap)) GetProcAddress(libHandle, `lgLcdUpdateBitmap`);
    if (lgLcdUpdateBitmap is null) throw new Exception(`could not load procedure lgLcdUpdateBitmap`);

    if (lgLcdInit() == ERROR_SUCCESS)
        _isAvailable = true;
}

static this()
{
    try
    {
        init_lglcd();
    }
    catch(Object o)
    {
        debug writefln("exception while loading lglcd", o);
    }
}

static ~this()
{
    if (isAvailable)
    {
        debug writefln("deinitializing lglcd");
        lgLcdDeInit();
        debug writefln("unloading dynamic library (", libHandle, ")");
        FreeLibrary(libHandle);
    }
}








