/* OCaml-Win32
 * win32_rebar.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: Rebar Controls */

static DWORD rebar_band_styles[] = {
    RBBS_BREAK,
    RBBS_CHILDEDGE,
    RBBS_FIXEDBMP,
    RBBS_FIXEDSIZE,
    RBBS_GRIPPERALWAYS,
    RBBS_HIDDEN,
    RBBS_NOGRIPPER,
    RBBS_NOVERT,
    RBBS_VARIABLEHEIGHT
};
Define_static_constants(rebar_band_styles);

static REBARBANDINFO* new_rebarbandinfo(value val)
{
    REBARBANDINFO* rbbi;
    value v;

    rbbi = malloc(sizeof(REBARBANDINFO));
    memset(rbbi, 0, sizeof(REBARBANDINFO));
    rbbi->cbSize = sizeof(REBARBANDINFO);
    if (Is_some(Field(val, 0))) {
        rbbi->fMask |= RBBIM_STYLE;
        rbbi->fStyle = flags_val(Value_of(Field(val, 0)), rebar_band_styles);
    }
    if (Is_some(Field(val, 1))) {
        rbbi->fMask |= RBBIM_COLORS;
        v = Value_of(Field(val, 1));
        rbbi->clrFore = color_val(Field(v, 0));
        rbbi->clrBack = color_val(Field(v, 1));
    }
    if (Is_some(Field(val, 2))) {
        rbbi->fMask |= RBBIM_TEXT;
        rbbi->lpText = new_string(Value_of(Field(val, 2)));
    }
    if (Is_some(Field(val, 3))) {
        rbbi->fMask |= RBBIM_IMAGE;
        rbbi->iImage = Int_val(Value_of(Field(val, 3)));
    }
    if (Is_some(Field(val, 4))) {
        rbbi->fMask |= RBBIM_CHILD;
        rbbi->hwndChild = Handle_val(Value_of(Field(val, 4)));
    }
    if (Is_some(Field(val, 5))) {
        rbbi->fMask |= RBBIM_CHILDSIZE;
        v = Value_of(Field(val, 5));
        rbbi->cxMinChild = Cx(Field(v, 0));
        rbbi->cyMinChild = Cy(Field(v, 0));
        rbbi->cyChild = Int_val(Field(v, 1));
        rbbi->cyMaxChild = Int_val(Field(v, 2));
        rbbi->cyIntegral = Int_val(Field(v, 3));
    }
    if (Is_some(Field(val, 6))) {
        rbbi->fMask |= RBBIM_SIZE;
        rbbi->cx = Int_val(Value_of(Field(val, 6)));
    }
    if (Is_some(Field(val, 7))) {
        rbbi->fMask |= RBBIM_BACKGROUND;
        rbbi->hbmBack = Handle_val(Value_of(Field(val, 7)));
    }
    if (Is_some(Field(val, 8))) {
        rbbi->fMask |= RBBIM_ID;
        rbbi->wID = Int_val(Value_of(Field(val, 8)));
    }
    if (Is_some(Field(val, 9))) {
        rbbi->fMask |= RBBIM_IDEALSIZE;
        rbbi->cxIdeal = Int_val(Value_of(Field(val, 9)));
    }
    if (Is_some(Field(val, 10))) {
        rbbi->fMask |= RBBIM_LPARAM;
        rbbi->lParam = Int32_val(Value_of(Field(val, 10)));
    }
    if (Is_some(Field(val, 11))) {
        rbbi->fMask |= RBBIM_HEADERSIZE;
        rbbi->cxHeader = Int_val(Value_of(Field(val, 11)));
    }
    return rbbi;
}

static void free_rebarbandinfo(REBARBANDINFO* rbbi)
{
    if (rbbi->fMask & RBBIM_TEXT)
        free_string(rbbi->lpText);
    free(rbbi);
}

static value alloc_rebarbandinfo(REBARBANDINFO* rbbi)
{
    CAMLparam0();
    CAMLlocal4(val, v, vv, vvv);
    SIZE sz;

    val = alloc_tuple(12);
    if (rbbi->fMask & RBBIM_STYLE) {
        vv = Alloc_flags(rbbi->fStyle, rebar_band_styles);
        v = alloc_some(vv);
        Store_field(val, 0, v);
    } else
        Store_field(val, 0, Val_none);
    if (rbbi->fMask & RBBIM_COLORS) {
        vv = alloc_tuple(2);
        vvv = alloc_color(rbbi->clrFore);
        Store_field(vv, 0, vvv);
        vvv = alloc_color(rbbi->clrBack);
        Store_field(vv, 1, vvv);
        v = alloc_some(vv);
        Store_field(val, 1, v);
    } else
        Store_field(val, 1, Val_none);
    if (rbbi->fMask & RBBIM_TEXT) {
        v = alloc_some(copy_string(rbbi->lpText));
        Store_field(val, 2, v);
    } else
        Store_field(val, 2, Val_none);
    if (rbbi->fMask & RBBIM_IMAGE) {
        v = alloc_some(Val_int(rbbi->iImage));
        Store_field(val, 3, v);
    } else
        Store_field(val, 3, Val_none);
    if (rbbi->fMask & RBBIM_CHILD) {
        v = alloc_some(alloc_handle(rbbi->hwndChild));
        Store_field(val, 4, v);
    } else
        Store_field(val, 4, Val_none);
    if (rbbi->fMask & RBBIM_CHILDSIZE) {
        vv = alloc_tuple(4);
        sz.cx = rbbi->cxMinChild;
        sz.cy = rbbi->cyMinChild;
        vvv = alloc_size(sz);
        Store_field(vv, 0, vvv);
        Store_field(vv, 1, Val_int(rbbi->cyChild));
        Store_field(vv, 2, Val_int(rbbi->cyMaxChild));
        Store_field(vv, 3, Val_int(rbbi->cyIntegral));
        v = alloc_some(vv);
        Store_field(val, 5, v);
    } else
        Store_field(val, 5, Val_none);
    if (rbbi->fMask & RBBIM_SIZE) {
        v = alloc_some(Val_int(rbbi->cx));
        Store_field(val, 6, v);
    } else
        Store_field(val, 6, Val_none);
    if (rbbi->fMask & RBBIM_BACKGROUND) {
        v = alloc_some(alloc_handle(rbbi->hbmBack));
        Store_field(val, 7, v);
    } else
        Store_field(val, 7, Val_none);
    if (rbbi->fMask & RBBIM_ID) {
        v = alloc_some(Val_int(rbbi->wID));
        Store_field(val, 8, v);
    } else
        Store_field(val, 8, Val_none);
    if (rbbi->fMask & RBBIM_IDEALSIZE) {
        v = alloc_some(Val_int(rbbi->cxIdeal));
        Store_field(val, 9, v);
    } else
        Store_field(val, 9, Val_none);
    if (rbbi->fMask & RBBIM_LPARAM) {
        v = alloc_some(copy_int32(rbbi->lParam));
        Store_field(val, 10, v);
    } else
        Store_field(val, 10, Val_none);
    if (rbbi->fMask & RBBIM_HEADERSIZE) {
        v = alloc_some(Val_int(rbbi->cxHeader));
        Store_field(val, 11, v);
    } else
        Store_field(val, 11, Val_none);
    CAMLreturn(val);
}

static DWORD rebarbandinfo_components_noparam[] = {
    RBBIM_STYLE,
    RBBIM_COLORS,
    RBBIM_IMAGE,
    RBBIM_CHILD,
    RBBIM_CHILDSIZE,
    RBBIM_SIZE,
    RBBIM_BACKGROUND,
    RBBIM_ID,
    RBBIM_IDEALSIZE,
    RBBIM_LPARAM,
    RBBIM_HEADERSIZE
};
Define_static_constants(rebarbandinfo_components_noparam);

/* Styles */

static DWORD rebar_style_options[] = {
    RBS_AUTOSIZE,
    RBS_BANDBORDERS,
    RBS_DBLCLKTOGGLE,
    RBS_FIXEDORDER,
    RBS_REGISTERDROP,
    RBS_TOOLTIPS,
    RBS_VARHEIGHT,
    RBS_VERTICALGRIPPER
};
Define_static_constants(rebar_style_options);

CAMLprim value rebar_style_of_option(value option)
{
    return copy_int64((int64)Constant_val(option, rebar_style_options));
}

/* Messages */

CAMLprim value pack_rb_begindrag(value band, value pos)
{
    DWORD dw;

    if (Is_some(pos))
        dw = MAKELONG(X(Value_of(pos)), Y(Value_of(pos)));
    else
        dw = -1;
    return pack_message(RB_BEGINDRAG, Int_val(band), dw);
}

CAMLprim value pack_rb_deleteband(value band)
{
    return pack_message(RB_DELETEBAND, Int_val(band), 0);
}

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

CAMLprim value pack_rb_dragmove(value pos)
{
    DWORD dw;

    if (Is_some(pos))
        dw = MAKELONG(X(Value_of(pos)), Y(Value_of(pos)));
    else
        dw = -1;
    return pack_message(RB_DRAGMOVE, 0, dw);
}

CAMLprim value issue_rb_getbandborders(value sender, value band)
{
    CAMLparam2(sender, band);
    RECT r;
    CAMLlocal2(contents, val);

    contents = pack_message(RB_GETBANDBORDERS, Int_val(band), (LPARAM)&r);
    callback(sender, contents);
    val = alloc_rect(&r);
    CAMLreturn(val);
}

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

CAMLprim value issue_rb_getbandinfo(value sender, value band, value mask)
{
    CAMLparam3(sender, band, mask);
    REBARBANDINFO rbbi;
    CAMLlocal4(comp, contents, result, val);

    memset(&rbbi, 0, sizeof(REBARBANDINFO));
    rbbi.cbSize = sizeof(REBARBANDINFO);
    for (; !Is_nil(mask); mask = Tail(mask)) {
        comp = Head(mask);
        if (Is_long(comp))
            rbbi.fMask |= Constant_val(comp, rebarbandinfo_components_noparam);
        else {
            rbbi.fMask |= RBBIM_TEXT;
            rbbi.cch = Int_val(Field(comp, 0)) + 1;
        }
    }
    if (rbbi.fMask & RBBIM_TEXT)
        rbbi.lpText = malloc(rbbi.cch);
    contents = pack_message(RB_GETBANDINFO, Int_val(band), (LPARAM)&rbbi);
    result = callback(sender, contents);
    if (Int32_val(result) == 0) {
        if (rbbi.fMask & RBBIM_TEXT)
            free(rbbi.lpText);
        raise_win32_error(0);
    }
    val = alloc_rebarbandinfo(&rbbi);
    if (rbbi.fMask & RBBIM_TEXT)
        free(rbbi.lpText);
    CAMLreturn(val);
}

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

CAMLprim value issue_rb_getbarinfo(value sender)
{
    CAMLparam1(sender);
    REBARINFO rbi;
    CAMLlocal3(contents, result, val);

    memset(&rbi, 0, sizeof(REBARINFO));
    rbi.cbSize = sizeof(REBARINFO);
    rbi.fMask = RBIM_IMAGELIST;
    contents = pack_message(RB_GETBARINFO, 0, (LPARAM)&rbi);
    result = callback(sender, contents);
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    val = alloc_handle(rbi.himl);
    CAMLreturn(val);
}

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

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

    memset(&c, 0, sizeof(COLORSCHEME));
    c.dwSize = sizeof(COLORSCHEME);
    contents = pack_message(RB_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_rb_getpalette(value result)
{
    return alloc_handle((HPALETTE)Int32_val(result));
}

CAMLprim value issue_rb_getrect(value sender, value band)
{
    CAMLparam2(sender, band);
    RECT r;
    CAMLlocal3(contents, result, val);

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

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

CAMLprim value pack_rb_getrowheight(value row)
{
    return pack_message(RB_GETROWHEIGHT, Int_val(row), 0);
}

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

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

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

static DWORD rebar_hit_test_parts[] = {
    RBHT_CAPTION,
    RBHT_CLIENT,
    RBHT_GRABBER
};
Define_static_constants(rebar_hit_test_parts);

CAMLprim value issue_rb_hittest(value sender, value pt)
{
    CAMLparam2(sender, pt);
    RBHITTESTINFO rbhti;
    CAMLlocal2(contents, val);

    memset(&rbhti, 0, sizeof(RBHITTESTINFO));
    rbhti.pt = point_val(pt);
    contents = pack_message(RB_HITTEST, 0, (LPARAM)&rbhti);
    callback(sender, contents);
    if (rbhti.flags == RBHT_NOWHERE)
        val = Val_int(0);
    else {
        val = alloc(2, 0);
        Store_field(val, 0, Val_int(rbhti.iBand));
        Store_field(val, 1, Val_constant(rbhti.flags, rebar_hit_test_parts));
    }
    CAMLreturn(val);
}

CAMLprim value pack_rb_idtoindex(value id)
{
    return pack_message(RB_IDTOINDEX, Int_val(id), 0);
}

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

CAMLprim value issue_rb_insertband(value sender, value band, value info)
{
    CAMLparam3(sender, band, info);
    REBARBANDINFO* rbbi;
    CAMLlocal2(contents, result);

    rbbi = new_rebarbandinfo(info);
    contents = pack_message(RB_INSERTBAND, Int_val(band), (LPARAM)rbbi);
    result = callback(sender, contents);
    free_rebarbandinfo(rbbi);
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    CAMLreturn(Val_unit);
}

CAMLprim value pack_rb_maximizeband(value band, value ideal)
{
    return pack_message(RB_MAXIMIZEBAND, Int_val(band), Bool_val(ideal));
}

CAMLprim value pack_rb_minimizeband(value band)
{
    return pack_message(RB_MINIMIZEBAND, Int_val(band), 0);
}

CAMLprim value pack_rb_moveband(value from_band, value to_band)
{
    return pack_message(RB_MOVEBAND, Int_val(from_band), Int_val(to_band));
}

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

CAMLprim value issue_rb_setbandinfo(value sender, value band, value info)
{
    CAMLparam3(sender, band, info);
    REBARBANDINFO* rbbi;
    CAMLlocal2(contents, result);

    rbbi = new_rebarbandinfo(info);
    contents = pack_message(RB_SETBANDINFO, Int_val(band), (LPARAM)rbbi);
    result = callback(sender, contents);
    free_rebarbandinfo(rbbi);
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    CAMLreturn(Val_unit);
}

CAMLprim value issue_rb_setbarinfo(value sender, value iml)
{
    CAMLparam2(sender, iml);
    REBARINFO rbi;
    CAMLlocal2(contents, result);

    rbi.cbSize = sizeof(REBARINFO);
    rbi.fMask = RBIM_IMAGELIST;
    rbi.himl = Handle_val(iml);
    contents = pack_message(RB_SETBARINFO, 0, (LPARAM)&rbi);
    result = callback(sender, contents);
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    CAMLreturn(Val_unit);
}

CAMLprim value pack_rb_setbkcolor(value color)
{
    return pack_message(RB_SETBKCOLOR, 0, (LPARAM)color_val(color));
}

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

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

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

CAMLprim value pack_rb_setpalette(value palette)
{
    return pack_message(RB_SETPALETTE, 0, (LPARAM)Handle_val(palette));
}

CAMLprim value unpack_rb_setpalette(value result)
{
    return alloc_handle((HPALETTE)Int32_val(result));
}

CAMLprim value pack_rb_setparent(value parent)
{
    return pack_message(RB_SETPARENT, (LPARAM)Handle_val(parent), 0);
}

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

CAMLprim value pack_rb_settextcolor(value color)
{
    return pack_message(RB_SETTEXTCOLOR, 0, (LPARAM)color_val(color));
}

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

CAMLprim value pack_rb_settooltips(value tooltips)
{
    return pack_message(RB_SETTOOLTIPS, (WPARAM)Handle_val(tooltips), 0);
}

CAMLprim value pack_rb_showband(value band, value show)
{
    return pack_message(RB_SHOWBAND, Int_val(band), Bool_val(show));
}

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

CAMLprim value issue_rb_sizetorect(value sender, value rect)
{
    CAMLparam2(sender, rect);
    RECT r;
    CAMLlocal2(contents, result);

    rect_val(rect, &r);
    contents = pack_message(RB_SIZETORECT, 0, (LPARAM)&r);
    result = callback(sender, contents);
    CAMLreturn(Val_bool(result != 0));
}

/* Notifications */

CAMLprim value unpack_nmcustomdraw_rebar_item_spec(value msg)
{
    NMCUSTOMDRAW* nmcd;

    nmcd = (NMCUSTOMDRAW*)Lparam(msg);
    return Val_int(nmcd->dwItemSpec);
}

CAMLprim value unpack_nmrebar(value msg)
{
    CAMLparam1(msg);
    NMREBAR* nmrb;
    CAMLlocal3(val, v, vv);

    nmrb = (NMREBAR*)Lparam(msg);
    val = alloc_tuple(4);
    if (nmrb->uBand != -1) {
        v = alloc_some(Val_int(nmrb->uBand));
        Store_field(val, 0, v);
    } else
        Store_field(val, 0, Val_none);
    if (nmrb->dwMask & RBNM_STYLE) {
        vv = Alloc_flags(nmrb->fStyle, rebar_band_styles);
        v = alloc_some(vv);
        Store_field(val, 1, v);
    } else
        Store_field(val, 1, Val_none);
    if (nmrb->dwMask & RBNM_ID) {
        v = alloc_some(Val_int(nmrb->wID));
        Store_field(val, 2, v);
    } else
        Store_field(val, 2, Val_none);
    if (nmrb->dwMask & RBNM_LPARAM) {
        v = alloc_some(copy_int32(nmrb->lParam));
        Store_field(val, 3, v);
    } else
        Store_field(val, 3, Val_none);
    CAMLreturn(val);
}

CAMLprim value unpack_nmrbautosize(value msg)
{
    CAMLparam1(msg);
    NMRBAUTOSIZE* nmrbas;
    CAMLlocal2(val, v);

    nmrbas = (NMRBAUTOSIZE*)Lparam(msg);
    val = alloc_tuple(3);
    Store_field(val, 0, Val_bool(nmrbas->fChanged != 0));
    v = alloc_rect(&nmrbas->rcTarget);
    Store_field(val, 1, v);
    v = alloc_rect(&nmrbas->rcActual);
    Store_field(val, 2, v);
    CAMLreturn(val);
}

CAMLprim value on_rbn_childsize(value handler, value wnd, value msg)
{
    CAMLparam3(handler, wnd, msg);
    NMHDR* nmh;
    NMREBARCHILDSIZE* nmrbcs;
    CAMLlocalN(args, 5);
    CAMLlocal4(val, v, result, ret);

    ret = Val_none;
    if (Int_val(Field(msg, 0)) == WM_NOTIFY) {
        nmh = (NMHDR*)Lparam(msg);
        if (nmh->code == RBN_CHILDSIZE) {
            nmrbcs = (NMREBARCHILDSIZE*)nmh;
            val = alloc_tuple(4);
            if (nmrbcs->uBand != -1)
                v = alloc_some(Val_int(nmrbcs->uBand));
            else
                v = Val_none;
            Store_field(val, 0, v);
            Store_field(val, 1, Val_int(nmrbcs->wID));
            v = alloc_rect(&nmrbcs->rcChild);
            Store_field(val, 2, v);
            v = alloc_rect(&nmrbcs->rcBand);
            Store_field(val, 3, v);
            args[0] = wnd;
            args[1] = msg;
            args[2] = Val_int(Wparam(msg));
            args[3] = alloc_nmhdr(nmh);
            args[4] = val;
            result = callbackN(handler, 5, args);
            if (Is_block(result)) {
                if (Tag_val(result) == 0)
                    ret = alloc_some(Field(result, 0));
                else {
                    rect_val(Field(result, 0), &nmrbcs->rcChild);
                    ret = alloc_some(copy_int32(0));
                }
            }
        }
    }
    CAMLreturn(ret);
}
