/* OCaml-Win32
 * win32_tooltip.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: ToolTip Controls */

static DWORD tool_options[] = {
    TTF_ABSOLUTE,
    TTF_CENTERTIP,
    TTF_RTLREADING,
    TTF_SUBCLASS,
    TTF_TRACK,
    TTF_TRANSPARENT
};
Define_static_constants(tool_options);

static TOOLINFO* new_toolinfo(value val)
{
    TOOLINFO* ti;
    value v;

    ti = malloc(sizeof(TOOLINFO));
    memset(ti, 0, sizeof(TOOLINFO));
    ti->cbSize = sizeof(TOOLINFO);
    ti->uFlags = flags_val(Field(val, 0), tool_options);
    ti->hwnd = Handle_val(Field(val, 1));
    v = Field(val, 2);
    if (Tag_val(v) == 0) {
        ti->uId = Int_val(Field(v, 0));
        rect_val(Field(v, 1), &ti->rect);
    } else {
        ti->uFlags |= TTF_IDISHWND;
        ti->uId = (UINT)Handle_val(Field(val, 0));
    }
    v = Field(val, 3);
    if (Is_block(v)) {
        if (Tag_val(v) == 0)
            ti->lpszText = new_string(Field(v, 0));
        else {
            ti->hinst = Handle_val(Field(v, 0));
            ti->lpszText = (LPTSTR)Int_val(Field(v, 1));
        }
    } else {
        if (Int_val(v) == 0)
            ti->lpszText = NULL;
        else
            ti->lpszText = LPSTR_TEXTCALLBACK;
    }
    ti->lParam = Int32_val(Field(val, 4));
    return ti;
}

static void free_toolinfo(TOOLINFO* ti)
{
    if (ti->lpszText != NULL && ti->lpszText != LPSTR_TEXTCALLBACK &&
            HIWORD((DWORD)ti->lpszText) != 0)
        free_string(ti->lpszText);
    free(ti);
}

static value alloc_toolinfo(TOOLINFO* ti)
{
    CAMLparam0();
    CAMLlocal3(val, v, vv);

    val = alloc_tuple(5);
    v = Alloc_flags(ti->uFlags, tool_options);
    Store_field(val, 0, v);
    v = alloc_handle(ti->hwnd);
    Store_field(val, 1, v);
    if (ti->uFlags & TTF_IDISHWND) {
        v = alloc(1, 1);
        vv = alloc_handle((HWND)ti->uId);
        Store_field(v, 0, vv);
    } else {
        v = alloc(2, 0);
        Store_field(v, 0, Val_int(ti->uId));
        vv = alloc_rect(&ti->rect);
        Store_field(v, 1, vv);
    }
    Store_field(val, 2, v);
    if (ti->lpszText == NULL)
        v = Val_int(0);
    else if (ti->lpszText == LPSTR_TEXTCALLBACK)
        v = Val_int(1);
    else if (HIWORD((DWORD)ti->lpszText) != 0) {
        v = alloc(1, 0);
        vv = copy_string(ti->lpszText);
        Store_field(v, 0, vv);
    } else {
        v = alloc(2, 1);
        vv = alloc_handle(ti->hinst);
        Store_field(v, 0, vv);
        Store_field(v, 1, Val_int((int)ti->lpszText));
    }
    Store_field(val, 3, v);
    v = copy_int32(ti->lParam);
    Store_field(val, 4, v);
    CAMLreturn(val);
}

static DWORD tooltip_delay_times[] = {
    TTDT_AUTOPOP,
    TTDT_INITIAL,
    TTDT_RESHOW
};
Define_static_constants(tooltip_delay_times);

/* Styles */

static DWORD tooltip_style_options[] = {
    TTS_ALWAYSTIP,
    TTS_NOPREFIX
};
Define_static_constants(tooltip_style_options);

CAMLprim value tooltip_style_of_option(value option)
{
    return copy_int64((int64)Constant_val(option, tooltip_style_options));
}

/* Messages */

CAMLprim value pack_ttm_activate(value activate)
{
    return pack_message(TTM_ACTIVATE, Bool_val(activate), 0);
}

CAMLprim value issue_ttm_addtool(value sender, value toolinfo)
{
    CAMLparam2(sender, toolinfo);
    TOOLINFO* ti;
    CAMLlocal2(contents, result);

    ti = new_toolinfo(toolinfo);
    contents = pack_message(TTM_ADDTOOL, 0, (LPARAM)ti);
    result = callback(sender, contents);
    free_toolinfo(ti);
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    CAMLreturn(Val_unit);
}

CAMLprim value issue_ttm_deltool(value sender, value wnd, value id)
{
    CAMLparam3(sender, wnd, id);
    TOOLINFO ti;
    CAMLlocal1(contents);

    memset(&ti, 0, sizeof(TOOLINFO));
    ti.cbSize = sizeof(TOOLINFO);
    ti.hwnd = Handle_val(wnd);
    if (Tag_val(id) == 0)
        ti.uId = Int_val(Field(id, 0));
    else
        ti.uId = (UINT)Handle_val(Field(id, 0));
    contents = pack_message(TTM_DELTOOL, 0, (LPARAM)&ti);
    callback(sender, contents);
    CAMLreturn(Val_unit);
}

CAMLprim value issue_ttm_enumtools(value sender, value index, value get_text)
{
    CAMLparam3(sender, index, get_text);
    char* buf;
    TOOLINFO ti;
    CAMLlocal3(contents, result, val);

    if (Is_block(get_text))
        buf = malloc(Int_val(Field(get_text, 0)) + 1);
    else
        buf = NULL;
    memset(&ti, 0, sizeof(TOOLINFO));
    ti.cbSize = sizeof(TOOLINFO);
    ti.lpszText = buf;
    contents = pack_message(TTM_ENUMTOOLS, Int_val(index), (LPARAM)&ti);
    result = callback(sender, contents);
    if (Int32_val(result) == 0) {
        if (buf != NULL)
            free(buf);
        raise_win32_error(0);
    }
    val = alloc_toolinfo(&ti);
    if (buf != NULL)
        free(buf);
    CAMLreturn(val);
}

CAMLprim value issue_ttm_getcurrenttool(value sender, value get_text)
{
    CAMLparam2(sender, get_text);
    char* buf;
    TOOLINFO ti;
    CAMLlocal3(contents, result, val);

    if (Is_block(get_text))
        buf = malloc(Int_val(Field(get_text, 0)) + 1);
    else
        buf = NULL;
    memset(&ti, 0, sizeof(TOOLINFO));
    ti.cbSize = sizeof(TOOLINFO);
    ti.lpszText = buf;
    contents = pack_message(TTM_GETCURRENTTOOL, 0, (LPARAM)&ti);
    result = callback(sender, contents);
    if (Int32_val(result) != 0)
        val = alloc_some(alloc_toolinfo(&ti));
    else
        val = Val_none;
    if (buf != NULL)
        free(buf);
    CAMLreturn(val);
}

CAMLprim value pack_ttm_getdelaytime(value which)
{
    return pack_message(TTM_GETDELAYTIME, Constant_val(which, tooltip_delay_times), 0);
}

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

CAMLprim value issue_ttm_getmargin(value sender)
{
    CAMLparam1(sender);
    RECT r;
    CAMLlocal2(contents, val);

    contents = pack_message(TTM_GETMARGIN, 0, (LPARAM)&r);
    callback(sender, contents);
    val = alloc_rect(&r);
    CAMLreturn(val);
}

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

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

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

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

CAMLprim value issue_ttm_gettoolinfo(value sender, value wnd, value id, value get_text)
{
    CAMLparam4(sender, wnd, id, get_text);
    char* buf;
    TOOLINFO ti;
    CAMLlocal3(contents, result, val);

    if (Is_block(get_text))
        buf = malloc(Int_val(Field(get_text, 0)) + 1);
    else
        buf = NULL;
    memset(&ti, 0, sizeof(TOOLINFO));
    ti.cbSize = sizeof(TOOLINFO);
    ti.hwnd = Handle_val(wnd);
    if (Tag_val(id) == 0)
        ti.uId = Int_val(Field(id, 0));
    else
        ti.uId = (UINT)Handle_val(Field(id, 0));
    ti.lpszText = buf;
    contents = pack_message(TTM_GETTOOLINFO, 0, (LPARAM)&ti);
    result = callback(sender, contents);
    if (Int32_val(result) == 0) {
        if (buf != NULL)
            free(buf);
        raise_win32_error(0);
    }
    val = alloc_toolinfo(&ti);
    if (buf != NULL)
        free(buf);
    CAMLreturn(val);
}

CAMLprim value issue_ttm_hittest(value sender, value wnd, value pt, value get_text)
{
    CAMLparam4(sender, wnd, pt, get_text);
    char* buf;
    TTHITTESTINFO tthti;
    CAMLlocal3(contents, result, val);

    if (Is_block(get_text))
        buf = malloc(Int_val(Field(get_text, 0)) + 1);
    else
        buf = NULL;
    tthti.hwnd = Handle_val(wnd);
    tthti.pt = point_val(pt);
    memset(&tthti.ti, 0, sizeof(TOOLINFO));
    tthti.ti.cbSize = sizeof(TOOLINFO);
    tthti.ti.lpszText = buf;
    contents = pack_message(TTM_HITTEST, 0, (LPARAM)&tthti);
    result = callback(sender, contents);
    if (Int32_val(result) != 0)
        val = alloc_some(alloc_toolinfo(&tthti.ti));
    else
        val = Val_none;
    if (buf != NULL)
        free(buf);
    CAMLreturn(val);
}

CAMLprim value issue_ttm_newtoolrect(value sender, value wnd, value id, value rect)
{
    CAMLparam4(sender, wnd, id, rect);
    TOOLINFO ti;
    CAMLlocal1(contents);

    memset(&ti, 0, sizeof(TOOLINFO));
    ti.cbSize = sizeof(TOOLINFO);
    ti.hwnd = Handle_val(wnd);
    if (Tag_val(id) == 0)
        ti.uId = Int_val(Field(id, 0));
    else
        ti.uId = (UINT)Handle_val(Field(id, 0));
    rect_val(rect, &ti.rect);
    contents = pack_message(TTM_NEWTOOLRECT, 0, (LPARAM)&ti);
    callback(sender, contents);
    CAMLreturn(Val_unit);
}

CAMLprim value issue_ttm_relayevent(value sender, value msg)
{
    CAMLparam2(sender, msg);
    MSG m;
    CAMLlocal1(contents);

    msg_val(msg, &m);
    contents = pack_message(TTM_RELAYEVENT, 0, (LPARAM)&m);
    callback(sender, contents);
    CAMLreturn(Val_unit);
}

CAMLprim value pack_ttm_setdelaytime(value which, value time)
{
    return pack_message(TTM_SETDELAYTIME, Constant_val(which, tooltip_delay_times), Int_val(time));
}

CAMLprim value issue_ttm_setmargin(value sender, value margin)
{
    CAMLparam2(sender, margin);
    RECT r;
    CAMLlocal1(contents);

    rect_val(margin, &r);
    contents = pack_message(TTM_SETMARGIN, 0, (LPARAM)&r);
    callback(sender, contents);
    CAMLreturn(Val_unit);
}

CAMLprim value pack_ttm_setmaxtipwidth(value width)
{
    return pack_message(TTM_SETMAXTIPWIDTH, 0, Int_val(width));
}

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

CAMLprim value pack_ttm_settipbkcolor(value color)
{
    return pack_message(TTM_SETTIPBKCOLOR, (WPARAM)color_val(color), 0);
}

CAMLprim value pack_ttm_settiptextcolor(value color)
{
    return pack_message(TTM_SETTIPTEXTCOLOR, (WPARAM)color_val(color), 0);
}

CAMLprim value issue_ttm_settoolinfo(value sender, value toolinfo)
{
    CAMLparam2(sender, toolinfo);
    TOOLINFO* ti;
    CAMLlocal1(contents);

    ti = new_toolinfo(toolinfo);
    contents = pack_message(TTM_SETTOOLINFO, 0, (LPARAM)ti);
    callback(sender, contents);
    free_toolinfo(ti);
    CAMLreturn(Val_unit);
}

CAMLprim value issue_ttm_trackactivate(value sender, value wnd, value id, value activate)
{
    CAMLparam4(sender, wnd, id, activate);
    TOOLINFO ti;
    CAMLlocal1(contents);

    memset(&ti, 0, sizeof(TOOLINFO));
    ti.cbSize = sizeof(TOOLINFO);
    ti.hwnd = Handle_val(wnd);
    if (Tag_val(id) == 0)
        ti.uId = Int_val(Field(id, 0));
    else
        ti.uId = (UINT)Handle_val(Field(id, 0));
    contents = pack_message(TTM_TRACKACTIVATE, Bool_val(activate), (LPARAM)&ti);
    callback(sender, contents);
    CAMLreturn(Val_unit);
}

CAMLprim value pack_ttm_trackposition(value pos)
{
    return pack_message(TTM_TRACKPOSITION, 0, MAKELONG(X(pos), Y(pos)));
}

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

    p = point_val(pt);
    contents = pack_message(TTM_WINDOWFROMPOINT, 0, (LPARAM)&p);
    result = callback(sender, contents);
    val = alloc_handle((HWND)Int32_val(result));
    CAMLreturn(val);
}

CAMLprim value deliver_ttm_windowfrompoint(value handler, value wnd, value msg)
{
    CAMLparam3(handler, wnd, msg);
    CAMLlocal2(pt, result);

    pt = alloc_point(*(LPPOINT)Lparam(msg));
    result = callback3(handler, wnd, msg, pt);
    CAMLreturn(result);
}

CAMLprim value return_ttm_windowfrompoint(value result)
{
    return copy_int32((LRESULT)Handle_val(result));
}

/* Notifications */

CAMLprim value on_ttn_getdispinfo(value handler, value wnd, value msg)
{
    CAMLparam3(handler, wnd, msg);
    NMHDR* nmh;
    NMTTDISPINFO* nmttdi;
    CAMLlocalN(args, 6);
    CAMLlocal5(result, pair, ttdi, val, ret);

    ret = Val_none;
    if (Int_val(Field(msg, 0)) == WM_NOTIFY) {
        nmh = (NMHDR*)Lparam(msg);
        if (nmh->code == TTN_GETDISPINFO) {
            nmttdi = (NMTTDISPINFO*)nmh;
            args[0] = wnd;
            args[1] = msg;
            args[2] = Val_int(Wparam(msg));
            args[3] = alloc_nmhdr(nmh);
            if (nmttdi->uFlags & TTF_IDISHWND) {
                args[4] = alloc(1, 1);
                val = alloc_handle((HWND)nmh->idFrom);
                Store_field(args[4], 0, val);
            } else {
                args[4] = alloc(1, 0);
                Store_field(args[4], 0, Val_int(nmh->idFrom));
            }
            args[5] = copy_int32(nmttdi->lParam);
            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);
                    ttdi = Field(pair, 0);
                    val = Field(ttdi, 0);
                    if (Is_block(val)) {
                        if (Tag_val(val) == 0) {
                            nmttdi->lpszText = nmttdi->szText;
                            strncpy(nmttdi->szText, String_val(Field(val, 0)),
                                sizeof(nmttdi->szText) - 1);
                            nmttdi->szText[sizeof(nmttdi->szText)-1] = '\0';
                            nmttdi->hinst = NULL;
                        } else {
                            nmttdi->lpszText = (LPTSTR)Int_val(Field(val, 1));
                            nmttdi->hinst = Handle_val(Field(val, 0));
                        }
                    } else {
                        nmttdi->lpszText = NULL;
                        nmttdi->hinst = NULL;
                    }
                    if (Bool_val(Field(ttdi, 1)))
                        nmttdi->uFlags |= TTF_RTLREADING;
                    if (Bool_val(Field(pair, 1)))
                        nmttdi->uFlags |= TTF_DI_SETITEM;
                    ret = alloc_some(copy_int32(0));
                }
            }
        }
    }
    CAMLreturn(ret);
}
