/* OCaml-Win32
 * win32_tabcontrol.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: Tab Controls */

static DWORD tabcontrol_extended_styles[] = {
    TCS_EX_FLATSEPARATORS,
    TCS_EX_REGISTERDROP
};
Define_static_constants(tabcontrol_extended_styles);

static void tcitemstate_val(value val, DWORD* state, DWORD* mask)
{
    *state = 0;
    *mask = 0;
    if (Is_some(Field(val, 0))) {
        *mask |= TCIS_BUTTONPRESSED;
        if (Bool_val(Value_of(Field(val, 0))))
            *state |= TCIS_BUTTONPRESSED;
    }
    if (Is_some(Field(val, 1))) {
        *mask |= TCIS_HIGHLIGHTED;
        if (Bool_val(Value_of(Field(val, 1))))
            *state |= TCIS_HIGHLIGHTED;
    }
}

static value alloc_tcitemstate(DWORD state, DWORD mask)
{
    CAMLparam0();
    CAMLlocal2(val, v);

    val = alloc_tuple(2);
    if (mask & TCIS_BUTTONPRESSED) {
        v = alloc_some(Val_bool((state & TCIS_BUTTONPRESSED) != 0));
        Store_field(val, 0, v);
    } else
        Store_field(val, 0, Val_none);
    if (mask & TCIS_HIGHLIGHTED) {
        v = alloc_some(Val_bool((state & TCIS_HIGHLIGHTED) != 0));
        Store_field(val, 1, v);
    } else
        Store_field(val, 1, Val_none);
    CAMLreturn(val);
}

static TCITEM* new_tcitem(value val)
{
    int size;
    TCITEM* tci;
    value v;

    size = sizeof(TCITEM);
    v = Field(val, 3);
    if (Is_some(v) && Tag_val(Value_of(v)) == 1)
        size = sizeof(TCITEMHEADER) + string_length(Field(Value_of(v), 0));
    tci = malloc(size);
    memset(tci, 0, size);
    if (Is_some(Field(val, 0))) {
        tci->mask |= TCIF_STATE;
        tcitemstate_val(Value_of(Field(val, 0)), &tci->dwState, &tci->dwStateMask);
    }
    if (Is_some(Field(val, 1))) {
        tci->mask |= TCIF_TEXT;
        tci->pszText = new_string(Value_of(Field(val, 1)));
    }
    if (Is_some(Field(val, 2))) {
        tci->mask |= TCIF_IMAGE;
        tci->iImage = Int_val(Value_of(Field(val, 2)));
    }
    if (Is_some(Field(val, 3))) {
        tci->mask |= TCIF_PARAM;
        v = Value_of(Field(val, 3));
        if (Tag_val(v) == 0)
            tci->lParam = Int32_val(Field(v, 0));
        else
            memcpy(&tci->lParam, String_val(Field(v, 0)), string_length(Field(v, 0)));
    }
    if (Bool_val(Field(val, 4)))
        tci->mask |= TCIF_RTLREADING;
    return tci;
}

static void free_tcitem(TCITEM* tci)
{
    if (tci->mask & TCIF_TEXT)
        free_string(tci->pszText);
    free(tci);
}

static value alloc_tcitem(TCITEM* tci, value param_type)
{
    CAMLparam0();
    CAMLlocal3(val, v, vv);
    int size;

    val = alloc_tuple(5);
    if (tci->mask & TCIF_STATE) {
        v = alloc_some(alloc_tcitemstate(tci->dwState, tci->dwStateMask));
        Store_field(val, 0, v);
    } else
        Store_field(val, 0, Val_none);
    if (tci->mask & TCIF_TEXT) {
        v = alloc_some(copy_string(tci->pszText));
        Store_field(val, 1, v);
    } else
        Store_field(val, 1, Val_none);
    if (tci->mask & TCIF_IMAGE) {
        v = alloc_some(Val_int(tci->iImage));
        Store_field(val, 2, v);
    } else
        Store_field(val, 2, Val_none);
    if ((tci->mask & TCIF_PARAM) && Is_some(param_type)) {
        if (Is_long(Value_of(param_type))) {
            v = alloc(1, 0);
            vv = copy_int32(tci->lParam);
            Store_field(v, 0, vv);
        } else {
            v = alloc(1, 1);
            size = Int_val(Field(Value_of(param_type), 0));
            vv = alloc_string(size);
            memcpy(String_val(vv), &tci->lParam, size);
            Store_field(v, 0, vv);
        }
        Store_field(val, 3, v);
    } else
        Store_field(val, 3, Val_none);
    Store_field(val, 4, Val_bool((tci->mask & TCIF_RTLREADING) != 0));
    CAMLreturn(val);
}

static DWORD tcitem_state_components[] = {
    TCIS_BUTTONPRESSED,
    TCIS_HIGHLIGHTED
};
Define_static_constants(tcitem_state_components);

/* Styles */

static DWORD tabcontrol_style_options[] = {
    TCS_BOTTOM,
    TCS_BUTTONS,
    TCS_FIXEDWIDTH,
    TCS_FLATBUTTONS,
    TCS_FOCUSNEVER,
    TCS_FOCUSONBUTTONDOWN,
    TCS_FORCEICONLEFT,
    TCS_FORCELABELLEFT,
    TCS_HOTTRACK,
    TCS_MULTILINE,
    TCS_MULTISELECT,
    TCS_OWNERDRAWFIXED,
    TCS_RAGGEDRIGHT,
    TCS_RIGHT,
    TCS_RIGHTJUSTIFY,
    TCS_SCROLLOPPOSITE,
    TCS_SINGLELINE,
    TCS_TABS,
    TCS_TOOLTIPS,
    TCS_VERTICAL
};
Define_static_constants(tabcontrol_style_options);

CAMLprim value tabcontrol_style_of_option(value option)
{
    return copy_int64((int64)Constant_val(option, tabcontrol_style_options));
}

/* Messages */

CAMLprim value issue_tcm_adjustrect(value sender, value larger, value rect)
{
    CAMLparam3(sender, larger, rect);
    RECT r;
    CAMLlocal2(contents, val);

    rect_val(rect, &r);
    contents = pack_message(TCM_ADJUSTRECT, Bool_val(larger), (LPARAM)&r);
    callback(sender, contents);
    val = alloc_rect(&r);
    CAMLreturn(val);
}

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

CAMLprim value pack_tcm_deleteitem(value item)
{
    return pack_message(TCM_DELETEITEM, Int_val(item), 0);
}

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

CAMLprim value pack_tcm_deselectall(value exclude_focus)
{
    return pack_message(TCM_DESELECTALL, Bool_val(exclude_focus), 0);
}

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

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

CAMLprim value unpack_tcm_getextendedstyle(value result)
{
    return Alloc_flags(Int32_val(result), tabcontrol_extended_styles);
}

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

CAMLprim value issue_tcm_getitem(value sender, value item, value mask)
{
    CAMLparam3(sender, item, mask);
    DWORD m, sm;
    int size, text_size;
    TCITEM* tci;
    CAMLlocal5(comp, param_type, contents, result, val);

    m = 0;
    sm = 0;
    size = sizeof(TCITEM);
    text_size = 0;
    param_type = Val_none;
    for (; !Is_nil(mask); mask = Tail(mask)) {
        comp = Head(mask);
        if (Is_long(comp))
            m |= TCIF_IMAGE;
        else if (Tag_val(comp) == 0) {
            m |= TCIF_STATE;
            sm = flags_val(Field(comp, 0), tcitem_state_components);
        } else if (Tag_val(comp) == 1) {
            m |= TCIF_TEXT;
            text_size = Int_val(Field(comp, 0)) + 1;
        } else {
            m |= TCIF_PARAM;
            val = Field(comp, 0);
            if (Is_block(val))
                size = sizeof(TCITEMHEADER) + Int_val(Field(val, 0));
            param_type = alloc_some(val);
        }
    }
    tci = malloc(size);
    tci->mask = m;
    tci->dwStateMask = sm;
    tci->cchTextMax = text_size;
    if (tci->mask & TCIF_TEXT)
        tci->pszText = malloc(tci->cchTextMax);
    contents = pack_message(TCM_GETITEM, Int_val(item), (LPARAM)tci);
    result = callback(sender, contents);
    if (Int32_val(result) == 0) {
        if (tci->mask & TCIF_TEXT)
            free(tci->pszText);
        free(tci);
        raise_win32_error(0);
    }
    val = alloc_tcitem(tci, param_type);
    if (tci->mask & TCIF_TEXT)
        free(tci->pszText);
    free(tci);
    CAMLreturn(val);
}

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

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

    contents = pack_message(TCM_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 unpack_tcm_getrowcount(value result)
{
    return Val_int(Int32_val(result));
}

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

CAMLprim value pack_tcm_highlightitem(value item, value highlight)
{
    return pack_message(TCM_HIGHLIGHTITEM, Int_val(item), Bool_val(highlight));
}

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

static DWORD tabcontrol_hit_test_parts[] = {
    TCHT_ONITEM,
    TCHT_ONITEMICON,
    TCHT_ONITEMLABEL
};
Define_static_constants(tabcontrol_hit_test_parts);

CAMLprim value issue_tcm_hittest(value sender, value pt)
{
    CAMLparam2(sender, pt);
    TCHITTESTINFO tchti;
    CAMLlocal3(contents, result, val);

    memset(&tchti, 0, sizeof(TCHITTESTINFO));
    tchti.pt = point_val(pt);
    contents = pack_message(TCM_HITTEST, 0, (LPARAM)&tchti);
    result = callback(sender, contents);
    if (tchti.flags == TCHT_NOWHERE)
        val = Val_int(0);
    else {
        val = alloc(2, 0);
        Store_field(val, 0, Val_int(Int32_val(result)));
        Store_field(val, 1, Val_constant(tchti.flags, tabcontrol_hit_test_parts));
    }
    CAMLreturn(val);
}

CAMLprim value issue_tcm_insertitem(value sender, value item, value tcitem)
{
    CAMLparam3(sender, item, tcitem);
    TCITEM* tci;
    CAMLlocal2(contents, result);

    tci = new_tcitem(tcitem);
    contents = pack_message(TCM_INSERTITEM, Int_val(item), (LPARAM)tci);
    result = callback(sender, contents);
    free_tcitem(tci);
    if (Int32_val(result) == -1)
        raise_win32_error(0);
    CAMLreturn(Val_int(Int32_val(result)));
}

CAMLprim value pack_tcm_removeimage(value image)
{
    return pack_message(TCM_REMOVEIMAGE, Int_val(image), 0);
}

CAMLprim value pack_tcm_setcurfocus(value item)
{
    return pack_message(TCM_SETCURFOCUS, Int_val(item), 0);
}

CAMLprim value pack_tcm_setcursel(value item)
{
    return pack_message(TCM_SETCURSEL, Int_val(item), 0);
}

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

CAMLprim value pack_tcm_setextendedstyle(value mask, value style)
{
    return pack_message(
        TCM_SETEXTENDEDSTYLE,
        flags_val(mask, tabcontrol_extended_styles),
        flags_val(style, tabcontrol_extended_styles));
}

CAMLprim value unpack_tcm_setextendedstyle(value result)
{
    return Alloc_flags(Int32_val(result), tabcontrol_extended_styles);
}

CAMLprim value pack_tcm_setimagelist(value iml)
{
    return pack_message(TCM_SETIMAGELIST, 0, (LPARAM)Handle_val(iml));
}

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

CAMLprim value issue_tcm_setitem(value sender, value item, value tcitem)
{
    CAMLparam3(sender, item, tcitem);
    TCITEM* tci;
    CAMLlocal2(contents, result);

    tci = new_tcitem(tcitem);
    contents = pack_message(TCM_SETITEM, Int_val(item), (LPARAM)tci);
    result = callback(sender, contents);
    free_tcitem(tci);
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    CAMLreturn(Val_unit);
}

CAMLprim value pack_tcm_setitemextra(value extra)
{
    return pack_message(TCM_SETITEMEXTRA, Int_val(extra), 0);
}

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

CAMLprim value pack_tcm_setitemsize(value size)
{
    return pack_message(TCM_SETITEMSIZE, 0, MAKELPARAM(Cx(size), Cy(size)));
}

CAMLprim value unpack_tcm_setitemsize(value result)
{
    SIZE sz;

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

CAMLprim value pack_tcm_setmintabwidth(value width)
{
    return pack_message(TCM_SETMINTABWIDTH, 0, Int_val(width));
}

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

CAMLprim value pack_tcm_setpadding(value padding)
{
    return pack_message(TCM_SETPADDING, 0, MAKELPARAM(Cx(padding), Cy(padding)));
}

CAMLprim value pack_tcm_settooltips(value tooltips)
{
    return pack_message(TCM_SETTOOLTIPS, (WPARAM)Handle_val(tooltips), 0);
}

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

/* Notifications */

CAMLprim value unpack_nmtckeydown(value msg)
{
    CAMLparam1(msg);
    NMTCKEYDOWN* nmtckd;
    CAMLlocal2(val, v);

    nmtckd = (NMTCKEYDOWN*)Lparam(msg);
    val = alloc_tuple(2);
    v = alloc_virtualkeycode((BYTE)nmtckd->wVKey);
    Store_field(val, 0, v);
    v = alloc_keydata(nmtckd->flags);
    Store_field(val, 1, v);
    CAMLreturn(val);
}
