/* OCaml-Win32
 * win32_monthcalendar.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: Month Calendar Controls */

/* Styles */

static DWORD monthcal_style_options[] = {
    MCS_DAYSTATE,
    MCS_MULTISELECT,
    MCS_NOTODAY,
    MCS_NOTODAYCIRCLE,
    MCS_WEEKNUMBERS
};
Define_static_constants(monthcal_style_options);

CAMLprim value monthcal_style_of_option(value option)
{
    return copy_int64((int64)Constant_val(option, monthcal_style_options));
}

/* Messages */

CAMLprim value pack_mcm_getcolor(value which)
{
    return pack_message(MCM_GETCOLOR, Constant_val(which, calendar_colors), 0);
}

CAMLprim value unpack_mcm_getcolor(value result)
{
    if (Int32_val(result) == -1)
        raise_win32_error(0);
    return alloc_color((COLORREF)Int32_val(result));
}

CAMLprim value issue_mcm_getcursel(value sender)
{
    CAMLparam1(sender);
    SYSTEMTIME st;
    CAMLlocal3(contents, result, val);

    contents = pack_message(MCM_GETCURSEL, 0, (LPARAM)&st);
    result = callback(sender, contents);
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    val = alloc_systemtime(&st);
    CAMLreturn(val);
}

CAMLprim value unpack_mcm_getfirstdayofweek(value result)
{
    CAMLparam1(result);
    CAMLlocal1(val);

    val = alloc_tuple(2);
    Store_field(val, 0, Val_int(LOWORD(Int32_val(result))));
    Store_field(val, 1, Val_bool(HIWORD(Int32_val(result)) != 0));
    CAMLreturn(val);
}

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

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

CAMLprim value issue_mcm_getminreqrect(value sender)
{
    CAMLparam1(sender);
    RECT r;
    CAMLlocal3(contents, result, val);

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

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

static DWORD monthcal_range_types[] = {
    GMR_DAYSTATE,
    GMR_VISIBLE
};
Define_static_constants(monthcal_range_types);

CAMLprim value issue_mcm_getmonthrange(value sender, value type)
{
    CAMLparam2(sender, type);
    SYSTEMTIME st[2];
    CAMLlocal4(contents, result, val, v);

    contents = pack_message(MCM_GETMONTHRANGE, Constant_val(type, monthcal_range_types), (LPARAM)st);
    result = callback(sender, contents);
    val = alloc_tuple(3);
    v = alloc_systemtime(&st[0]);
    Store_field(val, 0, v);
    v = alloc_systemtime(&st[1]);
    Store_field(val, 1, v);
    Store_field(val, 2, Val_int(Int32_val(result)));
    CAMLreturn(val);
}

CAMLprim value issue_mcm_getrange(value sender)
{
    CAMLparam1(sender);
    SYSTEMTIME st[2];
    CAMLlocal4(contents, result, val, v);

    contents = pack_message(MCM_GETRANGE, 0, (LPARAM)st);
    result = callback(sender, contents);
    val = alloc_tuple(2);
    if (Int32_val(result) & GDTR_MIN)
        v = alloc_some(alloc_systemtime(&st[0]));
    else
        v = Val_none;
    Store_field(val, 0, v);
    if (Int32_val(result) & GDTR_MAX)
        v = alloc_some(alloc_systemtime(&st[1]));
    else
        v = Val_none;
    Store_field(val, 1, v);
    CAMLreturn(val);
}

CAMLprim value issue_mcm_getselrange(value sender)
{
    CAMLparam1(sender);
    SYSTEMTIME st[2];
    CAMLlocal4(contents, result, val, v);

    contents = pack_message(MCM_GETSELRANGE, 0, (LPARAM)st);
    result = callback(sender, contents);
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    val = alloc_tuple(2);
    v = alloc_systemtime(&st[0]);
    Store_field(val, 0, v);
    v = alloc_systemtime(&st[1]);
    Store_field(val, 1, v);
    CAMLreturn(val);
}

CAMLprim value issue_mcm_gettoday(value sender)
{
    CAMLparam1(sender);
    SYSTEMTIME st;
    CAMLlocal3(contents, result, val);

    contents = pack_message(MCM_GETTODAY, 0, (LPARAM)&st);
    result = callback(sender, contents);
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    val = alloc_systemtime(&st);
    CAMLreturn(val);
}

static DWORD monthcal_hit_test_titles[] = {
    MCHT_TITLEBK,
    MCHT_TITLEBTNNEXT,
    MCHT_TITLEBTNPREV,
    MCHT_TITLEMONTH,
    MCHT_TITLEYEAR
};
Define_static_constants(monthcal_hit_test_titles);

static DWORD monthcal_hit_test_calendars[] = {
    MCHT_CALENDARBK,
    MCHT_CALENDARDATE,
    MCHT_CALENDARDATENEXT,
    MCHT_CALENDARDATEPREV,
    MCHT_CALENDARDAY,
    MCHT_CALENDARWEEKNUM
};
Define_static_constants(monthcal_hit_test_calendars);

CAMLprim value issue_mcm_hittest(value sender, value pt)
{
    CAMLparam2(sender, pt);
    MCHITTESTINFO mchti;
    CAMLlocal3(contents, val, v);

    memset(&mchti, 0, sizeof(MCHITTESTINFO));
    mchti.cbSize = sizeof(MCHITTESTINFO);
    mchti.pt = point_val(pt);
    sender = pack_message(MCM_HITTEST, 0, (LPARAM)&mchti);
    callback(sender, contents);
    val = alloc_tuple(2);
    if (mchti.uHit == MCHT_NOWHERE)
        v = Val_int(0);
    else if (mchti.uHit == MCHT_TODAYLINK)
        v = Val_int(1);
    else if (mchti.uHit & MCHT_TITLE) {
        v = alloc(1, 0);
        Store_field(v, 0, Val_constant(mchti.uHit, monthcal_hit_test_titles));
    } else {
        v = alloc(1, 1);
        Store_field(v, 0, Val_constant(mchti.uHit, monthcal_hit_test_calendars));
    }
    Store_field(val, 0, v);
    v = alloc_systemtime(&mchti.st);
    Store_field(val, 1, v);
    CAMLreturn(val);
}

CAMLprim value pack_mcm_setcolor(value which, value color)
{
    return pack_message(MCM_SETCOLOR, Constant_val(which, calendar_colors), (LPARAM)color_val(color));
}

CAMLprim value unpack_mcm_setcolor(value result)
{
    if (Int32_val(result) == -1)
        raise_win32_error(0);
    return alloc_color((COLORREF)Int32_val(result));
}

CAMLprim value issue_mcm_setcursel(value sender, value time)
{
    CAMLparam2(sender, time);
    SYSTEMTIME st;
    CAMLlocal2(contents, result);

    systemtime_val(time, &st);
    contents = pack_message(MCM_SETCURSEL, 0, (LPARAM)&st);
    result = callback(sender, contents);
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    CAMLreturn(Val_unit);
}

CAMLprim value issue_mcm_setdaystate(value sender, value states)
{
    CAMLparam2(sender, states);
    int cnt, i;
    MONTHDAYSTATE* p;
    CAMLlocal2(contents, result);

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

CAMLprim value pack_mcm_setfirstdayofweek(value day)
{
    return pack_message(MCM_SETFIRSTDAYOFWEEK, Int_val(day), 0);
}

CAMLprim value unpack_mcm_setfirstdayofweek(value result)
{
    CAMLparam1(result);
    CAMLlocal1(val);

    val = alloc_tuple(2);
    Store_field(val, 0, Val_int(LOWORD(Int32_val(result))));
    Store_field(val, 1, Val_bool(HIWORD(Int32_val(result)) != 0));
    CAMLreturn(val);
}

CAMLprim value pack_mcm_setmaxselcount(value count)
{
    return pack_message(MCM_SETMAXSELCOUNT, Int_val(count), 0);
}

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

CAMLprim value pack_mcm_setmonthdelta(value delta)
{
    return pack_message(MCM_SETMONTHDELTA, Int_val(delta), 0);
}

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

CAMLprim value issue_mcm_setrange(value sender, value min, value max)
{
    CAMLparam3(sender, min, max);
    DWORD flags;
    SYSTEMTIME st[2];
    CAMLlocal2(contents, result);

    flags = 0;
    if (Is_some(min)) {
        flags |= GDTR_MIN;
        systemtime_val(Value_of(min), &st[0]);
    }
    if (Is_some(max)) {
        flags |= GDTR_MAX;
        systemtime_val(Value_of(max), &st[1]);
    }
    contents = pack_message(MCM_SETRANGE, flags, (LPARAM)st);
    result = callback(sender, contents);
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    CAMLreturn(Val_unit);
}

CAMLprim value issue_mcm_setselrange(value sender, value min, value max)
{
    CAMLparam3(sender, min, max);
    SYSTEMTIME st[2];
    CAMLlocal2(contents, result);

    systemtime_val(min, &st[0]);
    systemtime_val(max, &st[1]);
    contents = pack_message(MCM_SETSELRANGE, 0, (LPARAM)st);
    result = callback(sender, contents);
    if (Int32_val(result) == 0)
        raise_win32_error(0);
    CAMLreturn(Val_unit);
}

CAMLprim value issue_mcm_settoday(value sender, value time)
{
    CAMLparam2(sender, time);
    SYSTEMTIME st;
    SYSTEMTIME* stp;
    CAMLlocal1(contents);

    if (Is_some(time)) {
        systemtime_val(time, &st);
        stp = &st;
    } else
        stp = NULL;
    contents = pack_message(MCM_SETTODAY, 0, (LPARAM)stp);
    callback(sender, contents);
    CAMLreturn(Val_unit);
}

/* Notifications */

CAMLprim value unpack_nmselchange(value msg)
{
    CAMLparam1(msg);
    NMSELCHANGE* nmsc;
    CAMLlocal2(val, v);

    nmsc = (NMSELCHANGE*)Lparam(msg);
    val = alloc_tuple(2);
    v = alloc_systemtime(&nmsc->stSelStart);
    Store_field(val, 0, v);
    v = alloc_systemtime(&nmsc->stSelEnd);
    Store_field(val, 1, v);
    CAMLreturn(val);
}

CAMLprim value on_mcn_getdaystate(value handler, value wnd, value msg)
{
    CAMLparam3(handler, wnd, msg);
    NMHDR* nmh;
    NMDAYSTATE* nmds;
    CAMLlocalN(args, 6);
    CAMLlocal3(result, array, ret);
    int cnt, i;

    ret = Val_none;
    if (Int_val(Field(msg, 0)) == WM_NOTIFY) {
        nmh = (NMHDR*)Lparam(msg);
        if (nmh->code == MCN_GETDAYSTATE) {
            nmds = (NMDAYSTATE*)nmh;
            args[0] = wnd;
            args[1] = msg;
            args[2] = Val_int(Wparam(msg));
            args[3] = alloc_nmhdr(nmh);
            args[4] = alloc_systemtime(&nmds->stStart);
            args[5] = Val_int(nmds->cDayState);
            result = callbackN(handler, 6, args);
            if (Is_block(result)) {
                if (Tag_val(result) == 0)
                    ret = alloc_some(Field(result, 0));
                else {
                    array = Field(result, 0);
                    cnt = Wosize_val(array);
                    if (cnt > nmds->cDayState)
                        cnt = nmds->cDayState;
                    for (i = 0; i < cnt; ++i)
                        nmds->prgDayState[i] = Int32_val(Field(array, i));
                    ret = alloc_some(copy_int32(0));
                }
            }
        }
    }
    CAMLreturn(ret);
}
