/* OCaml-Win32
 * win32_toolbar.c
 * Copyright (c) 2002-2004 by Harry Chomsky
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "win32.h"

/********************************************************************/
/* User Interface: Windows User Experience: Windows Shell: SDK: Windows Controls: Individual Control Information: Toolbar Controls */

static DWORD toolbar_extended_styles[] = {
    TBSTYLE_EX_DRAWDDARROWS
};
Define_static_constants(toolbar_extended_styles);

static DWORD toolbar_button_styles[] = {
    TBSTYLE_AUTOSIZE,
    TBSTYLE_CHECK,
    TBSTYLE_DROPDOWN,
    TBSTYLE_GROUP,
    TBSTYLE_NOPREFIX,
    TBSTYLE_SEP
};
Define_static_constants(toolbar_button_styles);

static DWORD toolbar_button_states[] = {
    TBSTATE_CHECKED,
    TBSTATE_ELLIPSES,
    TBSTATE_ENABLED,
    TBSTATE_HIDDEN,
    TBSTATE_INDETERMINATE,
    TBSTATE_MARKED,
    TBSTATE_PRESSED,
    TBSTATE_WRAP
};
Define_static_constants(toolbar_button_states);

static void tbbutton_val(value val, TBBUTTON* tbb)
{
    tbb->iBitmap = Int_val(Field(val, 0));
    tbb->idCommand = Int_val(Field(val, 1));
    tbb->fsState = (BYTE)flags_val(Field(val, 2), toolbar_button_states);
    tbb->fsStyle = (BYTE)flags_val(Field(val, 3), toolbar_button_styles);
    tbb->dwData = Int32_val(Field(val, 4));
    tbb->iString = Int_val(Field(val, 5));
}

static value alloc_tbbutton(TBBUTTON* tbb)
{
    CAMLparam0();
    CAMLlocal2(val, v);

    val = alloc_tuple(6);
    Store_field(val, 0, Val_int(tbb->iBitmap));
    Store_field(val, 1, Val_int(tbb->idCommand));
    v = Alloc_flags(tbb->fsState, toolbar_button_states);
    Store_field(val, 2, v);
    v = Alloc_flags(tbb->fsStyle, toolbar_button_styles);
    Store_field(val, 3, v);
    v = copy_int32(tbb->dwData);
    Store_field(val, 4, v);
    Store_field(val, 5, Val_int(tbb->iString));
    CAMLreturn(val);
}

static TBBUTTONINFO* new_tbbuttoninfo(value val)
{
    TBBUTTONINFO* tbbi;

    tbbi = malloc(sizeof(TBBUTTONINFO));
    memset(tbbi, 0, sizeof(TBBUTTONINFO));
    tbbi->cbSize = sizeof(TBBUTTONINFO);
    if (Is_some(Field(val, 0))) {
        tbbi->dwMask |= TBIF_COMMAND;
        tbbi->idCommand = Int_val(Value_of(Field(val, 0)));
    }
    if (Is_some(Field(val, 1))) {
        tbbi->dwMask |= TBIF_IMAGE;
        tbbi->iImage = commoncontrolimageindex_val(Value_of(Field(val, 1)));
    }
    if (Is_some(Field(val, 2))) {
        tbbi->dwMask |= TBIF_STATE;
        tbbi->fsState = (BYTE)flags_val(Value_of(Field(val, 2)), toolbar_button_states);
    }
    if (Is_some(Field(val, 3))) {
        tbbi->dwMask |= TBIF_STYLE;
        tbbi->fsStyle = (BYTE)flags_val(Value_of(Field(val, 3)), toolbar_button_styles);
    }
    if (Is_some(Field(val, 4))) {
        tbbi->dwMask |= TBIF_SIZE;
        tbbi->cx = Int_val(Value_of(Field(val, 4)));
    }
    if (Is_some(Field(val, 5))) {
        tbbi->dwMask |= TBIF_LPARAM;
        tbbi->lParam = Int32_val(Value_of(Field(val, 5)));
    }
    if (Is_some(Field(val, 6))) {
        tbbi->dwMask |= TBIF_TEXT;
        tbbi->pszText = new_string(Value_of(Field(val, 6)));
    }
    return tbbi;
}

static void free_tbbuttoninfo(TBBUTTONINFO* tbbi)
{
    if (tbbi->dwMask & TBIF_TEXT)
        free_string(tbbi->pszText);
    free(tbbi);
}

static value alloc_tbbuttoninfo(TBBUTTONINFO* tbbi)
{
    CAMLparam0();
    CAMLlocal3(val, v, vv);

    val = alloc_tuple(7);
    if (tbbi->dwMask & TBIF_COMMAND) {
        v = alloc_some(Val_int(tbbi->idCommand));
        Store_field(val, 0, v);
    } else
        Store_field(val, 0, Val_none);
    if (tbbi->dwMask & TBIF_IMAGE) {
        v = alloc_some(alloc_commoncontrolimageindex(tbbi->iImage));
        Store_field(val, 1, v);
    } else
        Store_field(val, 1, Val_none);
    if (tbbi->dwMask & TBIF_STATE) {
        vv = Alloc_flags(tbbi->fsState, toolbar_button_states);
        v = alloc_some(vv);
        Store_field(val, 2, v);
    } else
        Store_field(val, 2, Val_none);
    if (tbbi->dwMask & TBIF_STYLE) {
        vv = Alloc_flags(tbbi->fsStyle, toolbar_button_styles);
        v = alloc_some(vv);
        Store_field(val, 3, v);
    } else
        Store_field(val, 3, Val_none);
    if (tbbi->dwMask & TBIF_SIZE) {
        v = alloc_some(Val_int(tbbi->cx));
        Store_field(val, 4, v);
    } else
        Store_field(val, 4, Val_none);
    if (tbbi->dwMask & TBIF_LPARAM) {
        v = alloc_some(copy_int32(tbbi->lParam));
        Store_field(val, 5, v);
    } else
        Store_field(val, 5, Val_none);
    if (tbbi->dwMask & TBIF_TEXT) {
        v = alloc_some(copy_string(tbbi->pszText));
        Store_field(val, 6, v);
    } else
        Store_field(val, 6, Val_none);
    CAMLreturn(val);
}

static DWORD tbbuttoninfo_components_noparam[] = {
    TBIF_COMMAND,
    TBIF_IMAGE,
    TBIF_STATE,
    TBIF_STYLE,
    TBIF_SIZE,
    TBIF_LPARAM
};
Define_static_constants(tbbuttoninfo_components_noparam);

static DWORD toolbar_standard_bitmaps[] = {
    IDB_HIST_LARGE_COLOR,
    IDB_HIST_SMALL_COLOR,
    IDB_STD_LARGE_COLOR,
    IDB_STD_SMALL_COLOR,
    IDB_VIEW_LARGE_COLOR,
    IDB_VIEW_SMALL_COLOR
};
Define_static_constants(toolbar_standard_bitmaps);

static void toolbarbitmapspec_val(value val, HINSTANCE* hinst, UINT* id)
{
    switch (Tag_val(val)) {
        case 0:
            *hinst = HINST_COMMCTRL;
            *id = Constant_val(Field(val, 0), toolbar_standard_bitmaps);
            break;
        case 1:
            *hinst = Handle_val(Field(val, 0));
            *id = Int_val(Field(val, 1));
            break;
        case 2:
            *hinst = NULL;
            *id = (UINT)Handle_val(Field(val, 0));
            break;
    }
}

CAMLprim value create_mapped_bitmap(value inst, value id, value masked, value color_maps)
{
    CAMLparam4(inst, id, masked, color_maps);
    HINSTANCE h;
    int n;
    COLORMAP* p;
    HBITMAP hbm;
    CAMLlocal1(val);

    h = Handle_val(inst);
    n = list_length(color_maps);
    if (n > 0)
        p = malloc(n * sizeof(COLORMAP));
    else
        p = NULL;
    n = 0;
    for (n = 0; !Is_nil(color_maps); color_maps = Tail(color_maps)) {
        val = Head(color_maps);
        p[n].from = color_val(Field(val, 0));
        p[n].to = color_val(Field(val, 1));
        ++n;
    }
    enter_blocking_section();
    hbm = CreateMappedBitmap(h, Int_val(id), Bool_val(masked) ? CMB_MASKED : 0, p, n);
    leave_blocking_section();
    if (n > 0)
        free(p);
    if (!hbm)
        raise_last_error();
    val = alloc_handle(hbm);
    CAMLreturn(val);
}

CAMLprim value create_toolbar_ex_native(value parent, value style, value id, value num_bitmaps, value bitmap, value buttons, value button_size, value bitmap_size)
{
    CAMLparam5(parent, style, id, num_bitmaps, bitmap);
    CAMLxparam3(buttons, button_size, bitmap_size);
    LARGE_INTEGER li;
    HWND hparent;
    DWORD ws;
    HINSTANCE bitmap_inst;
    UINT bitmap_id;
    int cnt;
    TBBUTTON* p;
    SIZE button_sz, bitmap_sz;
    HWND hwnd;
    CAMLlocal1(val);

    hparent = Handle_val(parent);
    li.QuadPart = Int64_val(style);
    ws = li.LowPart;
    toolbarbitmapspec_val(bitmap, &bitmap_inst, &bitmap_id);
    cnt = list_length(buttons);
    if (cnt > 0)
        p = malloc(cnt * sizeof(TBBUTTON));
    else
        p = NULL;
    cnt = 0;
    for (; !Is_nil(buttons); buttons = Tail(buttons))
        tbbutton_val(Head(buttons), &p[cnt++]);
    button_sz = size_val(button_size);
    bitmap_sz = size_val(bitmap_size);
    enter_blocking_section();
    hwnd = CreateToolbarEx(
        hparent, ws, Int_val(id), Int_val(num_bitmaps), bitmap_inst, bitmap_id, p, cnt,
        button_sz.cx, button_sz.cy, bitmap_sz.cx, bitmap_sz.cy, sizeof(TBBUTTON));
    leave_blocking_section();
    if (cnt > 0)
        free(p);
    if (!hwnd)
        raise_last_error();
    val = alloc_handle(hwnd);
    CAMLreturn(val);
}

CAMLprim value create_toolbar_ex_bytecode(value* argv, int argn)
{
    return create_toolbar_ex_native(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7]);
}

/* Styles */

static DWORD toolbar_style_options[] = {
    TBSTYLE_ALTDRAG,
    TBSTYLE_CUSTOMERASE,
    TBSTYLE_FLAT,
    TBSTYLE_LIST,
    TBSTYLE_REGISTERDROP,
    TBSTYLE_TOOLTIPS,
    TBSTYLE_TRANSPARENT,
    TBSTYLE_WRAPABLE
};
Define_static_constants(toolbar_style_options);

CAMLprim value toolbar_style_of_option(value option)
{
    return copy_int64((int64)Constant_val(option, toolbar_style_options));
}

/* Messages */

CAMLprim value issue_tb_addbitmap(value sender, value num_bitmaps, value bitmap)
{
    CAMLparam3(sender, num_bitmaps, bitmap);
    TBADDBITMAP tbab;
    CAMLlocal2(contents, result);

    toolbarbitmapspec_val(bitmap, &tbab.hInst, &tbab.nID);
    contents = pack_message(TB_ADDBITMAP, Int_val(num_bitmaps), (LPARAM)&tbab);
    result = callback(sender, contents);
    if (Int32_val(result) == -1)
        raise_win32_error(0);
    CAMLreturn(Val_int(Int32_val(result)));
}

CAMLprim value issue_tb_addbuttons(value sender, value buttons)
{
    CAMLparam2(sender, buttons);
    int n;
    TBBUTTON* p;
    CAMLlocal2(contents, result);

    n = list_length(buttons);
    if (n > 0)
        p = malloc(n * sizeof(TBBUTTON));
    else
        p = NULL;
    n = 0;
    for (; !Is_nil(buttons); buttons = Tail(buttons))
        tbbutton_val(Head(buttons), &p[n++]);
    contents = pack_message(TB_ADDBUTTONS, n, (LPARAM)p);
    result = callback(sender, contents);
    if (n > 0)
        free(p);
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    CAMLreturn(Val_unit);
}

CAMLprim value pack_tb_addstring(value inst, value id)
{
    return pack_message(TB_ADDSTRING, (WPARAM)Handle_val(inst), Int_val(id));
}

CAMLprim value unpack_tb_addstring(value result)
{
    if (Int32_val(result) == -1)
        raise_win32_error(0);
    return Val_int(Int32_val(result));
}

CAMLprim value issue_tb_addstring_direct(value sender, value strings)
{
    CAMLparam2(sender, strings);
    int len;
    char* p;
    char* q;
    CAMLlocal3(contents, result, val);

    len = 0;
    for (val = strings; !Is_nil(val); val = Tail(val))
        len += string_length(Head(val)) + 1;
    p = malloc(len + 1);
    q = p;
    for (val = strings; !Is_nil(val); val = Tail(val)) {
        strcpy(q, String_val(Head(val)));
        q += string_length(Head(val)) + 1;
    }
    *q = '\0';
    contents = pack_message(TB_ADDSTRING, 0, (LPARAM)p);
    result = callback(sender, contents);
    free(p);
    if (Int32_val(result) == -1)
        raise_win32_error(0);
    CAMLreturn(Val_int(Int32_val(result)));
}

CAMLprim value unpack_tb_buttoncount(value result)
{
    return Val_int(Int32_val(result));
}

CAMLprim value pack_tb_buttonstructsize(value unit)
{
    return pack_message(TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
}

CAMLprim value pack_tb_changebitmap(value button_id, value bitmap)
{
    return pack_message(TB_CHANGEBITMAP, Int_val(button_id), Int_val(bitmap));
}

CAMLprim value unpack_tb_changebitmap(value result)
{
    if (Int_val(result) == 0)
        raise_win32_error(0);
    return Val_unit;
}

CAMLprim value pack_tb_checkbutton(value button_id, value check)
{
    return pack_message(TB_CHECKBUTTON, Int_val(button_id), Bool_val(check));
}

CAMLprim value unpack_tb_checkbutton(value result)
{
    if (Int_val(result) == 0)
        raise_win32_error(0);
    return Val_unit;
}

CAMLprim value pack_tb_commandtoindex(value button_id)
{
    return pack_message(TB_COMMANDTOINDEX, Int_val(button_id), 0);
}

CAMLprim value unpack_tb_commandtoindex(value result)
{
    return Val_int(Int32_val(result));
}

CAMLprim value pack_tb_deletebutton(value button_id)
{
    return pack_message(TB_DELETEBUTTON, Int_val(button_id), 0);
}

CAMLprim value unpack_tb_deletebutton(value result)
{
    if (Int_val(result) == 0)
        raise_win32_error(0);
    return Val_unit;
}

CAMLprim value pack_tb_enablebutton(value button_id, value enable)
{
    return pack_message(TB_ENABLEBUTTON, Int_val(button_id), Bool_val(enable));
}

CAMLprim value unpack_tb_enablebutton(value result)
{
    if (Int_val(result) == 0)
        raise_win32_error(0);
    return Val_unit;
}

CAMLprim value unpack_tb_getanchorhighlight(value result)
{
    return Val_bool(Int32_val(result) != 0);
}

CAMLprim value pack_tb_getbitmap(value button_id)
{
    return pack_message(TB_GETBITMAP, Int_val(button_id), 0);
}

CAMLprim value unpack_tb_getbitmap(value result)
{
    return Val_int(Int32_val(result));
}

CAMLprim value unpack_tb_getbitmapflags(value result)
{
    return Val_bool((Int32_val(result) & TBBF_LARGE) != 0);
}

CAMLprim value issue_tb_getbutton(value sender, value button)
{
    CAMLparam2(sender, button);
    TBBUTTON tbb;
    CAMLlocal3(contents, result, val);

    contents = pack_message(TB_GETBUTTON, Int_val(button), (LPARAM)&tbb);
    result = callback(sender, contents);
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    val = alloc_tbbutton(&tbb);
    CAMLreturn(val);
}

CAMLprim value issue_tb_getbuttoninfo(value sender, value id, value mask)
{
    CAMLparam3(sender, id, mask);
    TBBUTTONINFO tbbi;
    CAMLlocal5(comp, contents, result, val, v);

    memset(&tbbi, 0, sizeof(TBBUTTONINFO));
    tbbi.cbSize = sizeof(TBBUTTONINFO);
    for (; !Is_nil(mask); mask = Tail(mask)) {
        comp = Head(mask);
        if (Is_long(comp))
            tbbi.dwMask |= Constant_val(comp, tbbuttoninfo_components_noparam);
        else {
            tbbi.dwMask |= TBIF_TEXT;
            tbbi.cchText = Int_val(Field(comp, 0)) + 1;
        }
    }
    if (tbbi.dwMask & TBIF_TEXT)
        tbbi.pszText = malloc(tbbi.cchText);
    contents = pack_message(TB_GETBUTTONINFO, Int_val(id), (LPARAM)&tbbi);
    result = callback(sender, contents);
    if (Int32_val(result) == -1) {
        if (tbbi.dwMask & TBIF_TEXT)
            free(tbbi.pszText);
        raise_win32_error(0);
    }
    val = alloc_tuple(2);
    v = alloc_tbbuttoninfo(&tbbi);
    Store_field(val, 0, v);
    Store_field(val, 1, Val_int(Int32_val(result)));
    if (tbbi.dwMask & TBIF_TEXT)
        free(tbbi.pszText);
    CAMLreturn(val);
}

CAMLprim value unpack_tb_getbuttonsize(value result)
{
    SIZE s;

    s.cx = LOWORD(Int32_val(result));
    s.cy = HIWORD(Int32_val(result));
    return alloc_size(s);
}

CAMLprim value issue_tb_getbuttontext(value sender, value id, value max_length)
{
    CAMLparam3(sender, id, max_length);
    char* s;
    CAMLlocal3(contents, result, val);

    s = malloc(Int_val(max_length) + 1);
    contents = pack_message(TB_GETBUTTONTEXT, Int_val(id), (LPARAM)s);
    result = callback(sender, contents);
    if (Int32_val(result) == -1) {
        free(s);
        raise_win32_error(0);
    }
    val = copy_string(s);
    free(s);
    CAMLreturn(val);
}

CAMLprim value issue_tb_getcolorscheme(value sender)
{
    CAMLparam1(sender);
    COLORSCHEME c;
    CAMLlocal3(contents, result, val);

    memset(&c, 0, sizeof(COLORSCHEME));
    c.dwSize = sizeof(COLORSCHEME);
    contents = pack_message(TB_GETCOLORSCHEME, 0, (LPARAM)&c);
    result = callback(sender, contents);
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    val = alloc_colorscheme(&c);
    CAMLreturn(val);
}

CAMLprim value unpack_tb_getdisabledimagelist(value result)
{
    return alloc_handle((HIMAGELIST)Int32_val(result));
}

CAMLprim value unpack_tb_getextendedstyle(value result)
{
    return Alloc_flags(Int32_val(result), toolbar_extended_styles);
}

CAMLprim value unpack_tb_gethotimagelist(value result)
{
    return alloc_handle((HIMAGELIST)Int32_val(result));
}

CAMLprim value unpack_tb_gethotitem(value result)
{
    return Val_int(Int32_val(result));
}

CAMLprim value unpack_tb_getimagelist(value result)
{
    return alloc_handle((HIMAGELIST)Int32_val(result));
}

CAMLprim value unpack_tb_getinsertmarkcolor(value result)
{
    return alloc_color((COLORREF)Int32_val(result));
}

CAMLprim value issue_tb_getitemrect(value sender, value button)
{
    CAMLparam2(sender, button);
    RECT r;
    CAMLlocal3(contents, result, val);

    contents = pack_message(TB_GETITEMRECT, Int_val(button), (LPARAM)&r);
    result = callback(sender, contents);
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    val = alloc_rect(&r);
    CAMLreturn(val);
}

CAMLprim value issue_tb_getmaxsize(value sender)
{
    CAMLparam1(sender);
    SIZE s;
    CAMLlocal3(contents, result, val);

    contents = pack_message(TB_GETMAXSIZE, 0, (LPARAM)&s);
    result = callback(sender, contents);
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    val = alloc_size(s);
    CAMLreturn(val);
}

CAMLprim value unpack_tb_getpadding(value result)
{
    SIZE s;

    s.cx = LOWORD(Int32_val(result));
    s.cy = HIWORD(Int32_val(result));
    return alloc_size(s);
}

CAMLprim value issue_tb_getrect(value sender, value id)
{
    CAMLparam2(sender, id);
    RECT r;
    CAMLlocal3(contents, result, val);

    contents = pack_message(TB_GETRECT, Int_val(id), (LPARAM)&r);
    result = callback(sender, contents);
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    val = alloc_rect(&r);
    CAMLreturn(val);
}

CAMLprim value unpack_tb_getrows(value result)
{
    return Val_int(Int32_val(result));
}

CAMLprim value pack_tb_getstate(value id)
{
    return pack_message(TB_GETSTATE, Int_val(id), 0);
}

CAMLprim value unpack_tb_getstate(value result)
{
    if (Int32_val(result) == -1)
        raise_win32_error(0);
    return Alloc_flags(Int32_val(result), toolbar_button_states);
}

CAMLprim value unpack_tb_getstyle(value result)
{
    return copy_int64(Int32_val(result));
}

CAMLprim value unpack_tb_gettextrows(value result)
{
    return Val_int(Int32_val(result));
}

CAMLprim value unpack_tb_gettooltips(value result)
{
    return alloc_handle((HWND)Int32_val(result));
}

CAMLprim value pack_tb_hidebutton(value id, value hide)
{
    return pack_message(TB_HIDEBUTTON, Int_val(id), Bool_val(hide));
}

CAMLprim value unpack_tb_hidebutton(value result)
{
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    return Val_unit;
}

CAMLprim value issue_tb_hittest(value sender, value pt)
{
    CAMLparam2(sender, pt);
    POINT p;
    CAMLlocal3(contents, result, val);

    p = point_val(pt);
    contents = pack_message(TB_HITTEST, 0, (LPARAM)&p);
    result = callback(sender, contents);
    if (Int32_val(result) >= 0) {
        val = alloc(1, 0);
        Store_field(val, 0, Val_int(Int32_val(result)));
    } else {
        val = alloc(1, 1);
        Store_field(val, 0, Val_int(-Int32_val(result)));
    }
    CAMLreturn(val);
}

CAMLprim value pack_tb_indeterminate(value id, value indeterminate)
{
    return pack_message(TB_INDETERMINATE, Int_val(id), Bool_val(indeterminate));
}

CAMLprim value unpack_tb_indeterminate(value result)
{
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    return Val_unit;
}

CAMLprim value issue_tb_insertbutton(value sender, value button, value tbbutton)
{
    CAMLparam3(sender, button, tbbutton);
    TBBUTTON tbb;
    CAMLlocal2(contents, result);

    tbbutton_val(tbbutton, &tbb);
    contents = pack_message(TB_INSERTBUTTON, Int_val(button), (LPARAM)&tbb);
    result = callback(sender, contents);
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    CAMLreturn(Val_unit);
}

CAMLprim value pack_tb_isbuttonchecked(value id)
{
    return pack_message(TB_ISBUTTONCHECKED, Int_val(id), 0);
}

CAMLprim value unpack_tb_isbuttonchecked(value result)
{
    return Val_bool(Int32_val(result) != 0);
}

CAMLprim value pack_tb_isbuttonenabled(value id)
{
    return pack_message(TB_ISBUTTONENABLED, Int_val(id), 0);
}

CAMLprim value unpack_tb_isbuttonenabled(value result)
{
    return Val_bool(Int32_val(result) != 0);
}

CAMLprim value pack_tb_isbuttonhidden(value id)
{
    return pack_message(TB_ISBUTTONHIDDEN, Int_val(id), 0);
}

CAMLprim value unpack_tb_isbuttonhidden(value result)
{
    return Val_bool(Int32_val(result) != 0);
}

CAMLprim value pack_tb_isbuttonhighlighted(value id)
{
    return pack_message(TB_ISBUTTONHIGHLIGHTED, Int_val(id), 0);
}

CAMLprim value unpack_tb_isbuttonhighlighted(value result)
{
    return Val_bool(Int32_val(result) != 0);
}

CAMLprim value pack_tb_isbuttonindeterminate(value id)
{
    return pack_message(TB_ISBUTTONINDETERMINATE, Int_val(id), 0);
}

CAMLprim value unpack_tb_isbuttonindeterminate(value result)
{
    return Val_bool(Int32_val(result) != 0);
}

CAMLprim value pack_tb_isbuttonpressed(value id)
{
    return pack_message(TB_ISBUTTONPRESSED, Int_val(id), 0);
}

CAMLprim value unpack_tb_isbuttonpressed(value result)
{
    return Val_bool(Int32_val(result) != 0);
}

CAMLprim value pack_tb_loadimages(value bitmap)
{
    HINSTANCE hinst;
    UINT id;

    toolbarbitmapspec_val(bitmap, &hinst, &id);
    return pack_message(TB_LOADIMAGES, id, (LPARAM)hinst);
}

CAMLprim value unpack_tb_loadimages(value result)
{
    return Val_int(Int32_val(result));
}

CAMLprim value issue_tb_mapaccelerator(value sender, value ch)
{
    CAMLparam2(sender, ch);
    UINT id;
    CAMLlocal2(contents, result);

    contents = pack_message(TB_MAPACCELERATOR, Int_val(ch), (LPARAM)&id);
    result = callback(sender, contents);
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    CAMLreturn(Val_int(id));
}

CAMLprim value pack_tb_markbutton(value id, value highlight)
{
    return pack_message(TB_MARKBUTTON, Int_val(id), Bool_val(highlight));
}

CAMLprim value unpack_tb_markbutton(value result)
{
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    return Val_unit;
}

CAMLprim value pack_tb_movebutton(value old_pos, value new_pos)
{
    return pack_message(TB_MOVEBUTTON, Int_val(old_pos), Int_val(new_pos));
}

CAMLprim value unpack_tb_movebutton(value result)
{
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    return Val_unit;
}

CAMLprim value pack_tb_pressbutton(value id, value press)
{
    return pack_message(TB_PRESSBUTTON, Int_val(id), Bool_val(press));
}

CAMLprim value unpack_tb_pressbutton(value result)
{
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    return Val_unit;
}

CAMLprim value issue_tb_replacebitmap(value sender, value tbreplacebitmap)
{
    CAMLparam2(sender, tbreplacebitmap);
    TBREPLACEBITMAP tbrb;
    CAMLlocal2(contents, result);

    toolbarbitmapspec_val(Field(tbreplacebitmap, 0), &tbrb.hInstOld, &tbrb.nIDOld);
    toolbarbitmapspec_val(Field(tbreplacebitmap, 1), &tbrb.hInstNew, &tbrb.nIDNew);
    tbrb.nButtons = Int_val(Field(tbreplacebitmap, 2));
    contents = pack_message(TB_REPLACEBITMAP, 0, (LPARAM)&tbrb);
    result = callback(sender, contents);
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    CAMLreturn(Val_unit);
}

CAMLprim value issue_tb_saverestore(value sender, value save, value tbsaveparams)
{
    CAMLparam3(sender, save, tbsaveparams);
    TBSAVEPARAMS tbsp;
    CAMLlocal1(contents);

    tbsp.hkr = Handle_val(Field(tbsaveparams, 0));
    tbsp.pszSubKey = new_string(Field(tbsaveparams, 1));
    tbsp.pszValueName = new_string(Field(tbsaveparams, 2));
    contents = pack_message(TB_SAVERESTORE, Bool_val(save), (LPARAM)&tbsp);
    callback(sender, contents);
    free_string((char*)tbsp.pszSubKey);
    free_string((char*)tbsp.pszValueName);
    CAMLreturn(Val_unit);
}

CAMLprim value pack_tb_setanchorhighlight(value anchor)
{
    return pack_message(TB_SETANCHORHIGHLIGHT, Bool_val(anchor), 0);
}

CAMLprim value unpack_tb_setanchorhighlight(value result)
{
    return Val_bool(Int32_val(result) != 0);
}

CAMLprim value pack_tb_setbitmapsize(value size)
{
    return pack_message(TB_SETBITMAPSIZE, 0, MAKELONG(Cx(size), Cy(size)));
}

CAMLprim value unpack_tb_setbitmapsize(value result)
{
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    return Val_unit;
}

CAMLprim value issue_tb_setbuttoninfo(value sender, value id, value info)
{
    CAMLparam3(sender, id, info);
    TBBUTTONINFO* tbbi;
    CAMLlocal2(contents, result);

    tbbi = new_tbbuttoninfo(info);
    contents = pack_message(TB_SETBUTTONINFO, Int_val(id), (LPARAM)tbbi);
    result = callback(sender, contents);
    free_tbbuttoninfo(tbbi);
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    CAMLreturn(Val_unit);
}

CAMLprim value pack_tb_setbuttonsize(value size)
{
    return pack_message(TB_SETBUTTONSIZE, 0, MAKELONG(Cx(size), Cy(size)));
}

CAMLprim value unpack_tb_setbuttonsize(value result)
{
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    return Val_unit;
}

CAMLprim value pack_tb_setbuttonwidth(value min, value max)
{
    return pack_message(TB_SETBUTTONWIDTH, 0, MAKELONG(Int_val(min), Int_val(max)));
}

CAMLprim value unpack_tb_setbuttonwidth(value result)
{
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    return Val_unit;
}

CAMLprim value pack_tb_setcmdid(value button, value id)
{
    return pack_message(TB_SETCMDID, Int_val(button), Int_val(id));
}

CAMLprim value unpack_tb_setcmdid(value result)
{
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    return Val_unit;
}

CAMLprim value issue_tb_setcolorscheme(value sender, value cs)
{
    CAMLparam2(sender, cs);
    COLORSCHEME c;
    CAMLlocal1(contents);

    colorscheme_val(cs, &c);
    contents = pack_message(TB_SETCOLORSCHEME, 0, (LPARAM)&c);
    callback(sender, contents);
    CAMLreturn(Val_unit);
}

CAMLprim value pack_tb_setdisabledimagelist(value iml)
{
    return pack_message(TB_SETDISABLEDIMAGELIST, 0, (LPARAM)Handle_val(iml));
}

CAMLprim value unpack_tb_setdisabledimagelist(value result)
{
    return alloc_handle((HIMAGELIST)Int32_val(result));
}

CAMLprim value pack_tb_setextendedstyle(value style)
{
    return pack_message(TB_SETEXTENDEDSTYLE, 0, flags_val(style, toolbar_extended_styles));
}

CAMLprim value unpack_tb_setextendedstyle(value result)
{
    return Alloc_flags(Int32_val(result), toolbar_extended_styles);
}

CAMLprim value pack_tb_sethotimagelist(value iml)
{
    return pack_message(TB_SETHOTIMAGELIST, 0, (LPARAM)Handle_val(iml));
}

CAMLprim value unpack_tb_sethotimagelist(value result)
{
    return alloc_handle((HIMAGELIST)Int32_val(result));
}

CAMLprim value pack_tb_sethotitem(value item)
{
    return pack_message(TB_SETHOTITEM, Int_val(item), 0);
}

CAMLprim value unpack_tb_sethotitem(value result)
{
    return Val_int(Int32_val(result));
}

CAMLprim value pack_tb_setimagelist(value iml)
{
    return pack_message(TB_SETIMAGELIST, 0, (LPARAM)Handle_val(iml));
}

CAMLprim value unpack_tb_setimagelist(value result)
{
    return alloc_handle((HIMAGELIST)Int32_val(result));
}

CAMLprim value pack_tb_setindent(value indent)
{
    return pack_message(TB_SETINDENT, Int_val(indent), 0);
}

CAMLprim value unpack_tb_setindent(value result)
{
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    return Val_unit;
}

CAMLprim value pack_tb_setinsertmarkcolor(value color)
{
    return pack_message(TB_SETINSERTMARKCOLOR, 0, (LPARAM)color_val(color));
}

CAMLprim value unpack_tb_setinsertmarkcolor(value result)
{
    return alloc_color((COLORREF)Int32_val(result));
}

CAMLprim value pack_tb_setmaxtextrows(value rows)
{
    return pack_message(TB_SETMAXTEXTROWS, Int_val(rows), 0);
}

CAMLprim value unpack_tb_setmaxtextrows(value result)
{
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    return Val_unit;
}

CAMLprim value pack_tb_setpadding(value padding)
{
    return pack_message(TB_SETPADDING, 0, MAKELPARAM(Cx(padding), Cy(padding)));
}

CAMLprim value unpack_tb_setpadding(value result)
{
    SIZE s;

    s.cx = LOWORD(Int32_val(result));
    s.cy = HIWORD(Int32_val(result));
    return alloc_size(s);
}

CAMLprim value pack_tb_setparent(value parent)
{
    return pack_message(TB_SETPARENT, (WPARAM)Handle_val(parent), 0);
}

CAMLprim value unpack_tb_setparent(value result)
{
    return alloc_handle((HWND)Int32_val(result));
}

CAMLprim value issue_tb_setrows(value sender, value rows, value larger)
{
    CAMLparam3(sender, rows, larger);
    RECT r;
    CAMLlocal2(contents, val);

    contents = pack_message(TB_SETROWS, MAKEWPARAM(Int_val(rows), Bool_val(larger)), (LPARAM)&r);
    callback(sender, contents);
    val = alloc_rect(&r);
    CAMLreturn(val);
}

CAMLprim value pack_tb_setstate(value id, value state)
{
    return pack_message(TB_SETSTATE, Int_val(id), flags_val(state, toolbar_button_states));
}

CAMLprim value unpack_tb_setstate(value result)
{
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    return Val_unit;
}

CAMLprim value pack_tb_setstyle(value style)
{
    return pack_message(TB_SETSTYLE, 0, (DWORD)Int64_val(style));
}

CAMLprim value pack_tb_settooltips(value tooltips)
{
    return pack_message(TB_SETTOOLTIPS, (WPARAM)Handle_val(tooltips), 0);
}

/* Notifications */

CAMLprim value unpack_nmtbcustomdraw(value msg)
{
    CAMLparam1(msg);
    NMTBCUSTOMDRAW* nmtbcd;
    CAMLlocal2(val, v);

    nmtbcd = (NMTBCUSTOMDRAW*)Lparam(msg);
    val = alloc_tuple(13);
    Store_field(val, 0, Val_int(nmtbcd->nmcd.dwItemSpec));
    v = alloc_handle(nmtbcd->hbrMonoDither);
    Store_field(val, 1, v);
    v = alloc_handle(nmtbcd->hbrLines);
    Store_field(val, 2, v);
    v = alloc_handle(nmtbcd->hpenLines);
    Store_field(val, 3, v);
    v = alloc_color(nmtbcd->clrText);
    Store_field(val, 4, v);
    v = alloc_color(nmtbcd->clrMark);
    Store_field(val, 5, v);
    v = alloc_color(nmtbcd->clrTextHighlight);
    Store_field(val, 6, v);
    v = alloc_color(nmtbcd->clrBtnFace);
    Store_field(val, 7, v);
    v = alloc_color(nmtbcd->clrBtnHighlight);
    Store_field(val, 8, v);
    v = alloc_color(nmtbcd->clrHighlightHotTrack);
    Store_field(val, 9, v);
    v = alloc_rect(&nmtbcd->rcText);
    Store_field(val, 10, v);
    Store_field(val, 11, Val_constant(nmtbcd->nStringBkMode, background_modes));
    Store_field(val, 12, Val_constant(nmtbcd->nHLStringBkMode, background_modes));
    CAMLreturn(val);
}

CAMLprim value unpack_nmtoolbar(value msg)
{
    CAMLparam1(msg);
    NMTOOLBAR* nmtb;
    CAMLlocal1(val);

    nmtb = (NMTOOLBAR*)Lparam(msg);
    val = alloc_tuple(1);
    Store_field(val, 0, Val_int(nmtb->iItem));
    CAMLreturn(val);
}

CAMLprim value unpack_nmtoolbar_querying(value msg)
{
    CAMLparam1(msg);
    NMTOOLBAR* nmtb;
    CAMLlocal2(val, v);

    nmtb = (NMTOOLBAR*)Lparam(msg);
    val = alloc_tuple(2);
    Store_field(val, 0, Val_int(nmtb->iItem));
    v = alloc_tbbutton(&nmtb->tbButton);
    Store_field(val, 1, v);
    CAMLreturn(val);
}

static DWORD nmtbhotitem_reasons[] = {
    HICF_ACCELERATOR,
    HICF_ARROWKEYS,
    HICF_DUPACCEL,
    HICF_ENTERING,
    HICF_LEAVING,
    HICF_MOUSE,
    HICF_OTHER,
    HICF_RESELECT
};
Define_static_constants(nmtbhotitem_reasons);

CAMLprim value unpack_nmtbhotitem(value msg)
{
    CAMLparam1(msg);
    NMTBHOTITEM* nmtbhi;
    CAMLlocal2(val, v);

    nmtbhi = (NMTBHOTITEM*)Lparam(msg);
    val = alloc_tuple(3);
    Store_field(val, 0, Val_int(nmtbhi->idOld));
    Store_field(val, 1, Val_int(nmtbhi->idNew));
    v = Alloc_flags(nmtbhi->dwFlags, nmtbhotitem_reasons);
    Store_field(val, 2, v);
    CAMLreturn(val);
}

static DWORD tbn_dropdown_results[] = {
    TBDDRET_DEFAULT,
    TBDDRET_NODEFAULT,
    TBDDRET_TREATPRESSED
};
Define_static_constants(tbn_dropdown_results);

CAMLprim value return_tbn_dropdown(value result)
{
    return copy_int32(Constant_val(result, tbn_dropdown_results));
}

CAMLprim value on_tbn_getbuttoninfo(value handler, value wnd, value msg)
{
    CAMLparam3(handler, wnd, msg);
    NMHDR* nmh;
    NMTOOLBAR* nmtb;
    CAMLlocalN(args, 6);
    CAMLlocal3(result, val, ret);

    ret = Val_none;
    if (Int_val(Field(msg, 0)) == WM_NOTIFY) {
        nmh = (NMHDR*)Lparam(msg);
        if (nmh->code == TBN_GETBUTTONINFO) {
            nmtb = (NMTOOLBAR*)nmh;
            args[0] = wnd;
            args[1] = msg;
            args[2] = Val_int(Wparam(msg));
            args[3] = alloc_nmhdr(nmh);
            args[4] = Val_int(nmtb->iItem);
            args[5] = Val_int(nmtb->cchText - 1);
            result = callbackN(handler, 6, args);
            if (Is_block(result)) {
                if (Tag_val(result) == 0)
                    ret = alloc_some(Field(result, 0));
                else {
                    val = Field(result, 0);
                    if (Is_some(val)) {
                        strncpy(nmtb->pszText, String_val(Value_of(val)), nmtb->cchText - 1);
                        nmtb->pszText[nmtb->cchText-1] = '\0';
                        ret = alloc_some(copy_int32(1));
                    } else
                        ret = alloc_some(copy_int32(0));
                }
            }
        }
    }
    CAMLreturn(ret);
}

static DWORD toolbar_dispinfo_components_noparam[] = {
    TBNF_IMAGE
};
Define_static_constants(toolbar_dispinfo_components_noparam);

CAMLprim value on_tbn_getdispinfo(value handler, value wnd, value msg)
{
    CAMLparam3(handler, wnd, msg);
    NMHDR* nmh;
    NMTBDISPINFO* nmtbdi;
    BOOL has_text;
    CAMLlocalN(args, 7);
    CAMLlocal5(mask, result, pair, tbdi, val);
    CAMLlocal1(ret);

    ret = Val_none;
    if (Int_val(Field(msg, 0)) == WM_NOTIFY) {
        nmh = (NMHDR*)Lparam(msg);
        if (nmh->code == TBN_GETDISPINFO) {
            nmtbdi = (NMTBDISPINFO*)nmh;
            has_text =
                (nmtbdi->dwMask & TBNF_TEXT) &&
                nmtbdi->pszText != NULL &&
                nmtbdi->cchText > 0;
            mask = Alloc_flags(nmtbdi->dwMask, toolbar_dispinfo_components_noparam);
            if (has_text) {
                val = alloc(1, 0);
                Store_field(val, 0, Val_int(nmtbdi->cchText - 1));
                mask = alloc_cons(val, mask);
            }
            args[0] = wnd;
            args[1] = msg;
            args[2] = Val_int(Wparam(msg));
            args[3] = alloc_nmhdr(nmh);
            args[4] = mask;
            args[5] = Val_int(nmtbdi->idCommand);
            args[6] = copy_int32(nmtbdi->lParam);
            result = callbackN(handler, 7, args);
            if (Is_block(result)) {
                if (Tag_val(result) == 0)
                    ret = alloc_some(Field(result, 0));
                else {
                    pair = Field(result, 0);
                    tbdi = Field(pair, 0);
                    if (Is_some(Field(tbdi, 0)))
                        nmtbdi->iImage = Int_val(Value_of(Field(tbdi, 0)));
                    if (has_text && Is_some(Field(tbdi, 1))) {
                        strncpy(
                            nmtbdi->pszText,
                            String_val(Value_of(Field(tbdi, 1))),
                            nmtbdi->cchText - 1);
                        nmtbdi->pszText[nmtbdi->cchText-1] = '\0';
                    }
                    if (Bool_val(Field(pair, 1)))
                        nmtbdi->dwMask |= TBNF_DI_SETITEM;
                    ret = alloc_some(copy_int32(0));
                }
            }
        }
    }
    CAMLreturn(ret);
}

CAMLprim value on_tbn_getinfotip(value handler, value wnd, value msg)
{
    CAMLparam3(handler, wnd, msg);
    NMHDR* nmh;
    NMTBGETINFOTIP* nmtbgit;
    CAMLlocalN(args, 7);
    CAMLlocal3(result, val, ret);

    ret = Val_none;
    if (Int_val(Field(msg, 0)) == WM_NOTIFY) {
        nmh = (NMHDR*)Lparam(msg);
        if (nmh->code == TBN_GETINFOTIP) {
            nmtbgit = (NMTBGETINFOTIP*)nmh;
            args[0] = wnd;
            args[1] = msg;
            args[2] = Val_int(Wparam(msg));
            args[3] = alloc_nmhdr(nmh);
            args[4] = Val_int(nmtbgit->cchTextMax - 1);
            args[5] = Val_int(nmtbgit->iItem);
            args[6] = copy_int32(nmtbgit->lParam);
            result = callbackN(handler, 7, args);
            if (Is_block(result)) {
                if (Tag_val(result) == 0)
                    ret = alloc_some(Field(result, 0));
                else {
                    val = Field(result, 0);
                    strncpy(nmtbgit->pszText, String_val(val), nmtbgit->cchTextMax - 1);
                    nmtbgit->pszText[nmtbgit->cchTextMax-1] = '\0';
                    ret = alloc_some(copy_int32(0));
                }
            }
        }
    }
    CAMLreturn(ret);
}

CAMLprim value return_tbn_querydelete(value result)
{
    return copy_int32(Bool_val(result));
}

CAMLprim value return_tbn_queryinsert(value result)
{
    return copy_int32(Bool_val(result));
}
