/* OCaml-Win32
 * win32_comboboxex.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: ComboBoxEx Controls */

static DWORD comboboxex_extended_styles[] = {
    CBES_EX_CASESENSITIVE,
    CBES_EX_NOEDITIMAGE,
    CBES_EX_NOEDITIMAGEINDENT,
    CBES_EX_NOSIZELIMIT,
    CBES_EX_PATHWORDBREAKPROC
};
Define_static_constants(comboboxex_extended_styles);

static COMBOBOXEXITEM* new_comboboxexitem(value val)
{
    COMBOBOXEXITEM* cbei;

    cbei = malloc(sizeof(COMBOBOXEXITEM));
    memset(cbei, 0, sizeof(COMBOBOXEXITEM));
    cbei->iItem = Int_val(Field(val, 0));
    if (Is_some(Field(val, 1))) {
        cbei->mask |= CBEIF_TEXT;
        cbei->pszText = new_commoncontroltext(Value_of(Field(val, 1)));
    }
    if (Is_some(Field(val, 2))) {
        cbei->mask |= CBEIF_IMAGE;
        cbei->iImage = commoncontrolimageindex_val(Value_of(Field(val, 2)));
    }
    if (Is_some(Field(val, 3))) {
        cbei->mask |= CBEIF_SELECTEDIMAGE;
        cbei->iSelectedImage = commoncontrolimageindex_val(Value_of(Field(val, 3)));
    }
    if (Is_some(Field(val, 4))) {
        cbei->mask |= CBEIF_OVERLAY;
        cbei->iOverlay = commoncontrolimageindex_val(Value_of(Field(val, 4)));
    }
    if (Is_some(Field(val, 5))) {
        cbei->mask |= CBEIF_INDENT;
        cbei->iIndent = commoncontrolindentamount_val(Value_of(Field(val, 5)));
    }
    if (Is_some(Field(val, 6))) {
        cbei->mask |= CBEIF_LPARAM;
        cbei->lParam = Int32_val(Value_of(Field(val, 6)));
    }
    return cbei;
}

static void free_comboboxexitem(COMBOBOXEXITEM* cbei)
{
    if (cbei->mask & CBEIF_TEXT)
        free_commoncontroltext(cbei->pszText);
    free(cbei);
}

static value alloc_comboboxexitem(COMBOBOXEXITEM* cbei)
{
    CAMLparam0();
    CAMLlocal2(val, v);

    val = alloc_tuple(7);
    Store_field(val, 0, Val_int(cbei->iItem));
    if (cbei->mask & CBEIF_TEXT) {
        v = alloc_some(alloc_commoncontroltext(cbei->pszText));
        Store_field(val, 1, v);
    } else
        Store_field(val, 1, Val_none);
    if (cbei->mask & CBEIF_IMAGE) {
        v = alloc_some(alloc_commoncontrolimageindex(cbei->iImage));
        Store_field(val, 2, v);
    } else
        Store_field(val, 2, Val_none);
    if (cbei->mask & CBEIF_SELECTEDIMAGE) {
        v = alloc_some(alloc_commoncontrolimageindex(cbei->iSelectedImage));
        Store_field(val, 3, v);
    } else
        Store_field(val, 3, Val_none);
    if (cbei->mask & CBEIF_OVERLAY) {
        v = alloc_some(alloc_commoncontrolimageindex(cbei->iOverlay));
        Store_field(val, 4, v);
    } else
        Store_field(val, 4, Val_none);
    if (cbei->mask & CBEIF_INDENT) {
        v = alloc_some(alloc_commoncontrolindentamount(cbei->iIndent));
        Store_field(val, 5, v);
    } else
        Store_field(val, 5, Val_none);
    if (cbei->mask & CBEIF_LPARAM) {
        v = alloc_some(copy_int32(cbei->lParam));
        Store_field(val, 6, v);
    } else
        Store_field(val, 6, Val_none);
    CAMLreturn(val);
}

static DWORD comboboxexitem_components_noparam[] = {
    CBEIF_IMAGE,
    CBEIF_SELECTEDIMAGE,
    CBEIF_OVERLAY,
    CBEIF_INDENT,
    CBEIF_LPARAM
};
Define_static_constants(comboboxexitem_components_noparam);

/* Messages */

CAMLprim value pack_cbem_deleteitem(value item)
{
    return pack_message(CBEM_DELETEITEM, Int_val(item), 0);
}

CAMLprim value unpack_cbem_deleteitem(value result)
{
    return Val_int(check_combo_box_result(Int32_val(result)));
}

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

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

CAMLprim value unpack_cbem_getextendedstyle(value result)
{
    return Alloc_flags(Int32_val(result), comboboxex_extended_styles);
}

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

CAMLprim value issue_cbem_getitem(value sender, value item, value mask)
{
    CAMLparam3(sender, item, mask);
    COMBOBOXEXITEM cbei;
    CAMLlocal4(comp, contents, result, val);

    memset(&cbei, 0, sizeof(COMBOBOXEXITEM));
    for (; !Is_nil(mask); mask = Tail(mask)) {
        comp = Head(mask);
        if (Is_long(comp))
            cbei.mask |= Constant_val(comp, comboboxexitem_components_noparam);
        else {
            cbei.mask |= CBEIF_TEXT;
            cbei.cchTextMax = Int_val(Field(comp, 0)) + 1;
        }
    }
    cbei.iItem = Int_val(item);
    if (cbei.mask & CBEIF_TEXT)
        cbei.pszText = malloc(cbei.cchTextMax);
    contents = pack_message(CBEM_GETITEM, 0, (LPARAM)&cbei);
    result = callback(sender, contents);
    if (Int32_val(result) == 0) {
        if (cbei.mask & CBEIF_TEXT)
            free(cbei.pszText);
        raise_win32_error(0);
    }
    val = alloc_comboboxexitem(&cbei);
    if (cbei.mask & CBEIF_TEXT)
        free(cbei.pszText);
    CAMLreturn(val);
}

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

CAMLprim value issue_cbem_insertitem(value sender, value comboboxexitem)
{
    CAMLparam2(sender, comboboxexitem);
    COMBOBOXEXITEM* cbei;
    CAMLlocal2(contents, result);

    cbei = new_comboboxexitem(comboboxexitem);
    contents = pack_message(CBEM_INSERTITEM, 0, (LPARAM)cbei);
    result = callback(sender, contents);
    free_comboboxexitem(cbei);
    if (Int32_val(result) == -1)
        raise_win32_error(0);
    CAMLreturn(Val_int(Int32_val(result)));
}

CAMLprim value pack_cbem_setextendedstyle(value mask, value style)
{
    return pack_message(
        CBEM_SETEXTENDEDSTYLE,
        flags_val(mask, comboboxex_extended_styles),
        flags_val(style, comboboxex_extended_styles));
}

CAMLprim value unpack_cbem_setextendedstyle(value result)
{
    return Alloc_flags(Int32_val(result), comboboxex_extended_styles);
}

CAMLprim value pack_cbem_setimagelist(value iml)
{
    return pack_message(CBEM_SETIMAGELIST, 0, (LPARAM)Handle_val(iml));
}

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

CAMLprim value issue_cbem_setitem(value sender, value comboboxexitem)
{
    CAMLparam2(sender, comboboxexitem);
    COMBOBOXEXITEM* cbei;
    CAMLlocal2(contents, result);

    cbei = new_comboboxexitem(comboboxexitem);
    contents = pack_message(CBEM_SETITEM, 0, (LPARAM)cbei);
    result = callback(sender, contents);
    free_comboboxexitem(cbei);
    if (Int32_val(result) == -1)
        raise_win32_error(0);
    CAMLreturn(Val_unit);
}

/* Notifications */

CAMLprim value unpack_nmcomboboxex(value msg)
{
    CAMLparam1(msg);
    NMCOMBOBOXEX* nmcbe;
    CAMLlocal2(val, v);

    nmcbe = (NMCOMBOBOXEX*)Lparam(msg);
    val = alloc_tuple(1);
    v = alloc_comboboxexitem(&nmcbe->ceItem);
    Store_field(val, 0, v);
    CAMLreturn(val);
}

CAMLprim value unpack_nmcbedragbegin(value msg)
{
    CAMLparam1(msg);
    NMCBEDRAGBEGIN* nmcbedb;
    CAMLlocal2(val, v);

    nmcbedb = (NMCBEDRAGBEGIN*)Lparam(msg);
    val = alloc_tuple(2);
    Store_field(val, 0, Val_int(nmcbedb->iItemid));
    v = copy_string(nmcbedb->szText);
    Store_field(val, 1, v);
    CAMLreturn(val);
}

static DWORD comboboxex_end_edit_reasons[] = {
    CBENF_DROPDOWN,
    CBENF_ESCAPE,
    CBENF_KILLFOCUS,
    CBENF_RETURN
};
Define_static_constants(comboboxex_end_edit_reasons);

CAMLprim value unpack_nmcbeendedit(value msg)
{
    CAMLparam1(msg);
    NMCBEENDEDIT* nmcbeee;
    CAMLlocal2(val, v);

    nmcbeee = (NMCBEENDEDIT*)Lparam(msg);
    val = alloc_tuple(4);
    Store_field(val, 0, Val_bool(nmcbeee->fChanged));
    if (nmcbeee->iNewSelection == CB_ERR)
        v = Val_none;
    else
        v = alloc_some(Val_int(nmcbeee->iNewSelection));
    Store_field(val, 1, v);
    v = copy_string(nmcbeee->szText);
    Store_field(val, 2, v);
    Store_field(val, 3, Val_constant(nmcbeee->iWhy, comboboxex_end_edit_reasons));
    CAMLreturn(val);
}

CAMLprim value on_cben_getdispinfo(value handler, value wnd, value msg)
{
    CAMLparam3(handler, wnd, msg);
    NMHDR* nmh;
    NMCOMBOBOXEX* nmcbe;
    BOOL has_text;
    CAMLlocalN(args, 6);
    CAMLlocal5(mask, result, pair, cbei, val);
    CAMLlocal1(ret);

    ret = Val_none;
    if (Int_val(Field(msg, 0)) == WM_NOTIFY) {
        nmh = (NMHDR*)Lparam(msg);
        if (nmh->code == CBEN_GETDISPINFO) {
            nmcbe = (NMCOMBOBOXEX*)nmh;
            has_text =
                (nmcbe->ceItem.mask & CBEIF_TEXT) &&
                nmcbe->ceItem.pszText != NULL &&
                nmcbe->ceItem.cchTextMax > 0;
            mask = Alloc_flags(nmcbe->ceItem.mask, comboboxexitem_components_noparam);
            if (has_text) {
                val = alloc(1, 0);
                Store_field(val, 0, Val_int(nmcbe->ceItem.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(nmcbe->ceItem.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);
                    cbei = Field(pair, 0);
                    if (has_text && Is_some(Field(cbei, 1))) {
                        val = Value_of(Field(cbei, 1));
                        if (Is_block(val)) {
                            strncpy(
                                nmcbe->ceItem.pszText,
                                String_val(Field(val, 0)),
                                nmcbe->ceItem.cchTextMax - 1);
                            nmcbe->ceItem.pszText[nmcbe->ceItem.cchTextMax-1] = '\0';
                        } else
                            nmcbe->ceItem.pszText[0] = '\0';
                    }
                    if (Is_some(Field(cbei, 2)))
                        nmcbe->ceItem.iImage = commoncontrolimageindex_val(Value_of(Field(cbei, 2)));
                    if (Is_some(Field(cbei, 3)))
                        nmcbe->ceItem.iSelectedImage = commoncontrolimageindex_val(Value_of(Field(cbei, 3)));
                    if (Is_some(Field(cbei, 4)))
                        nmcbe->ceItem.iOverlay = commoncontrolimageindex_val(Value_of(Field(cbei, 4)));
                    if (Is_some(Field(cbei, 5)))
                        nmcbe->ceItem.iIndent = commoncontrolindentamount_val(Value_of(Field(cbei, 5)));
                    if (Bool_val(Field(pair, 1)))
                        nmcbe->ceItem.mask |= CBEIF_DI_SETITEM;
                    ret = alloc_some(copy_int32(0));
                }
            }
        }
    }
    CAMLreturn(ret);
}
