/* OCaml-Win32
 * win32_header.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: Header Controls */

static DWORD hditem_justifications[] = {
    HDF_CENTER,
    HDF_LEFT,
    HDF_RIGHT
};
Define_static_constants(hditem_justifications);

static DWORD hditem_format_options[] = {
    HDF_BITMAP_ON_RIGHT,
    HDF_IMAGE,
    HDF_OWNERDRAW,
    HDF_RTLREADING,
    HDF_STRING
};
Define_static_constants(hditem_format_options);

static int hditemformat_val(value val)
{
    return
        Constant_val(Field(val, 0), hditem_justifications) |
        flags_val(Field(val, 1), hditem_format_options);
}

static value alloc_hditemformat(int format)
{
    CAMLparam0();
    CAMLlocal2(val, v);

    val = alloc_tuple(2);
    Store_field(val, 0, Val_constant(format & HDF_JUSTIFYMASK, hditem_justifications));
    v = Alloc_flags(format, hditem_format_options);
    Store_field(val, 1, v);
    CAMLreturn(val);
}

static HDITEM* new_hditem(value val)
{
    HDITEM* hdi;

    hdi = malloc(sizeof(HDITEM));
    memset(hdi, 0, sizeof(HDITEM));
    if (Is_some(Field(val, 0))) {
        hdi->mask |= HDI_WIDTH;
        hdi->cxy = Int_val(Value_of(Field(val, 0)));
    }
    if (Is_some(Field(val, 1))) {
        hdi->mask |= HDI_TEXT;
        hdi->pszText = new_commoncontroltext(Value_of(Field(val, 1)));
    }
    if (Is_some(Field(val, 2))) {
        hdi->mask |= HDI_BITMAP;
        hdi->hbm = Handle_val(Value_of(Field(val, 2)));
    }
    if (Is_some(Field(val, 3))) {
        hdi->mask |= HDI_FORMAT;
        hdi->fmt = hditemformat_val(Value_of(Field(val, 3)));
    }
    if (Is_some(Field(val, 4))) {
        hdi->mask |= HDI_LPARAM;
        hdi->lParam = Int32_val(Value_of(Field(val, 4)));
    }
    if (Is_some(Field(val, 5))) {
        hdi->mask |= HDI_IMAGE;
        hdi->iImage = commoncontrolimageindex_val(Value_of(Field(val, 5)));
    }
    if (Is_some(Field(val, 6))) {
        hdi->mask |= HDI_ORDER;
        hdi->iOrder = Int_val(Value_of(Field(val, 6)));
    }
    return hdi;
}

static void free_hditem(HDITEM* hdi)
{
    if (hdi->mask & HDI_TEXT)
        free_commoncontroltext(hdi->pszText);
    free(hdi);
}

static value alloc_hditem(HDITEM* hdi)
{
    CAMLparam0();
    CAMLlocal2(val, v);

    val = alloc_tuple(7);
    if (hdi->mask & HDI_WIDTH) {
        v = alloc_some(Val_int(hdi->cxy));
        Store_field(val, 0, v);
    } else
        Store_field(val, 0, Val_none);
    if (hdi->mask & HDI_TEXT) {
        v = alloc_some(alloc_commoncontroltext(hdi->pszText));
        Store_field(val, 1, v);
    } else
        Store_field(val, 1, Val_none);
    if (hdi->mask & HDI_BITMAP) {
        v = alloc_some(alloc_handle(hdi->hbm));
        Store_field(val, 2, v);
    } else
        Store_field(val, 2, Val_none);
    if (hdi->mask & HDI_FORMAT) {
        v = alloc_some(alloc_hditemformat(hdi->fmt));
        Store_field(val, 3, v);
    } else
        Store_field(val, 3, Val_none);
    if (hdi->mask & HDI_LPARAM) {
        v = alloc_some(copy_int32(hdi->lParam));
        Store_field(val, 4, v);
    } else
        Store_field(val, 4, Val_none);
    if (hdi->mask & HDI_IMAGE) {
        v = alloc_some(alloc_commoncontrolimageindex(hdi->iImage));
        Store_field(val, 5, v);
    } else
        Store_field(val, 5, Val_none);
    if (hdi->mask & HDI_ORDER) {
        v = alloc_some(Val_int(hdi->iOrder));
        Store_field(val, 6, v);
    } else
        Store_field(val, 6, Val_none);
    CAMLreturn(val);
}

static DWORD hditem_components_noparam[] = {
    HDI_WIDTH,
    HDI_BITMAP,
    HDI_FORMAT,
    HDI_LPARAM,
    HDI_IMAGE,
    HDI_ORDER
};
Define_static_constants(hditem_components_noparam);

/* Styles */

static DWORD header_style_options[] = {
    HDS_BUTTONS,
    HDS_DRAGDROP,
    HDS_FULLDRAG,
    HDS_HIDDEN,
    HDS_HORZ,
    HDS_HOTTRACK
};
Define_static_constants(header_style_options);

CAMLprim value header_style_of_option(value option)
{
    return copy_int64((int64)Constant_val(option, header_style_options));
}

CAMLprim value pack_hdm_createdragimage(value item)
{
    return pack_message(HDM_CREATEDRAGIMAGE, Int_val(item), 0);
}

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

CAMLprim value pack_hdm_deleteitem(value item)
{
    return pack_message(HDM_DELETEITEM, Int_val(item), 0);
}

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

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

CAMLprim value issue_hdm_getitem(value sender, value item, value mask)
{
    CAMLparam3(sender, item, mask);
    HDITEM hdi;
    CAMLlocal4(comp, contents, result, val);

    memset(&hdi, 0, sizeof(HDITEM));
    for (; !Is_nil(mask); mask = Tail(mask)) {
        comp = Head(mask);
        if (Is_long(comp))
            hdi.mask |= Constant_val(comp, hditem_components_noparam);
        else {
            hdi.mask |= HDI_TEXT;
            hdi.cchTextMax = Int_val(Field(comp, 0)) + 1;
        }
    }
    if (hdi.mask & HDI_TEXT)
        hdi.pszText = malloc(hdi.cchTextMax);
    contents = pack_message(HDM_GETITEM, Int_val(item), (LPARAM)&hdi);
    result = callback(sender, contents);
    if (Int32_val(result) == 0) {
        if (hdi.mask & HDI_TEXT)
            free(hdi.pszText);
        raise_win32_error(0);
    }
    val = alloc_hditem(&hdi);
    if (hdi.mask & HDI_TEXT)
        free(hdi.pszText);
    CAMLreturn(val);
}

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

CAMLprim value issue_hdm_getitemrect(value sender, value item)
{
    CAMLparam2(sender, item);
    RECT r;
    CAMLlocal3(contents, result, val);

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

CAMLprim value issue_hdm_getorderarray(value sender, value count)
{
    CAMLparam2(sender, count);
    int cnt, i;
    int* p;
    CAMLlocal3(contents, result, val);

    cnt = Int_val(count);
    if (cnt > 0)
        p = malloc(cnt * sizeof(int));
    else
        p = NULL;
    memset(p, 0, cnt * sizeof(int));
    contents = pack_message(HDM_GETORDERARRAY, cnt, (LPARAM)p);
    result = callback(sender, contents);
    if (Int32_val(result) == 0) {
        if (cnt > 0)
            free(p);
        raise_win32_error(0);
    }
    val = cnt > 0 ? alloc_tuple(cnt) : Atom(0);
    for (i = 0; i < cnt; ++i)
        Store_field(val, i, Val_int(p[i]));
    if (cnt > 0)
        free(p);
    CAMLreturn(val);
}

static DWORD header_hit_test_directions[] = {
    HHT_ABOVE,
    HHT_BELOW,
    HHT_TOLEFT,
    HHT_TORIGHT
};
Define_static_constants(header_hit_test_directions);

static DWORD header_hit_test_parts[] = {
    HHT_ONDIVIDER,
    HHT_ONDIVOPEN,
    HHT_ONHEADER
};
Define_static_constants(header_hit_test_parts);

CAMLprim value issue_hdm_hittest(value sender, value pt)
{
    CAMLparam2(sender, pt);
    HDHITTESTINFO hdhti;
    CAMLlocal4(contents, result, val, v);

    memset(&hdhti, 0, sizeof(hdhti));
    hdhti.pt = point_val(pt);
    contents = pack_message(HDM_HITTEST, 0, (LPARAM)&hdhti);
    result = callback(sender, contents);
    if (Int32_val(result) == -1) {
        if (hdhti.flags == HHT_NOWHERE)
            val = Val_int(0);
        else {
            val = alloc(1, 0);
            v = Alloc_flags(hdhti.flags, header_hit_test_directions);
            Store_field(val, 0, v);
        }
    } else {
        val = alloc(2, 1);
        Store_field(val, 0, Val_int(hdhti.iItem));
        Store_field(val, 1, Val_constant(hdhti.flags, header_hit_test_parts));
    }
    CAMLreturn(val);
}

CAMLprim value issue_hdm_insertitem(value sender, value item, value hditem)
{
    CAMLparam3(sender, item, hditem);
    HDITEM* hdi;
    CAMLlocal2(contents, result);

    hdi = new_hditem(hditem);
    contents = pack_message(HDM_INSERTITEM, Int_val(item), (LPARAM)hdi);
    result = callback(sender, contents);
    free_hditem(hdi);
    if (Int32_val(result) == -1)
        raise_win32_error(0);
    CAMLreturn(Val_int(Int32_val(result)));
}

CAMLprim value issue_hdm_layout(value sender, value rect)
{
    CAMLparam2(sender, rect);
    RECT r;
    WINDOWPOS wp;
    HDLAYOUT hdl;
    CAMLlocal3(contents, result, val);

    rect_val(rect, &r);
    hdl.prc = &r;
    hdl.pwpos = &wp;
    contents = pack_message(HDM_LAYOUT, 0, (LPARAM)&hdl);
    result = callback(sender, contents);
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    val = alloc_windowpos(&wp);
    CAMLreturn(val);
}

CAMLprim value pack_hdm_ordertoindex(value order)
{
    return pack_message(HDM_ORDERTOINDEX, Int_val(order), 0);
}

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

CAMLprim value pack_hdm_sethotdivider(value hot_spot)
{
    BOOL flag;
    DWORD dw;

    if (Tag_val(hot_spot) == 0) {
        flag = TRUE;
        dw = MAKELONG(X(Field(hot_spot, 0)), Y(Field(hot_spot, 0)));
    } else {
        flag = FALSE;
        dw = Int_val(Field(hot_spot, 0));
    }
    return pack_message(HDM_SETHOTDIVIDER, flag, dw);
}

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

CAMLprim value pack_hdm_setimagelist(value iml)
{
    return pack_message(HDM_SETIMAGELIST, 0, (LPARAM)Handle_val(iml));
}

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

CAMLprim value issue_hdm_setitem(value sender, value item, value hditem)
{
    CAMLparam3(sender, item, hditem);
    HDITEM* hdi;
    CAMLlocal2(contents, result);

    hdi = new_hditem(hditem);
    contents = pack_message(HDM_SETITEM, Int_val(item), (LPARAM)&hdi);
    result = callback(sender, contents);
    free_hditem(hdi);
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    CAMLreturn(Val_unit);
}

CAMLprim value issue_hdm_setorderarray(value sender, value order)
{
    CAMLparam2(sender, order);
    int cnt, i;
    int* p;
    CAMLlocal2(contents, result);

    cnt = Wosize_val(order);
    if (cnt > 0)
        p = malloc(cnt * sizeof(int));
    else
        p = NULL;
    for (i = 0; i < cnt; ++i)
        p[i] = Int_val(Field(order, i));
    contents = pack_message(HDM_SETORDERARRAY, cnt, (LPARAM)p);
    result = callback(sender, contents);
    if (cnt > 0)
        free(p);
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    CAMLreturn(Val_unit);
}

/* Notifications */

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

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

CAMLprim value unpack_nmheader(value msg)
{
    CAMLparam1(msg);
    NMHEADER* nmh;
    CAMLlocal2(val, v);

    nmh = (NMHEADER*)Lparam(msg);
    val = alloc_tuple(3);
    Store_field(val, 0, Val_int(nmh->iItem));
    Store_field(val, 1, Val_int(nmh->iButton));
    if (nmh->pitem)
        v = alloc_some(alloc_hditem(nmh->pitem));
    else
        v = Val_none;
    Store_field(val, 2, v);
    CAMLreturn(val);
}

CAMLprim value on_hdn_getdispinfo(value handler, value wnd, value msg)
{
    CAMLparam3(handler, wnd, msg);
    NMHDR* nmh;
    NMHDDISPINFO* nmhddi;
    BOOL has_text;
    CAMLlocalN(args, 6);
    CAMLlocal5(mask, result, pair, hddi, val);
    CAMLlocal1(ret);

    ret = Val_none;
    if (Int_val(Field(msg, 0)) == WM_NOTIFY) {
        nmh = (NMHDR*)Lparam(msg);
        if (nmh->code == HDN_GETDISPINFO) {
            nmhddi = (NMHDDISPINFO*)nmh;
            has_text =
                (nmhddi->mask & HDI_TEXT) &&
                nmhddi->pszText != NULL &&
                nmhddi->cchTextMax > 0;
            mask = Alloc_flags(nmhddi->mask, hditem_components_noparam);
            if (has_text) {
                val = alloc(1, 0);
                Store_field(val, 0, Val_int(nmhddi->cchTextMax - 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] = Val_int(nmhddi->iItem);
            args[5] = mask;
            result = callbackN(handler, 6, args);
            if (Is_block(result)) {
                if (Tag_val(result) == 0)
                    ret = alloc_some(Field(result, 0));
                else {
                    pair = Field(result, 0);
                    hddi = Field(pair, 0);
                    if (has_text && Is_some(Field(hddi, 0))) {
                        strncpy(
                            nmhddi->pszText,
                            String_val(Value_of(Field(hddi, 0))),
                            nmhddi->cchTextMax - 1);
                        nmhddi->pszText[nmhddi->cchTextMax-1] = '\0';
                    }
                    if (Is_some(Field(hddi, 1)))
                        nmhddi->iImage = Int_val(Value_of(Field(hddi, 1)));
                    if (Is_some(Field(hddi, 2)))
                        nmhddi->lParam = Int32_val(Value_of(Field(hddi, 2)));
                    if (Bool_val(Field(pair, 1)))
                        nmhddi->mask |= HDI_DI_SETITEM;
                    ret = alloc_some(copy_int32(0));
                }
            }
        }
    }
    CAMLreturn(ret);
}
