#include "private.h"
#include "schedui.h"
#include "offl_cpp.h"
#include "dialmon.h"    // For WAITCURSOR
#include "shellids.h"   // For Help IDs

//xnotfmgr - can probably nuke most of this file

#define TF_DLGCTL                   TF_ALWAYS
#define TF_DUMPTRIGGER              0

#define MAX_GROUPNAME_LEN           40
#define MAX_LOADSTRING_LEN          64
#define MAX_SHORTTIMEFORMAT_LEN     80
#define MAX_TIMESEP_LEN             8

/////////////////////////////////////////////////////////////////////////////
// Design constants
/////////////////////////////////////////////////////////////////////////////
#define TASK_ALLDAYS                    (TASK_SUNDAY | TASK_MONDAY | TASK_TUESDAY | TASK_WEDNESDAY | TASK_THURSDAY | TASK_FRIDAY | TASK_SATURDAY)
#define TASK_WEEKDAYS                   (TASK_MONDAY | TASK_TUESDAY | TASK_WEDNESDAY | TASK_THURSDAY | TASK_FRIDAY)
#define TASK_WEEKENDDAYS                (TASK_SUNDAY | TASK_SATURDAY)
#define TASK_ALLMONTHS                  (TASK_JANUARY | TASK_FEBRUARY | TASK_MARCH | TASK_APRIL | TASK_MAY | TASK_JUNE | TASK_JULY | TASK_AUGUST | TASK_SEPTEMBER | TASK_OCTOBER | TASK_NOVEMBER | TASK_DECEMBER)

#define DEFAULT_DAILY_EVERYNDAYS        1
#define DEFAULT_DAILY_EVERYNDAYS_MIN    1
#define DEFAULT_DAILY_EVERYNDAYS_MAX    999
#define DEFAULT_WEEKLY_REPEATWEEKS      1
#define DEFAULT_WEEKLY_REPEATWEEKS_MIN  1
#define DEFAULT_WEEKLY_REPEATWEEKS_MAX  99
#define DEFAULT_MONTHLY_DAY_MIN         1
#define DEFAULT_MONTHLY_DAY_MAX         31
#define DEFAULT_MONTHLY_MONTHS          1
#define DEFAULT_MONTHLY_MONTHS_MIN      1
#define DEFAULT_MONTHLY_MONTHS_MAX      6
#define DEFAULT_UPDATETIME_HRS          0   // 12:00am
#define DEFAULT_UPDATETIME_MINS         0
#define DEFAULT_REPEATUPDATE_HRS        1
#define DEFAULT_REPEATUPDATE_HRS_MIN    1
#define DEFAULT_REPEATUPDATE_HRS_MAX    99
#define DEFAULT_REPEAT_START_HRS        9   // 9:00am
#define DEFAULT_REPEAT_START_MINS       0
#define DEFAULT_REPEAT_END_HRS          17  // 5:00pm
#define DEFAULT_REPEAT_END_MINS         0

#define DEFAULT_RANDOM_MINUTES_INTERVAL 30

// NOTE: These #defines have a dependency on the numeric order of the
// controls in the dialog resource! Make sure they are contiguious
// in RESOURCE.H! [jaym]
#define IDC_TYPE_GROUP_FIRST        IDC_CUSTOM_DAILY
#define IDC_TYPE_GROUP_LAST         IDC_CUSTOM_MONTHLY
    // Group type radio buttons

#define IDC_DAILY_GROUP_FIRST       IDC_CUSTOM_DAILY_EVERYNDAYS
#define IDC_DAILY_GROUP_LAST        IDC_CUSTOM_DAILY_EVERYWEEKDAY
    // Daily group type radio buttons

#define IDC_MONTHLY_GROUP_FIRST     IDC_CUSTOM_MONTHLY_DAYOFMONTH
#define IDC_MONTHLY_GROUP_LAST      IDC_CUSTOM_MONTHLY_PERIODIC
    // Monthly group type radio buttons

#define IDC_TIME_GROUP_FIRST        IDC_CUSTOM_TIME_UPDATEAT
#define IDC_TIME_GROUP_LAST         IDC_CUSTOM_TIME_REPEATEVERY
    // Time radio buttons

#define IDC_CUSTOM_DAILY_FIRST      IDC_CUSTOM_DAILY_EVERYNDAYS
#define IDC_CUSTOM_DAILY_LAST       IDC_CUSTOM_DAILY_STATIC1
#define IDC_CUSTOM_WEEKLY_FIRST     IDC_CUSTOM_WEEKLY_STATIC1
#define IDC_CUSTOM_WEEKLY_LAST      IDC_CUSTOM_WEEKLY_DAY7
#define IDC_CUSTOM_MONTHLY_FIRST    IDC_CUSTOM_MONTHLY_DAYOFMONTH
#define IDC_CUSTOM_MONTHLY_LAST     IDC_CUSTOM_MONTHLY_STATIC4
    // Group type controls

#define IDS_MONTHLY_LIST1_FIRST     IDS_WEEK1
#define IDS_MONTHLY_LIST1_LAST      IDS_WEEK5
    // Combo box items.

#define CX_DIALOG_GUTTER            10
#define CY_DIALOG_GUTTER            10

/////////////////////////////////////////////////////////////////////////////
// Module variables
/////////////////////////////////////////////////////////////////////////////
static const TCHAR s_szRepeatHrsAreMins[] = TEXT("RepeatHrsAreMins");

static const CTLGRPITEM c_rgnCtlGroups[] = 
{
    // 'Container' control                  First item in control       Last item in control
    //-------------------------             --------------------------- ----------------------
    { IDC_CUSTOM_GROUP_DAILY,               IDC_CUSTOM_DAILY_FIRST,     IDC_CUSTOM_DAILY_LAST },
    { IDC_CUSTOM_GROUP_WEEKLY,              IDC_CUSTOM_WEEKLY_FIRST,    IDC_CUSTOM_WEEKLY_LAST },
    { IDC_CUSTOM_GROUP_MONTHLY,             IDC_CUSTOM_MONTHLY_FIRST,   IDC_CUSTOM_MONTHLY_LAST },
};

static const CTLGRPITEM c_rgnCtlItems[] = 
{
    // 'Container' control                  First item in control       Last item in control
    //-------------------------             --------------------------- ----------------------
    { IDC_CUSTOM_MONTHLY_PERIODIC_LIST1,    IDS_MONTHLY_LIST1_FIRST,    IDS_MONTHLY_LIST1_LAST },
};

static const WORD c_rgwMonthMaps[] =
{
    0x0FFF,         // every month       111111111111b
    0x0AAA,         // every other month 101010101010b
    0x0924,         // every 3 months    100100100100b
    0x0888,         // every 4 months    100010001000b
    0x0842,         // every 5 months    100001000010b
    0x0820          // every 6 months    100000100000b
};

#define NUM_CUSTOM_DAYSOFTHEWEEK    3
static WORD s_rgwDaysOfTheWeek[NUM_CUSTOM_DAYSOFTHEWEEK+7] = 
{
    TASK_ALLDAYS,
    TASK_WEEKDAYS,
    TASK_WEEKENDDAYS,

    // ...
};
    // NOTE: These should be kept in line with IDS_DAY1 to IDS_DAY3

extern TCHAR c_szHelpFile[];
DWORD aCustomDlgHelpIDs[] =
{
    IDD_CUSTOM_SCHEDULE,                        IDH_CUSTOM_SCHEDULE,

    IDC_CUSTOM_NEWGROUP,                        IDH_SCHEDULE_NEW,
    IDC_CUSTOM_REMOVEGROUP,                     IDH_SCHEDULE_REMOVE,

    IDC_CUSTOM_GROUP_LIST,                      IDH_NEW_NAME,
    IDC_CUSTOM_GROUP_EDIT,                      IDH_NEW_NAME,

    IDC_CUSTOM_DAILY,                           IDH_SCHED_DAYS,
    IDC_CUSTOM_WEEKLY,                          IDH_SCHED_DAYS,
    IDC_CUSTOM_MONTHLY,                         IDH_SCHED_DAYS,

    IDC_CUSTOM_DAILY_EVERYNDAYS,                IDH_SCHED_FREQUENCY,
    IDC_CUSTOM_DAILY_EVERYNDAYS_EDIT,           IDH_SCHED_FREQUENCY,
    IDC_CUSTOM_DAILY_STATIC1,                   IDH_SCHED_FREQUENCY,
    IDC_CUSTOM_DAILY_EVERYWEEKDAY,              IDH_SCHED_FREQUENCY,
    IDC_CUSTOM_WEEKLY_STATIC1,                  IDH_SCHED_FREQUENCY,
    IDC_CUSTOM_WEEKLY_REPEATWEEKS_EDIT,         IDH_SCHED_FREQUENCY,
    IDC_CUSTOM_WEEKLY_STATIC2,                  IDH_SCHED_FREQUENCY,
    IDC_CUSTOM_WEEKLY_DAY1,                     IDH_SCHED_FREQUENCY,
    IDC_CUSTOM_WEEKLY_DAY2,                     IDH_SCHED_FREQUENCY,
    IDC_CUSTOM_WEEKLY_DAY3,                     IDH_SCHED_FREQUENCY,
    IDC_CUSTOM_WEEKLY_DAY4,                     IDH_SCHED_FREQUENCY,
    IDC_CUSTOM_WEEKLY_DAY5,                     IDH_SCHED_FREQUENCY,
    IDC_CUSTOM_WEEKLY_DAY6,                     IDH_SCHED_FREQUENCY,
    IDC_CUSTOM_WEEKLY_DAY7,                     IDH_SCHED_FREQUENCY,
    IDC_CUSTOM_MONTHLY_DAYOFMONTH,              IDH_SCHED_FREQUENCY,
    IDC_CUSTOM_MONTHLY_STATIC3,                 IDH_SCHED_FREQUENCY,
    IDC_CUSTOM_MONTHLY_DAYOFMONTH_MONTH_EDIT,   IDH_SCHED_FREQUENCY,
    IDC_CUSTOM_MONTHLY_STATIC4,                 IDH_SCHED_FREQUENCY,
    IDC_CUSTOM_MONTHLY_PERIODIC,                IDH_SCHED_FREQUENCY,
    IDC_CUSTOM_MONTHLY_PERIODIC_LIST1,          IDH_SCHED_FREQUENCY,
    IDC_CUSTOM_MONTHLY_PERIODIC_LIST2,          IDH_SCHED_FREQUENCY,
    IDC_CUSTOM_MONTHLY_STATIC1,                 IDH_SCHED_FREQUENCY,
    IDC_CUSTOM_MONTHLY_PERIODIC_EDIT,           IDH_SCHED_FREQUENCY,
    IDC_CUSTOM_MONTHLY_STATIC2,                 IDH_SCHED_FREQUENCY,

    IDC_CUSTOM_TIME_UPDATEAT,                   IDH_SCHED_TIME,
    IDC_CUSTOM_TIME_UPDATEAT_TIME,              IDH_SCHED_TIME,

    IDC_CUSTOM_TIME_REPEATEVERY,                IDH_SCHED_REPEAT,
    IDC_CUSTOM_TIME_REPEATEVERY_EDIT,           IDH_SCHED_REPEAT,
    IDC_CUSTOM_TIME_REPEATBETWEEN,              IDH_SCHED_REPEAT,
    IDC_CUSTOM_TIME_REPEATBETWEEN_START_TIME,   IDH_SCHED_REPEAT,
    IDC_CUSTOM_TIME_REPEATBETWEEN_END_TIME,     IDH_SCHED_REPEAT,

    IDC_CUSTOM_MINIMIZENETUSE,                  IDH_VARY_START
};
    // Help IDs

/////////////////////////////////////////////////////////////////////////////
// Local functions
/////////////////////////////////////////////////////////////////////////////
void InitDlgCtls(HWND hwndDlg, SSUIDLGINFO * pDlgInfo);
void PositionDlgCtls(HWND hwndDlg);
void InitDlgDefaults(HWND hwndDlg, SSUIDLGINFO * pDlgInfo);
void InitDlgInfo(HWND hwndDlg, SSUIDLGINFO * pDlgInfo);

BOOL HandleDlgButtonClick(HWND hwndDlg, WPARAM wParam, SSUIDLGINFO * pDlgInfo);
BOOL HandleButtonClick(HWND hwndDlg, WPARAM wParam);
BOOL HandleGroupChange(HWND hwndDlg, SSUIDLGINFO * pDlgInfo);

void SetDlgCustomType(HWND hwndDlg, int iType);
void SetDlgWeekDays(HWND hwndDlg, WORD rgfDaysOfTheWeek);
WORD GetDlgWeekDays(HWND hwndDlg);
void SetTaskTriggerToDefaults(TASK_TRIGGER * pTaskTrigger);
void SetTaskTriggerFromDlg(HWND hwndDlg, SSUIDLGINFO * pDlgInfo);

HRESULT CreateScheduleGroupFromDlg(HWND hwndDlg, SSUIDLGINFO * pDlgInfo);

void    OnDataChanged(HWND hwndDlg, SSUIDLGINFO * pDlgInfo);
HRESULT ApplyDlgChanges(HWND hwndDlg, SSUIDLGINFO * pDlgInfo);
BOOL    ValidateDlgFields(HWND hwndDlg, SSUIDLGINFO * pDlgInfo);

UINT    GetDlgGroupName(HWND hwndDlg, LPTSTR pszGroupName, int cchGroupNameLen);
int     GetDlgGroupNameLength(HWND hwndDlg);
BOOL    SetDlgItemMonth(HWND hwndDlg, int idCtl, WORD rgfMonths);
WORD    GetDlgItemMonth(HWND hwndDlg, int idCtl);
void    SetDlgItemNextUpdate(HWND hwndDlg, int idCtl, SSUIDLGINFO * pDlgInfo);

HRESULT GetSchedGroupTaskTrigger(PNOTIFICATIONCOOKIE pGroupCookie,TASK_TRIGGER * pTaskTrigger);
BOOL CALLBACK ShowScheduleUIDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);

DWORD   IncTime(DWORD dwTime, int iIncHours, int iIncMinutes);
int     DayFromDaysFlags(DWORD rgfDays);
int     MonthCountFromMonthsFlags(WORD rgfMonths);
int     DayIndexFromDaysOfTheWeekFlags(WORD rgfDaysOfTheWeek);
HRESULT GetDaysOfWeekString(WORD rgfDaysOfTheWeek, LPTSTR ptszBuf, UINT cchBuf);
HRESULT GetMonthsString(WORD rgfMonths, LPTSTR ptszBuf, UINT cchBuf);
void    RestrictDlgItemRange(HWND hwndDlg, int idCtl, SSUIDLGINFO * pDlgInfo);
void    RestrictTimeRepeatEvery(HWND hwndDlg);

int     SGMessageBox(HWND hwndParent, UINT idStringRes, UINT uType);

#ifdef DEBUG
void    DumpTaskTrigger(TASK_TRIGGER * pTaskTrigger);
#endif  // DEBUG

/////////////////////////////////////////////////////////////////////////////
// ScheduleSummaryFromGroup
/////////////////////////////////////////////////////////////////////////////
HRESULT ScheduleSummaryFromGroup
(
    /* [in] */      PNOTIFICATIONCOOKIE pGroupCookie,
    /* [in][out] */ LPTSTR              pszSummary,
    /* [in] */      UINT                cchSummary
)
{
//xnotfmgr

    HRESULT hrResult;

    ASSERT((pszSummary != NULL) && (cchSummary > 0));

    for (;;)
    {
         *pszSummary = TEXT('\0');
 
        TASK_TRIGGER    tt;
        if (FAILED(hrResult = GetSchedGroupTaskTrigger(pGroupCookie, &tt)))
            break;

        if (FAILED(hrResult = ScheduleSummaryFromTaskTrigger(&tt, pszSummary, cchSummary)))
            break;

        hrResult = S_OK;
        break;
    }

    return hrResult;
}

/////////////////////////////////////////////////////////////////////////////
// ScheduleSummaryFromTaskTrigger
/////////////////////////////////////////////////////////////////////////////
HRESULT ScheduleSummaryFromTaskTrigger
(
    TASK_TRIGGER *  pTT,
    LPTSTR          pszSummary,
    UINT            cchSummary
)
{
    TCHAR   szLineOut[128];
    TCHAR   szFormat[128];
    HRESULT hrResult;

    if (!MLLoadString(IDS_SUMMARY_UPDATE, szLineOut, ARRAYSIZE(szLineOut)))
    {
        hrResult = HRESULT_FROM_WIN32(GetLastError());
        return hrResult;
    }

    StrNCpy(pszSummary, szLineOut, cchSummary);

    switch (pTT->TriggerType)
    {
        case TASK_TIME_TRIGGER_DAILY:
        {
            if (pTT->Type.Daily.DaysInterval == 1)
            {
                // Daily -- every day
                if (!MLLoadString(IDS_SUMMARY_EVERY_DAY, szLineOut, ARRAYSIZE(szLineOut)))
                {
                    hrResult = HRESULT_FROM_WIN32(GetLastError());
                    return hrResult;
                }
            }
            else
            {
                // Daily -- every %d days
                if (!MLLoadString(IDS_SUMMARY_DAILY_FORMAT, szFormat, ARRAYSIZE(szFormat)))
                {
                    hrResult = HRESULT_FROM_WIN32(GetLastError());
                    return hrResult;
                }

                wnsprintf(szLineOut, ARRAYSIZE(szLineOut), szFormat, 
                          pTT->Type.Daily.DaysInterval);
            }

            lstrcatn(pszSummary, szLineOut, cchSummary);
            break;
        }

        case TASK_TIME_TRIGGER_WEEKLY:
        {
            TCHAR szDOW[128];

            if (FAILED(hrResult = GetDaysOfWeekString(  pTT->Type.Weekly.rgfDaysOfTheWeek,
                                                        szDOW,
                                                        ARRAYSIZE(szDOW))))
            {
                return hrResult;
            }

            if (pTT->Type.Weekly.WeeksInterval == 1)
            {
                // Daily -- every weekday
                if (pTT->Type.Weekly.rgfDaysOfTheWeek == TASK_WEEKDAYS)
                {
                    if (!MLLoadString(IDS_SUMMARY_EVERY_WEEKDAY, szLineOut, ARRAYSIZE(szLineOut)))
                    {
                        hrResult = HRESULT_FROM_WIN32(GetLastError());
                        return hrResult;
                    }
                }
                else
                {
                    // Weekly -- every %s of every week
                    if (!MLLoadString(IDS_SUMMARY_EVERY_WEEK_FORMAT, szFormat, ARRAYSIZE(szFormat)))
                    {
                        hrResult = HRESULT_FROM_WIN32(GetLastError());
                        return hrResult;
                    }

                    wnsprintf(szLineOut, ARRAYSIZE(szLineOut), szFormat, szDOW);
                }
            }
            else
            {
                TCHAR szWeeks[16];

                // Weekly -- every %s of every %s weeks
                if (!MLLoadString(IDS_SUMMARY_WEEKLY_FORMAT, szFormat, ARRAYSIZE(szFormat)))
                {
                    hrResult = HRESULT_FROM_WIN32(GetLastError());
                    return hrResult;
                }

                wnsprintf(szWeeks, ARRAYSIZE(szWeeks), "%d", pTT->Type.Weekly.WeeksInterval);

                TCHAR * rgpsz[2] = { szDOW, szWeeks };
                if (!FormatMessage( FORMAT_MESSAGE_FROM_STRING
                                        | FORMAT_MESSAGE_ARGUMENT_ARRAY,
                                    szFormat,
                                    0,
                                    0,
                                    szLineOut, 
                                    ARRAYSIZE(szLineOut),
                                    (va_list*)rgpsz))
                {
                    hrResult = HRESULT_FROM_WIN32(GetLastError());
                    return hrResult;
                }
            }

            lstrcatn(pszSummary, szLineOut, cchSummary);
            break;
        }

        case TASK_TIME_TRIGGER_MONTHLYDATE:
        {
            if (pTT->Type.MonthlyDate.rgfMonths == TASK_ALLMONTHS)
            {
                // Monthly -- on day %d of every month
                if (!MLLoadString(
                                IDS_SUMMARY_EVERY_MONTHLYDATE_FORMAT,
                                szFormat,
                                ARRAYSIZE(szFormat)))
                {
                    hrResult = HRESULT_FROM_WIN32(GetLastError());
                    return hrResult;
                }

                wnsprintf(szLineOut, ARRAYSIZE(szLineOut), szFormat, 
                          DayFromDaysFlags(pTT->Type.MonthlyDate.rgfDays));
            }
            else
            {
                // Monthly -- on day %d of %s
                if (!MLLoadString(
                                IDS_SUMMARY_MONTHLYDATE_FORMAT,
                                szFormat,
                                ARRAYSIZE(szFormat)))
                {
                    hrResult = HRESULT_FROM_WIN32(GetLastError());
                    return hrResult;
                }

                TCHAR szMonths[128];
                if (FAILED(hrResult = GetMonthsString(  pTT->Type.MonthlyDate.rgfMonths,
                                                        szMonths,
                                                        ARRAYSIZE(szMonths))))
                {
                    return hrResult;
                }

                TCHAR szDay[16];
                wnsprintf(szDay, ARRAYSIZE(szDay), "%d", 
                          DayFromDaysFlags(pTT->Type.MonthlyDate.rgfDays));

                TCHAR * rgpsz[2] = { szDay, szMonths };
                if (!FormatMessage( FORMAT_MESSAGE_FROM_STRING
                                        | FORMAT_MESSAGE_ARGUMENT_ARRAY,
                                    szFormat,
                                    0,
                                    0,
                                    szLineOut, 
                                    ARRAYSIZE(szLineOut),
                                    (va_list*)rgpsz))
                {
                    hrResult = HRESULT_FROM_WIN32(GetLastError());
                    return hrResult;
                }
            }

            lstrcatn(pszSummary, szLineOut, cchSummary);
            break;
        }

        case TASK_TIME_TRIGGER_MONTHLYDOW:
        {
            ASSERT((pTT->Type.MonthlyDOW.wWhichWeek >= TASK_FIRST_WEEK));
            ASSERT((pTT->Type.MonthlyDOW.wWhichWeek <= TASK_LAST_WEEK));

            TCHAR szWeek[32];
            if (!MLLoadString(
                            IDS_MONTHLY_LIST1_FIRST + pTT->Type.MonthlyDOW.wWhichWeek - 1,
                            szWeek,
                            ARRAYSIZE(szWeek)))
            {
                hrResult = HRESULT_FROM_WIN32(GetLastError());
                return hrResult;
            }

            TCHAR szDOW[128];
            if (FAILED(hrResult = GetDaysOfWeekString(  pTT->Type.MonthlyDOW.rgfDaysOfTheWeek,
                                                        szDOW,
                                                        ARRAYSIZE(szDOW))))
            {
                return hrResult;
            }

            if (pTT->Type.MonthlyDate.rgfMonths == TASK_ALLMONTHS)
            {
                // Monthly -- on the %s %s of every month
                if (!MLLoadString(
                                IDS_SUMMARY_EVERY_MONTHLYDOW_FORMAT,
                                szFormat,
                                ARRAYSIZE(szFormat)))
                {
                    hrResult = HRESULT_FROM_WIN32(GetLastError());
                    return hrResult;
                }

                TCHAR * rgpsz[2] = { szWeek, szDOW };
                if (!FormatMessage( FORMAT_MESSAGE_FROM_STRING
                                        | FORMAT_MESSAGE_ARGUMENT_ARRAY,
                                    szFormat,
                                    0,
                                    0,
                                    szLineOut, 
                                    ARRAYSIZE(szLineOut),
                                    (va_list*)rgpsz))
                {
                    hrResult = HRESULT_FROM_WIN32(GetLastError());
                    return hrResult;
                }
            }
            else
            {
                TCHAR szMonths[128];
                if (FAILED(hrResult = GetMonthsString(  pTT->Type.MonthlyDOW.rgfMonths,
                                                        szMonths,
                                                        ARRAYSIZE(szMonths))))
                {
                    return hrResult;
                }

                // Monthly -- on the %s %s of %s
                if (!MLLoadString(
                                IDS_SUMMARY_MONTHLYDOW_FORMAT,
                                szFormat,
                                ARRAYSIZE(szFormat)))
                {
                    hrResult = HRESULT_FROM_WIN32(GetLastError());
                    return hrResult;
                }

                TCHAR * rgpsz[3] = { szWeek, szDOW, szMonths };
                if (!FormatMessage( FORMAT_MESSAGE_FROM_STRING
                                        | FORMAT_MESSAGE_ARGUMENT_ARRAY,
                                    szFormat,
                                    0,
                                    0,
                                    szLineOut, 
                                    ARRAYSIZE(szLineOut),
                                    (va_list*)rgpsz))
                {
                    hrResult = HRESULT_FROM_WIN32(GetLastError());
                    return hrResult;
                }
            }

            lstrcatn(pszSummary, szLineOut, cchSummary);
            break;
        }

        default:
        {
            ASSERT(FALSE);
            return E_FAIL;
        }
    }

    TCHAR       szStartTime[MAX_SHORTTIMEFORMAT_LEN];
    SYSTEMTIME  st = { 0 };
    st.wHour    = pTT->wStartHour;
    st.wMinute  = pTT->wStartMinute;

    EVAL(GetTimeFormat( LOCALE_USER_DEFAULT,
                        TIME_NOSECONDS,
                        &st,
                        NULL,
                        szStartTime,
                        ARRAYSIZE(szStartTime)) > 0);

    // Handle the 'Time' settings.
    if (pTT->MinutesInterval == 0)
    {
        // at %s
        if (!MLLoadString(
                        IDS_SUMMARY_ONCE_DAY_FORMAT,
                        szFormat,
                        ARRAYSIZE(szFormat)))
        {
            hrResult = HRESULT_FROM_WIN32(GetLastError());
            return hrResult;
        }

        wnsprintf(szLineOut, ARRAYSIZE(szLineOut), szFormat, szStartTime);
        lstrcatn(pszSummary, szLineOut, cchSummary);
    }
    else
    {
        if ((pTT->MinutesInterval % 60) == 0)   //repeating on the hour
        {
            if ((pTT->MinutesInterval / 60) == 1)
            {
                // every hour
                if (!MLLoadString(
                                IDS_SUMMARY_REPEAT_EVERY_HOUR,
                                szLineOut,
                                ARRAYSIZE(szLineOut)))
                {
                    hrResult = HRESULT_FROM_WIN32(GetLastError());
                    return hrResult;
                }
            }
            else
            {
                // every %d hours
                if (!MLLoadString(
                                IDS_SUMMARY_REPEAT_HOURLY_FORMAT,
                                szFormat,
                                ARRAYSIZE(szFormat)))
                {
                    hrResult = HRESULT_FROM_WIN32(GetLastError());
                    return hrResult;
                }

                wnsprintf(szLineOut, ARRAYSIZE(szLineOut), szFormat, 
                         (pTT->MinutesInterval / 60));
            }
        }
        else        //not integral hours, use minutes
        {
            if (pTT->MinutesInterval == 1)
            {
                // every minute
                if (!MLLoadString(
                                IDS_SUMMARY_REPEAT_EVERY_MIN,
                                szLineOut,
                                ARRAYSIZE(szLineOut)))
                {
                    hrResult = HRESULT_FROM_WIN32(GetLastError());
                    return hrResult;
                }
            }
            else
            {
                // every %d minutes
                if (!MLLoadString(
                                IDS_SUMMARY_REPEAT_MINUTELY_FORMAT,
                                szFormat,
                                ARRAYSIZE(szFormat)))
                {
                    hrResult = HRESULT_FROM_WIN32(GetLastError());
                    return hrResult;
                }

                wnsprintf(szLineOut, ARRAYSIZE(szLineOut), szFormat, 
                         pTT->MinutesInterval);
            }
        }

        lstrcatn(pszSummary, szLineOut, cchSummary);

        // between %s and %s
        if (pTT->MinutesDuration != (24 * 60))
        {
            if (!MLLoadString(
                            IDS_SUMMARY_REPEAT_BETWEEN_FORMAT,
                            szFormat,
                            ARRAYSIZE(szFormat)))
            {
                hrResult = HRESULT_FROM_WIN32(GetLastError());
                return hrResult;
            }

            DWORD dwEndTime = IncTime(  MAKELONG(pTT->wStartHour, pTT->wStartMinute),
                                        (pTT->MinutesDuration / 60),
                                        (pTT->MinutesDuration % 60));

            ZeroMemory(&st, SIZEOF(SYSTEMTIME));
            st.wHour    = LOWORD(dwEndTime);
            st.wMinute  = HIWORD(dwEndTime);

            TCHAR szEndTime[MAX_SHORTTIMEFORMAT_LEN];
            EVAL(GetTimeFormat( LOCALE_USER_DEFAULT,
                                TIME_NOSECONDS,
                                &st,
                                NULL,
                                szEndTime,
                                ARRAYSIZE(szEndTime)) > 0);

            TCHAR * rgpsz[2] = { szStartTime, szEndTime };
            if (!FormatMessage( FORMAT_MESSAGE_FROM_STRING
                                    | FORMAT_MESSAGE_ARGUMENT_ARRAY,
                                szFormat,
                                0,
                                0,
                                szLineOut, 
                                ARRAYSIZE(szLineOut),
                                (va_list*)rgpsz))
            {
                hrResult = HRESULT_FROM_WIN32(GetLastError());
                return hrResult;
            }

            lstrcatn(pszSummary, szLineOut, cchSummary);
        }
    }

    return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// GetDaysOfWeekString
/////////////////////////////////////////////////////////////////////////////
HRESULT GetDaysOfWeekString
(
    WORD    rgfDaysOfTheWeek,
    LPTSTR  ptszBuf,
    UINT    cchBuf
)
{
    if (rgfDaysOfTheWeek == 0)
        return E_INVALIDARG;

    int     cch;
    TCHAR   tszSep[8];
    BOOL    fMoreThanOne = FALSE;
    HRESULT hrResult;


    // REVIEW: Could probably make this simpler by creating a table [jaym]
    // TASK_* -> LOCALE_SABBREVDAYNAME?
    // REARCHITECT: Needs a re-write, hard to understand code

    if (!MLLoadString(IDS_SUMMARY_LIST_SEP, tszSep, ARRAYSIZE(tszSep)))
    {
        hrResult = HRESULT_FROM_WIN32(GetLastError());
        return hrResult;
    }

    *ptszBuf = TEXT('\0');

    if (rgfDaysOfTheWeek & TASK_MONDAY)
    {
        if (!GetLocaleInfo( LOCALE_USER_DEFAULT,
                            LOCALE_SABBREVDAYNAME1,
                            ptszBuf,
                            cchBuf))
        {
            hrResult = HRESULT_FROM_WIN32(GetLastError());
            return hrResult;
        }

        fMoreThanOne = TRUE;
    }

    if (rgfDaysOfTheWeek & TASK_TUESDAY)
    {
        if (fMoreThanOne)
            lstrcat(ptszBuf, tszSep);
        
        cch = lstrlen(ptszBuf);
        if (!GetLocaleInfo( LOCALE_USER_DEFAULT,
                            LOCALE_SABBREVDAYNAME2,
                            ptszBuf + cch,
                            cchBuf - cch))
        {
            hrResult = HRESULT_FROM_WIN32(GetLastError());
            return hrResult;
        }

        fMoreThanOne = TRUE;
    }

    if (rgfDaysOfTheWeek & TASK_WEDNESDAY)
    {
        if (fMoreThanOne)
            lstrcat(ptszBuf, tszSep);
        
        cch = lstrlen(ptszBuf);
        if (!GetLocaleInfo( LOCALE_USER_DEFAULT,
                            LOCALE_SABBREVDAYNAME3,
                            ptszBuf + cch,
                            cchBuf - cch))
        {
            hrResult = HRESULT_FROM_WIN32(GetLastError());
            return hrResult;
        }

        fMoreThanOne = TRUE;
    }

    if (rgfDaysOfTheWeek & TASK_THURSDAY)
    {
        if (fMoreThanOne)
            lstrcat(ptszBuf, tszSep);
        
        cch = lstrlen(ptszBuf);
        if (!GetLocaleInfo( LOCALE_USER_DEFAULT,
                            LOCALE_SABBREVDAYNAME4,
                            ptszBuf + cch,
                            cchBuf - cch))
        {
            hrResult = HRESULT_FROM_WIN32(GetLastError());
            return hrResult;
        }
        
        fMoreThanOne = TRUE;
    }

    if (rgfDaysOfTheWeek & TASK_FRIDAY)
    {
        if (fMoreThanOne)
            lstrcat(ptszBuf, tszSep);

        cch = lstrlen(ptszBuf);
        if (!GetLocaleInfo( LOCALE_USER_DEFAULT,
                            LOCALE_SABBREVDAYNAME5,
                            ptszBuf + cch,
                            cchBuf - cch))
        {
            hrResult = HRESULT_FROM_WIN32(GetLastError());
            return hrResult;
        }

        fMoreThanOne = TRUE;
    }

    if (rgfDaysOfTheWeek & TASK_SATURDAY)
    {
        if (fMoreThanOne)
            lstrcat(ptszBuf, tszSep);

        cch = lstrlen(ptszBuf);
        if (!GetLocaleInfo( LOCALE_USER_DEFAULT,
                            LOCALE_SABBREVDAYNAME6,
                            ptszBuf + cch,
                            cchBuf - cch))
        {
            hrResult = HRESULT_FROM_WIN32(GetLastError());
            return hrResult;
        }

        fMoreThanOne = TRUE;
    }

    if (rgfDaysOfTheWeek & TASK_SUNDAY)
    {
        if (fMoreThanOne)
            lstrcat(ptszBuf, tszSep);

        cch = lstrlen(ptszBuf);
        if (!GetLocaleInfo( LOCALE_USER_DEFAULT,
                            LOCALE_SABBREVDAYNAME7,
                            ptszBuf + cch,
                            cchBuf - cch))
        {
            hrResult = HRESULT_FROM_WIN32(GetLastError());
            return hrResult;
        }
    }

    return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// GetMonthsString
/////////////////////////////////////////////////////////////////////////////
HRESULT GetMonthsString
(
    WORD    rgfMonths,
    LPTSTR  ptszBuf,
    UINT    cchBuf
)
{
    if (rgfMonths == 0)
        return E_INVALIDARG;

    int     cch;
    TCHAR   tszSep[8];
    BOOL    fMoreThanOne = FALSE;
    HRESULT hr;

    if (!MLLoadString(IDS_SUMMARY_LIST_SEP, tszSep, ARRAYSIZE(tszSep)))
    {
        hr = HRESULT_FROM_WIN32(GetLastError());
        return hr;
    }

    *ptszBuf = TEXT('\0');

    for (WORD i = 0; i < 12; i++)
    {
        if ((rgfMonths >> i) & 0x1)
        {
            if (fMoreThanOne)
                lstrcat(ptszBuf, tszSep);

            cch = lstrlen(ptszBuf);
            if (!GetLocaleInfo( LOCALE_USER_DEFAULT,
                                LOCALE_SABBREVMONTHNAME1 + i,
                                ptszBuf + cch,
                                cchBuf - cch))
            {
                hr = HRESULT_FROM_WIN32(GetLastError());
                return hr;
            }

            fMoreThanOne = TRUE;
        }
    }

    return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// ShowScheduleUI
/////////////////////////////////////////////////////////////////////////////
HRESULT ShowScheduleUI
(
    /* [in] */      HWND                hwndParent,
    /* [in][out] */ PNOTIFICATIONCOOKIE pGroupCookie,
    /* [in][out] */ DWORD *             pdwFlags
)
{
//xnotfmgr

    SSUIDLGINFO ssuiDlgInfo = { 0 };
    HRESULT     hrResult;

    // Initialize the custom time edit control class.
    INITCOMMONCONTROLSEX icc;
    icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
    icc.dwICC = ICC_DATE_CLASSES;
    EVAL(SUCCEEDED(InitCommonControlsEx(&icc)));

    for (;;)
    {
        // REVIEW: Check for invalid/conflicting dwFlags. [jaym]
        if  (
            (pGroupCookie == NULL)
            ||
            (pdwFlags == NULL)
            )
        {
            return E_INVALIDARG;
        }

        ssuiDlgInfo.dwFlags         = *pdwFlags;
        ssuiDlgInfo.pGroupCookie    = pGroupCookie;

        // Get the GetRunTimes function entry point from URLMON.
        if ((ssuiDlgInfo.hinstURLMON = LoadLibrary(TEXT("URLMON.DLL"))) != NULL)
        {
            EVAL((ssuiDlgInfo.pfnGetRunTimes = (GRTFUNCTION)GetProcAddress( ssuiDlgInfo.hinstURLMON,
                                                                            TEXT("GetRunTimes"))) != NULL);
        }
        else
            TraceMsg(TF_ERROR, "Unable to LoadLibrary(URLMON.DLL)");

        // Setup the TASK_TRIGGER with default or sched. group info.
        if (ssuiDlgInfo.dwFlags & SSUI_CREATENEWSCHEDULE)
        {
            *(ssuiDlgInfo.pGroupCookie) = GUID_NULL;
            SetTaskTriggerToDefaults(&ssuiDlgInfo.ttTaskTrigger);
        }
        else
        {
            ASSERT(ssuiDlgInfo.dwFlags & SSUI_EDITSCHEDULE);

            if (FAILED(hrResult = GetSchedGroupTaskTrigger( ssuiDlgInfo.pGroupCookie,
                                                            &ssuiDlgInfo.ttTaskTrigger)))
            {
                hrResult = E_INVALIDARG;
                break;
            }
        }

        // See if we want to specify schedule hours in minutes.
        ReadRegValue(   HKEY_CURRENT_USER,
                        WEBCHECK_REGKEY,
                        s_szRepeatHrsAreMins,
                        &ssuiDlgInfo.dwRepeatHrsAreMins,
                        SIZEOF(&ssuiDlgInfo.dwRepeatHrsAreMins));

#ifdef DEBUG
        if (ssuiDlgInfo.dwRepeatHrsAreMins)
            TraceMsg(TF_ALWAYS, "!!! WARNING! Treating Repeat Hours as Minutes !!!");
#endif  // DEBUG

        // Show the dialog.
        int iResult;
        if ((iResult = DialogBoxParam(  MLGetHinst(),
                                        MAKEINTRESOURCE(IDD_CUSTOM_SCHEDULE),
                                        hwndParent,
                                        ShowScheduleUIDlgProc,
                                        (LPARAM)&ssuiDlgInfo)) < 0)
        {
            TraceMsg(TF_ERROR, "SSUI: DialogBoxParam failed");

            hrResult = E_FAIL;
            break;
        }

        switch (iResult)
        {
            case IDCANCEL:
            {
                if (!(ssuiDlgInfo.dwFlags & SSUI_EDITSCHEDULE))
                    break;

                // FALL THROUGH!!!
            }

            case IDOK:
            {
                *pdwFlags = ssuiDlgInfo.dwFlags;

                if (ssuiDlgInfo.dwFlags & SSUI_SCHEDULELISTUPDATED)
                {
                    ASSERT(ssuiDlgInfo.pGroupCookie != NULL);
                    ASSERT(*(ssuiDlgInfo.pGroupCookie) != GUID_NULL);

                    pGroupCookie = ssuiDlgInfo.pGroupCookie;
                }

                break;
            }

            default:
            {
                ASSERT(FALSE);
                break;
            }
        }

        hrResult = S_OK;
        break;
    }

    if (ssuiDlgInfo.hinstURLMON != NULL)
        FreeLibrary(ssuiDlgInfo.hinstURLMON);

    return hrResult;
}

/////////////////////////////////////////////////////////////////////////////
// ShowScheduleUIDlgProc
/////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK ShowScheduleUIDlgProc
(
    HWND    hwndDlg,
    UINT    uMsg,
    WPARAM  wParam,
    LPARAM  lParam
)
{
    SSUIDLGINFO * pDlgInfo = (SSUIDLGINFO *)GetWindowLong(hwndDlg, DWL_USER);

    switch (uMsg)
    {
        case WM_INITDIALOG:
        {
            SSUIDLGINFO * pDlgInfo = (SSUIDLGINFO *)lParam;
            ASSERT(pDlgInfo != NULL);

            SetWindowLong(hwndDlg, DWL_USER, lParam);

            // Resize the dialog -- controls are to the right in RC.
            RECT rectCancel;
            GetWindowRect(GetDlgItem(hwndDlg, IDCANCEL), &rectCancel);
            MapWindowPoints(HWND_DESKTOP, hwndDlg, (POINT *)&rectCancel, 2);

            SetWindowPos(   hwndDlg,
                            NULL,
                            0,
                            0,
                            rectCancel.right
                                + (GetSystemMetrics(SM_CXDLGFRAME) * 2)
                                + CX_DIALOG_GUTTER,
                            rectCancel.bottom
                                + (GetSystemMetrics(SM_CYDLGFRAME) * 2)
                                + GetSystemMetrics(SM_CYCAPTION)
                                + CY_DIALOG_GUTTER,
                            SWP_NOMOVE
                                | SWP_NOACTIVATE
                                | SWP_NOZORDER
                                | SWP_NOOWNERZORDER);

            // Initialize the initial dialog settings and content.
            InitDlgCtls(hwndDlg, pDlgInfo);
            InitDlgInfo(hwndDlg, pDlgInfo);

            break;
        }

        case WM_HELP:
        {
            MLWinHelpWrap((HWND)((LPHELPINFO) lParam)->hItemHandle,
                    c_szHelpFile,
                    HELP_WM_HELP,
                    (DWORD)aCustomDlgHelpIDs);
            break;
        }

        case WM_CONTEXTMENU:
        {
            MLWinHelpWrap((HWND)wParam,
                    c_szHelpFile,
                    HELP_CONTEXTMENU,
                    (DWORD)aCustomDlgHelpIDs);
            break;
        }

        case WM_COMMAND:
        {
            if (pDlgInfo == NULL)
                return FALSE;

            switch (LOWORD(wParam))
            {
                case IDOK:
                {
                    if  (
                        pDlgInfo->bDataChanged
                        &&
                        FAILED(ApplyDlgChanges(hwndDlg, pDlgInfo))
                        )
                    {
                        break;
                    }

                    if (pDlgInfo->bScheduleChanged)
                        pDlgInfo->dwFlags |= SSUI_SCHEDULECHANGED;

                    // FALL THROUGH!!!
                }

                case IDCANCEL:
                {
                    EVAL(SUCCEEDED(SchedGroupComboBox_Clear(GetDlgItem(hwndDlg, IDC_CUSTOM_GROUP_LIST))));
                    EndDialog(hwndDlg, wParam);
                    break;
                }

                case IDC_CUSTOM_GROUP_EDIT:
                {
                    switch (HIWORD(wParam))
                    {
                        case EN_CHANGE:
                        {
                            pDlgInfo->bDataChanged = TRUE;
                            pDlgInfo->bScheduleNameChanged = TRUE;
                            break;
                        }

                        default:
                            break;
                    }

                    break;
                }

                case IDC_CUSTOM_GROUP_LIST:
                {
                    switch (HIWORD(wParam))
                    {
                        case CBN_EDITCHANGE:
                        {
                            pDlgInfo->bDataChanged = TRUE;
                            pDlgInfo->bScheduleNameChanged = TRUE;
                            break;
                        }

                        case CBN_SELCHANGE:
                        {
                            HandleGroupChange(hwndDlg, pDlgInfo);
                            break;
                        }

                        default:
                            break;
                    }

                    break;
                }

                case IDC_CUSTOM_MONTHLY_PERIODIC_LIST1:
                case IDC_CUSTOM_MONTHLY_PERIODIC_LIST2:
                {
                    switch (HIWORD(wParam))
                    {
                        case CBN_SELCHANGE:
                        {
                            OnDataChanged(hwndDlg, pDlgInfo);
                            break;
                        }

                        default:
                            break;
                    }

                    break;
                }

                case IDC_CUSTOM_TIME_REPEATEVERY_EDIT:
                case IDC_CUSTOM_DAILY_EVERYNDAYS_EDIT:
                case IDC_CUSTOM_WEEKLY_REPEATWEEKS_EDIT:
                case IDC_CUSTOM_MONTHLY_DAYOFMONTH_DAY_EDIT:
                case IDC_CUSTOM_MONTHLY_DAYOFMONTH_MONTH_EDIT:
                case IDC_CUSTOM_MONTHLY_PERIODIC_EDIT:
                {
                    switch (HIWORD(wParam))
                    {
                        case EN_CHANGE:
                        {
                            OnDataChanged(hwndDlg, pDlgInfo);
                            break;
                        }

                        case EN_KILLFOCUS:
                        {
                            RestrictDlgItemRange(hwndDlg, LOWORD(wParam), pDlgInfo);
                            break;
                        }

                        default:
                            break;
                    }

                    break;
                }

                default:
                {
                    if (HandleDlgButtonClick(hwndDlg, wParam, pDlgInfo))
                        OnDataChanged(hwndDlg, pDlgInfo);

                    break;
                }
            }

            break;
        }

        case WM_NOTIFY:
        {
            switch (wParam)
            {
                case IDC_CUSTOM_TIME_UPDATEAT_TIME:
                case IDC_CUSTOM_TIME_REPEATBETWEEN_START_TIME:
                case IDC_CUSTOM_TIME_REPEATBETWEEN_END_TIME:
                {
                    switch (((NMHDR *)lParam)->code)
                    {
                        case DTN_DATETIMECHANGE:
                        {
                            OnDataChanged(hwndDlg, pDlgInfo);
                            RestrictTimeRepeatEvery(hwndDlg);
                            break;
                        }

                        default:
                            break;
                    }

                    break;
                }

                default:
                    break;
            }

            break;
        }

        default:
            return FALSE;
    }

    return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// HandleDlgButtonClick
/////////////////////////////////////////////////////////////////////////////
BOOL HandleDlgButtonClick
(
    HWND            hwndDlg,
    WPARAM          wParam,
    SSUIDLGINFO *   pDlgInfo
)
{
    switch (LOWORD(wParam))
    {
        case IDC_CUSTOM_NEWGROUP:
        {
            ASSERT(pDlgInfo != NULL);

            int iReply = IDNO;

            if (pDlgInfo->bDataChanged)
            {
                // Ask user if they want to save changes.
                iReply = SGMessageBox(  hwndDlg,
                                        IDS_CUSTOM_INFO_SAVECHANGES,
                                        MB_YESNOCANCEL | MB_ICONQUESTION);

            }

            switch (iReply)
            {
                case IDYES:
                {
                    // Apply changes
                    if (FAILED(ApplyDlgChanges(hwndDlg, pDlgInfo)))
                        break;

                    // FALL THROUGH!!!
                }

                case IDNO:
                {
                    pDlgInfo->dwFlags |= SSUI_CREATENEWSCHEDULE;
                    *(pDlgInfo->pGroupCookie) = GUID_NULL;

                    SetTaskTriggerToDefaults(&pDlgInfo->ttTaskTrigger);

                    InitDlgInfo(hwndDlg, pDlgInfo);
                    break;
                }

                case IDCANCEL:
                    break;
            }

            break;
        }

        case IDC_CUSTOM_REMOVEGROUP:
        {
            // REVIEW: Can we check to see if the group is really in use. [jaym]

            // Tell user that they are removing a group that may be in use.
            if (SGMessageBox(   hwndDlg,
                                IDS_CUSTOM_INFO_REMOVEGROUP,
                                MB_YESNO | MB_ICONWARNING) == IDYES)
            {
                NOTIFICATIONCOOKIE  groupCookie;
                HWND                hwndCombo = GetDlgItem(hwndDlg, IDC_CUSTOM_GROUP_LIST);

                if  (
                    SUCCEEDED(SchedGroupComboBox_GetCurGroup(   hwndCombo,
                                                                &groupCookie))
                    &&
                    SUCCEEDED(DeleteScheduleGroup(&groupCookie))
                    )
                {
                    // Reset the list and select the first group
                    EVAL(SUCCEEDED(SchedGroupComboBox_Fill(hwndCombo)));
                    ComboBox_SetCurSel(hwndCombo, 0);

                    pDlgInfo->dwFlags |= SSUI_SCHEDULEREMOVED;

                    // Make sure we don't ask the user to save changes.
                    pDlgInfo->bDataChanged = FALSE;

                    HandleGroupChange(hwndDlg, pDlgInfo);
                }
                else
                {
                    SGMessageBox(   hwndDlg,
                                    IDS_CUSTOM_ERROR_REMOVEGROUP,
                                    MB_OK | MB_ICONWARNING);
                    break;
                }
            }

            break;
        }

        case IDC_CUSTOM_WEEKLY_DAY1:
        case IDC_CUSTOM_WEEKLY_DAY2:
        case IDC_CUSTOM_WEEKLY_DAY3:
        case IDC_CUSTOM_WEEKLY_DAY4:
        case IDC_CUSTOM_WEEKLY_DAY5:
        case IDC_CUSTOM_WEEKLY_DAY6:
        case IDC_CUSTOM_WEEKLY_DAY7:
        case IDC_CUSTOM_MINIMIZENETUSE:
        {
            return TRUE;
        }

        default:
            return HandleButtonClick(hwndDlg, wParam);
    }

    return FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// HandleButtonClick
/////////////////////////////////////////////////////////////////////////////
BOOL HandleButtonClick
(
    HWND    hwndDlg,
    WPARAM  wParam
)
{
    BOOL bEnable = (IsDlgButtonChecked(hwndDlg, LOWORD(wParam)) == BST_CHECKED);

    switch (LOWORD(wParam))
    {
        case IDC_CUSTOM_DAILY:
        case IDC_CUSTOM_WEEKLY:
        case IDC_CUSTOM_MONTHLY:
        {
            SetDlgCustomType(hwndDlg, LOWORD(wParam));
            break;
        }

        case IDC_CUSTOM_DAILY_EVERYNDAYS:
        {
            Edit_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_DAILY_EVERYNDAYS_EDIT), bEnable);
            EnableWindow(GetDlgItem(hwndDlg, IDC_CUSTOM_DAILY_EVERYNDAYS_SPIN), bEnable);
            break;
        }

        case IDC_CUSTOM_DAILY_EVERYWEEKDAY:
        {
            Edit_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_DAILY_EVERYNDAYS_EDIT), !bEnable);
            EnableWindow(GetDlgItem(hwndDlg, IDC_CUSTOM_DAILY_EVERYNDAYS_SPIN), !bEnable);
            break;
        }

        case IDC_CUSTOM_MONTHLY_DAYOFMONTH:
        {
            Edit_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY_DAYOFMONTH_DAY_EDIT), bEnable);
            EnableWindow(GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY_DAYOFMONTH_DAY_SPIN), bEnable);
            Edit_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY_DAYOFMONTH_MONTH_EDIT), bEnable);
            EnableWindow(GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY_DAYOFMONTH_MONTH_SPIN), bEnable);

            ListBox_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY_PERIODIC_LIST1), !bEnable);
            ListBox_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY_PERIODIC_LIST2), !bEnable);
            Edit_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY_PERIODIC_EDIT), !bEnable);
            EnableWindow(GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY_PERIODIC_SPIN), !bEnable);
            break;
        }

        case IDC_CUSTOM_MONTHLY_PERIODIC:
        {
            ListBox_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY_PERIODIC_LIST1), bEnable);
            ListBox_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY_PERIODIC_LIST2), bEnable);
            Edit_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY_PERIODIC_EDIT), bEnable);
            EnableWindow(GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY_PERIODIC_SPIN), bEnable);

            Edit_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY_DAYOFMONTH_DAY_EDIT), !bEnable);
            EnableWindow(GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY_DAYOFMONTH_DAY_SPIN), !bEnable);
            Edit_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY_DAYOFMONTH_MONTH_EDIT), !bEnable);
            EnableWindow(GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY_DAYOFMONTH_MONTH_SPIN), !bEnable);
            break;
        }

        case IDC_CUSTOM_TIME_UPDATEAT:
        {
            EnableWindow(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_UPDATEAT_TIME), bEnable);
            Edit_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_REPEATEVERY_EDIT), !bEnable);
            EnableWindow(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_REPEATEVERY_SPIN), !bEnable);

            EnableWindow(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_REPEATBETWEEN), !bEnable);
            EnableWindow(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_REPEATBETWEEN_START_TIME), !bEnable);
            EnableWindow(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_REPEATBETWEEN_END_TIME), !bEnable);
            break;
        }

        case IDC_CUSTOM_TIME_REPEATEVERY:
        {
            Edit_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_REPEATEVERY_EDIT), bEnable);
            EnableWindow(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_REPEATEVERY_SPIN), bEnable);

            EnableWindow(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_REPEATBETWEEN), bEnable);
            EnableWindow(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_UPDATEAT_TIME), !bEnable);

            bEnable = (IsDlgButtonChecked(hwndDlg, IDC_CUSTOM_TIME_REPEATBETWEEN) == BST_CHECKED);

            EnableWindow(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_REPEATBETWEEN_START_TIME), bEnable);
            EnableWindow(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_REPEATBETWEEN_END_TIME), bEnable);

            break;
        }

        case IDC_CUSTOM_TIME_REPEATBETWEEN:
        {
            EnableWindow(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_REPEATBETWEEN_START_TIME), bEnable);
            EnableWindow(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_REPEATBETWEEN_END_TIME), bEnable);

            // Restrict 'repeat between' to time range - 1hr
            if (bEnable)
                RestrictTimeRepeatEvery(hwndDlg);
            else
            {
                UpDown_SetRange(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_REPEATEVERY_SPIN),
                                DEFAULT_REPEATUPDATE_HRS_MIN, DEFAULT_REPEATUPDATE_HRS_MAX);
            }

            break;
        }

        default:
            return FALSE;
    }

    return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// HandleGroupChange
/////////////////////////////////////////////////////////////////////////////
BOOL HandleGroupChange
(
    HWND            hwndDlg,
    SSUIDLGINFO *   pDlgInfo
)
{
    int     iReply = IDNO;
    BOOL    bChanged = FALSE;

    if (pDlgInfo->bDataChanged)
    {
        // Ask user if they want to save changes.
        iReply = SGMessageBox(  hwndDlg,
                                IDS_CUSTOM_INFO_SAVECHANGES,
                                MB_YESNO | MB_ICONQUESTION);
    }

    switch (iReply)
    {
        case IDYES:
        {
            // Apply changes
            if (FAILED(ApplyDlgChanges(hwndDlg, pDlgInfo)))
                break;

            // FALL THROUGH!!!
        }

        case IDNO:
        {
            // No longer creating new schedule, the
            // user has selected an existing schedule.
            pDlgInfo->dwFlags &= ~SSUI_CREATENEWSCHEDULE;

            EVAL(SUCCEEDED(SchedGroupComboBox_GetCurGroup(  GetDlgItem(hwndDlg, IDC_CUSTOM_GROUP_LIST),
                                                            pDlgInfo->pGroupCookie)));

            EVAL(SUCCEEDED(GetSchedGroupTaskTrigger( pDlgInfo->pGroupCookie,
                                                     &pDlgInfo->ttTaskTrigger)));

            InitDlgInfo(hwndDlg, pDlgInfo);

            pDlgInfo->bScheduleChanged = TRUE;
            bChanged = TRUE;

            break;
        }

        default:
        {
            ASSERT(FALSE);
            break;
        }
    }

    return bChanged;
}

/////////////////////////////////////////////////////////////////////////////
// InitDlgCtls
/////////////////////////////////////////////////////////////////////////////
void InitDlgCtls
(
    HWND            hwndDlg,
    SSUIDLGINFO *   pDlgInfo
)
{
    DWORD i;

    // Reposition the dialog controls.
    PositionDlgCtls(hwndDlg);

    // Load the common combo box values.
    for (i = 0; i < ARRAYSIZE(c_rgnCtlItems); i++)
    {
        HWND hwndCtl = GetDlgItem(hwndDlg, c_rgnCtlItems[i].idContainer);
        
        ASSERT(hwndCtl != NULL);

        for (int j = c_rgnCtlItems[i].idFirst; j <= c_rgnCtlItems[i].idLast; j++)
        {
            TCHAR szString[MAX_LOADSTRING_LEN];
            EVAL(MLLoadString(j, szString, MAX_LOADSTRING_LEN) != 0);
            EVAL(ComboBox_AddString(hwndCtl, szString) != CB_ERR);
        }
    }

    DWORD dwFirstDay = 0;
    EVAL(GetLocaleInfo( LOCALE_USER_DEFAULT,
                        LOCALE_IFIRSTDAYOFWEEK,
                        (char *)&dwFirstDay,
                        SIZEOF(DWORD)));
    dwFirstDay = LOCALE_SDAYNAME1 + (dwFirstDay - TEXT('0'));

    // Load check and combo boxes with the days of the weeks
    int     iDay;
    HWND    hwndCombo = GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY_PERIODIC_LIST2);
    TCHAR   szDayName[32];

    for (i = dwFirstDay; i < dwFirstDay+7; i++)
    {
        iDay = ((i <= LOCALE_SDAYNAME7) ? i : (LOCALE_SDAYNAME1 + (i - LOCALE_SDAYNAME7 - 1)));

        EVAL(GetLocaleInfo( LOCALE_USER_DEFAULT,
                            iDay,
                            szDayName,
                            ARRAYSIZE(szDayName)));

        SetDlgItemText( hwndDlg,
                        IDC_CUSTOM_WEEKLY_DAY1 + (i - dwFirstDay),
                        szDayName);

        int iIndex = ComboBox_AddString(hwndCombo, szDayName);

        // Initialize the lookup table for the combobox as we go.
        if (iDay == LOCALE_SDAYNAME1)
            s_rgwDaysOfTheWeek[iIndex] = TASK_MONDAY;
        else if (iDay == LOCALE_SDAYNAME2)
            s_rgwDaysOfTheWeek[iIndex] = TASK_TUESDAY;
        else if (iDay == LOCALE_SDAYNAME3)
            s_rgwDaysOfTheWeek[iIndex] = TASK_WEDNESDAY;
        else if (iDay == LOCALE_SDAYNAME4)
            s_rgwDaysOfTheWeek[iIndex] = TASK_THURSDAY;
        else if (iDay == LOCALE_SDAYNAME5)
            s_rgwDaysOfTheWeek[iIndex] = TASK_FRIDAY;
        else if (iDay == LOCALE_SDAYNAME6)
            s_rgwDaysOfTheWeek[iIndex] = TASK_SATURDAY;
        else if (iDay == LOCALE_SDAYNAME7)
            s_rgwDaysOfTheWeek[iIndex] = TASK_SUNDAY;
        else    
            ASSERT(FALSE);
   }

    // Show/Hide controls based on dialog type.
    if (pDlgInfo->dwFlags & SSUI_CREATENEWSCHEDULE)
    {
        // Hide the 'New' and 'Remove' buttons if
        // we are creating a new schedule group.
        ShowWindow(GetDlgItem(hwndDlg, IDC_CUSTOM_NEWGROUP), SW_HIDE);
        ShowWindow(GetDlgItem(hwndDlg, IDC_CUSTOM_REMOVEGROUP), SW_HIDE);

        // Hide the dropdown schedule group list.
        ShowWindow(GetDlgItem(hwndDlg, IDC_CUSTOM_GROUP_LIST), SW_HIDE);
        ShowWindow(GetDlgItem(hwndDlg, IDC_CUSTOM_GROUP_EDIT), SW_SHOW);
    }
    else if (pDlgInfo->dwFlags & SSUI_EDITSCHEDULE)
    {
        // Hide the schedule group name edit.
        ShowWindow(GetDlgItem(hwndDlg, IDC_CUSTOM_GROUP_EDIT), SW_HIDE);
        ShowWindow(GetDlgItem(hwndDlg, IDC_CUSTOM_GROUP_LIST), SW_SHOW);

        EVAL(SUCCEEDED(SchedGroupComboBox_Fill(GetDlgItem(hwndDlg, IDC_CUSTOM_GROUP_LIST))));
        EVAL(SUCCEEDED(SchedGroupComboBox_SetCurGroup(GetDlgItem(hwndDlg, IDC_CUSTOM_GROUP_LIST), pDlgInfo->pGroupCookie)));
    }
    else
        ASSERT(FALSE);

    // Setup spinner ranges.
    UpDown_SetRange(GetDlgItem(hwndDlg, IDC_CUSTOM_DAILY_EVERYNDAYS_SPIN),
                    DEFAULT_DAILY_EVERYNDAYS_MIN, DEFAULT_DAILY_EVERYNDAYS_MAX);
    UpDown_SetRange(GetDlgItem(hwndDlg, IDC_CUSTOM_WEEKLY_REPEATWEEKS_SPIN),
                    DEFAULT_WEEKLY_REPEATWEEKS_MIN, DEFAULT_WEEKLY_REPEATWEEKS_MAX);
    UpDown_SetRange(GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY_DAYOFMONTH_DAY_SPIN),
                    DEFAULT_MONTHLY_DAY_MIN, DEFAULT_MONTHLY_DAY_MAX);
    UpDown_SetRange(GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY_DAYOFMONTH_MONTH_SPIN),
                    DEFAULT_MONTHLY_MONTHS_MIN, DEFAULT_MONTHLY_MONTHS_MAX);
    UpDown_SetRange(GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY_PERIODIC_SPIN),
                    DEFAULT_MONTHLY_MONTHS_MIN, DEFAULT_MONTHLY_MONTHS_MAX);
    UpDown_SetRange(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_REPEATEVERY_SPIN),
                    DEFAULT_REPEATUPDATE_HRS_MIN, DEFAULT_REPEATUPDATE_HRS_MAX);

    // Setup the time picker controls to use a short time format with no seconds.
    TCHAR szTimeFormat[MAX_SHORTTIMEFORMAT_LEN];
    LPTSTR pszTimeFormat = szTimeFormat;
    EVAL(GetLocaleInfo( LOCALE_USER_DEFAULT,
                        LOCALE_STIMEFORMAT,
                        szTimeFormat,
                        ARRAYSIZE(szTimeFormat)));

    TCHAR szTimeSep[MAX_TIMESEP_LEN];
    EVAL(GetLocaleInfo( LOCALE_USER_DEFAULT,
                        LOCALE_STIME,
                        szTimeSep,
                        ARRAYSIZE(szTimeSep)));
    int cchTimeSep = lstrlen(szTimeSep);

    TCHAR szShortTimeFormat[MAX_SHORTTIMEFORMAT_LEN];
    LPTSTR pszShortTimeFormat = szShortTimeFormat;

    // Remove the seconds format string and preceeding separator.
    while (*pszTimeFormat)
    {
        if ((*pszTimeFormat != TEXT('s')) && (*pszTimeFormat != TEXT('S')))
            *pszShortTimeFormat++ = *pszTimeFormat;
        else
        {
            *pszShortTimeFormat = TEXT('\0');

            StrTrim(szShortTimeFormat, TEXT(" "));
            StrTrim(szShortTimeFormat, szTimeSep);

            pszShortTimeFormat = szShortTimeFormat + lstrlen(szShortTimeFormat);
        }

        pszTimeFormat++;
    }

    *pszShortTimeFormat = TEXT('\0');

    // Set the format for the time picker controls.
    DateTime_SetFormat(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_UPDATEAT_TIME), szShortTimeFormat);
    DateTime_SetFormat(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_REPEATBETWEEN_START_TIME), szShortTimeFormat);
    DateTime_SetFormat(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_REPEATBETWEEN_END_TIME), szShortTimeFormat);
}

/////////////////////////////////////////////////////////////////////////////
// PositionDlgCtls
/////////////////////////////////////////////////////////////////////////////
void PositionDlgCtls
(
    HWND hwndDlg
)
{
    RECT rectDestGroup;
    GetWindowRect(GetDlgItem(hwndDlg, IDC_CUSTOM_GROUP_DEST), &rectDestGroup);
    MapWindowPoints(HWND_DESKTOP, hwndDlg, (POINT *)&rectDestGroup, 2);

    for (int i = 0; i < ARRAYSIZE(c_rgnCtlGroups); i++)
    {
        HWND hwndGroup = GetDlgItem(hwndDlg, c_rgnCtlGroups[i].idContainer);
        
        ASSERT(hwndGroup != NULL);

        RECT rectSrcGroup;
        GetWindowRect(hwndGroup, &rectSrcGroup);
        MapWindowPoints(HWND_DESKTOP, hwndDlg, (POINT *)&rectSrcGroup, 2);

        for (int j = c_rgnCtlGroups[i].idFirst; j <= c_rgnCtlGroups[i].idLast; j++)
        {
            HWND hwndItem = GetDlgItem(hwndDlg, j);

            RECT rectItem;
            GetWindowRect(hwndItem, &rectItem);
            MapWindowPoints(HWND_DESKTOP, hwndDlg, (POINT *)&rectItem, 2);

            MoveWindow( hwndItem,
                        rectDestGroup.left + (rectItem.left - rectSrcGroup.left),
                        rectDestGroup.top + (rectItem.top - rectSrcGroup.top),
                        (rectItem.right - rectItem.left),
                        (rectItem.bottom - rectItem.top),
                        FALSE);
        }
    }
}

/////////////////////////////////////////////////////////////////////////////
// InitDlgDefaults
/////////////////////////////////////////////////////////////////////////////
void InitDlgDefaults
(
    HWND            hwndDlg,
    SSUIDLGINFO *   pDlgInfo
)
{
    TCHAR szDefaultName[MAX_GROUPNAME_LEN];

    // Init control defaults for new schedule group.
    MLLoadString(IDS_CUSTOM_NEWSCHEDDEFNAME, szDefaultName, ARRAYSIZE(szDefaultName));

    if (pDlgInfo->dwFlags & SSUI_CREATENEWSCHEDULE)
    {
        SetDlgItemText(hwndDlg, IDC_CUSTOM_GROUP_EDIT, szDefaultName);
        SetDlgItemText(hwndDlg, IDC_CUSTOM_GROUP_LIST, szDefaultName);
        pDlgInfo->bDataChanged = FALSE;
    }

    // If we are creating a new schedule, everything needs to be saved.
    pDlgInfo->bDataChanged = ((pDlgInfo->dwFlags & SSUI_CREATENEWSCHEDULE) ? TRUE : FALSE);

    // Daily group
    CheckRadioButton(hwndDlg, IDC_TYPE_GROUP_FIRST, IDC_TYPE_GROUP_LAST, IDC_CUSTOM_DAILY);

    // Daily group properties
    CheckRadioButton(hwndDlg, IDC_DAILY_GROUP_FIRST, IDC_DAILY_GROUP_LAST, IDC_CUSTOM_DAILY_EVERYNDAYS);
    HandleDlgButtonClick(hwndDlg, IDC_CUSTOM_DAILY_EVERYNDAYS, NULL);

    SetDlgItemInt(hwndDlg, IDC_CUSTOM_DAILY_EVERYNDAYS_EDIT, DEFAULT_DAILY_EVERYNDAYS, FALSE);

    // Weekly group properties
    SetDlgItemInt(hwndDlg, IDC_CUSTOM_WEEKLY_REPEATWEEKS_EDIT, DEFAULT_WEEKLY_REPEATWEEKS, FALSE);

    SYSTEMTIME stLocal;
    GetLocalTime(&stLocal);

    WORD rgfDaysOfTheWeek = (1 << stLocal.wDayOfWeek);
    SetDlgWeekDays(hwndDlg, rgfDaysOfTheWeek);

    // Monthly group properties
    CheckRadioButton(hwndDlg, IDC_MONTHLY_GROUP_FIRST, IDC_MONTHLY_GROUP_LAST, IDC_CUSTOM_MONTHLY_DAYOFMONTH);
    HandleDlgButtonClick(hwndDlg, IDC_CUSTOM_MONTHLY_DAYOFMONTH, NULL);

    SetDlgItemInt(hwndDlg, IDC_CUSTOM_MONTHLY_DAYOFMONTH_DAY_EDIT, stLocal.wDay, FALSE);
    SetDlgItemInt(hwndDlg, IDC_CUSTOM_MONTHLY_DAYOFMONTH_MONTH_EDIT, DEFAULT_MONTHLY_MONTHS, FALSE);

    // REVIEW: Create code to figure out item to select for current date. [jaym]
    ComboBox_SetCurSel(GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY_PERIODIC_LIST1), 0);
    ComboBox_SetCurSel( GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY_PERIODIC_LIST2),
                        DayIndexFromDaysOfTheWeekFlags(rgfDaysOfTheWeek));

    SetDlgItemInt(hwndDlg, IDC_CUSTOM_MONTHLY_PERIODIC_EDIT, DEFAULT_MONTHLY_MONTHS, FALSE);

    // Time properties
    CheckRadioButton(hwndDlg, IDC_TIME_GROUP_FIRST, IDC_TIME_GROUP_LAST, IDC_CUSTOM_TIME_UPDATEAT);
    HandleDlgButtonClick(hwndDlg, IDC_CUSTOM_TIME_UPDATEAT, NULL);

    SYSTEMTIME st = { 0 };
    GetLocalTime(&st);
    st.wHour    = DEFAULT_UPDATETIME_HRS;
    st.wMinute  = DEFAULT_UPDATETIME_MINS;
    DateTime_SetSystemtime(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_UPDATEAT_TIME), GDT_VALID, &st);

    SetDlgItemInt(hwndDlg, IDC_CUSTOM_TIME_REPEATEVERY_EDIT, DEFAULT_REPEATUPDATE_HRS, FALSE);

    st.wHour    = DEFAULT_REPEAT_START_HRS;
    st.wMinute  = DEFAULT_REPEAT_START_MINS;
    DateTime_SetSystemtime(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_REPEATBETWEEN_START_TIME), GDT_VALID, &st);

    st.wHour    = DEFAULT_REPEAT_END_HRS;
    st.wMinute  = DEFAULT_REPEAT_END_MINS;
    DateTime_SetSystemtime(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_REPEATBETWEEN_END_TIME), GDT_VALID, &st);

    // Disable some controls if we are a predefined group.
    HWND hwndGroupList = GetDlgItem(hwndDlg, IDC_CUSTOM_GROUP_LIST);
    HWND hwndGroupEdit = FindWindowEx(  hwndGroupList,
                                        NULL,
                                        TEXT("Edit"),
                                        NULL);

    if (*(pDlgInfo->pGroupCookie) == NOTFCOOKIE_SCHEDULE_GROUP_DAILY)
    {
        Button_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_DAILY), TRUE);
        Button_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_WEEKLY), FALSE);
        Button_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY), FALSE);
        Button_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_REMOVEGROUP), FALSE);
        Edit_SetReadOnly(hwndGroupEdit, TRUE);
    }
    else if (*(pDlgInfo->pGroupCookie) == NOTFCOOKIE_SCHEDULE_GROUP_WEEKLY)
    {
        Button_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_DAILY), FALSE);
        Button_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_WEEKLY), TRUE);
        Button_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY), FALSE);
        Button_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_REMOVEGROUP), FALSE);
        Edit_SetReadOnly(hwndGroupEdit, TRUE);
    }
    else if (*(pDlgInfo->pGroupCookie) == NOTFCOOKIE_SCHEDULE_GROUP_MONTHLY)
    {
        Button_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_DAILY), FALSE);
        Button_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_WEEKLY), FALSE);
        Button_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY), TRUE);
        Button_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_REMOVEGROUP), FALSE);
        Edit_SetReadOnly(hwndGroupEdit, TRUE);
    }
    else
    {
        Button_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_DAILY), TRUE);
        Button_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_WEEKLY), TRUE);
        Button_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY), TRUE);
        Button_Enable(GetDlgItem(hwndDlg, IDC_CUSTOM_REMOVEGROUP), TRUE);
        Edit_SetReadOnly(hwndGroupEdit, FALSE);
    }

    // Make sure the combo redraws so the enable
    // or disable of the edit control looks good.
    RedrawWindow(hwndGroupList, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);

    // Vary start of next update
    CheckDlgButton(hwndDlg, IDC_CUSTOM_MINIMIZENETUSE, BST_CHECKED);
}

/////////////////////////////////////////////////////////////////////////////
// SetDlgCustomType
/////////////////////////////////////////////////////////////////////////////
void SetDlgCustomType
(
    HWND    hwndDlg,
    int     iType
)
{
    int i;

    for (i = IDC_CUSTOM_DAILY_FIRST; i <= IDC_CUSTOM_DAILY_LAST; i++)
        ShowWindow(GetDlgItem(hwndDlg, i), ((iType == IDC_CUSTOM_DAILY) ? SW_SHOW : SW_HIDE));

    for (i = IDC_CUSTOM_WEEKLY_FIRST; i <= IDC_CUSTOM_WEEKLY_LAST; i++)
        ShowWindow(GetDlgItem(hwndDlg, i), ((iType == IDC_CUSTOM_WEEKLY) ? SW_SHOW : SW_HIDE));

    for (i = IDC_CUSTOM_MONTHLY_FIRST; i <= IDC_CUSTOM_MONTHLY_LAST; i++)
        ShowWindow(GetDlgItem(hwndDlg, i), ((iType == IDC_CUSTOM_MONTHLY) ? SW_SHOW : SW_HIDE));
}

/////////////////////////////////////////////////////////////////////////////
// InitDlgInfo
/////////////////////////////////////////////////////////////////////////////
void InitDlgInfo
(
    HWND            hwndDlg,
    SSUIDLGINFO *   pDlgInfo
)
{
    int             nIDTriggerType;
    TASK_TRIGGER *  pTT = &(pDlgInfo->ttTaskTrigger);

#ifdef DEBUG
    DumpTaskTrigger(pTT);
#endif  // DEBUG

    pDlgInfo->bInitializing = TRUE;

    // Initialize defaults for dialog controls.
    InitDlgDefaults(hwndDlg, pDlgInfo);

    CheckDlgButton( hwndDlg,
                    IDC_CUSTOM_MINIMIZENETUSE,
                    ((pTT->wRandomMinutesInterval == 0) ? BST_UNCHECKED : BST_CHECKED));

    // Initialize the 'Time' settings.
    if (pTT->MinutesInterval == 0)
    {
        // Update at N
        CheckRadioButton(hwndDlg, IDC_TIME_GROUP_FIRST, IDC_TIME_GROUP_LAST, IDC_CUSTOM_TIME_UPDATEAT);
        HandleDlgButtonClick(hwndDlg, IDC_CUSTOM_TIME_UPDATEAT, NULL);

        SYSTEMTIME st = { 0 };
        GetLocalTime(&st);
        st.wHour    = pTT->wStartHour;
        st.wMinute  = pTT->wStartMinute;
        DateTime_SetSystemtime(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_UPDATEAT_TIME), GDT_VALID, &st);
    }
    else
    {
        // Repeat every N hour(s)
        CheckRadioButton(hwndDlg, IDC_TIME_GROUP_FIRST, IDC_TIME_GROUP_LAST, IDC_CUSTOM_TIME_REPEATEVERY);
        HandleDlgButtonClick(hwndDlg, IDC_CUSTOM_TIME_REPEATEVERY, NULL);

        SetDlgItemInt(  hwndDlg,
                        IDC_CUSTOM_TIME_REPEATEVERY_EDIT,
                        ((pDlgInfo->dwRepeatHrsAreMins)
                            ? pTT->MinutesInterval
                            : (pTT->MinutesInterval / 60)),
                        FALSE);

        // Between X and Y
        if (pTT->MinutesDuration != (24 * 60))
        {
            CheckDlgButton(hwndDlg, IDC_CUSTOM_TIME_REPEATBETWEEN, BST_CHECKED);
            HandleDlgButtonClick(hwndDlg, IDC_CUSTOM_TIME_REPEATBETWEEN, NULL);

            SYSTEMTIME st = { 0 };
            GetLocalTime(&st);
            st.wHour    = pTT->wStartHour;
            st.wMinute  = pTT->wStartMinute;
            DateTime_SetSystemtime(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_REPEATBETWEEN_START_TIME), GDT_VALID, &st);

            DWORD dwEndTime = IncTime(  MAKELONG(pTT->wStartHour, pTT->wStartMinute),
                                        (pTT->MinutesDuration / 60),
                                        (pTT->MinutesDuration % 60));

            st.wHour = LOWORD(dwEndTime);
            st.wMinute = HIWORD(dwEndTime);
            DateTime_SetSystemtime(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_REPEATBETWEEN_END_TIME), GDT_VALID, &st);
        }
    }

    // Initialize the 'Days' settings.
    switch (pTT->TriggerType)
    {
        case TASK_TIME_TRIGGER_DAILY:
        {
            nIDTriggerType = IDC_CUSTOM_DAILY;

            // Every N day(s)
            CheckRadioButton(hwndDlg, IDC_DAILY_GROUP_FIRST, IDC_DAILY_GROUP_LAST, IDC_CUSTOM_DAILY_EVERYNDAYS);
            HandleDlgButtonClick(hwndDlg, IDC_CUSTOM_DAILY_EVERYNDAYS, NULL);

            SetDlgItemInt(hwndDlg, IDC_CUSTOM_DAILY_EVERYNDAYS_EDIT, pTT->Type.Daily.DaysInterval, FALSE);
            break;
        }

        case TASK_TIME_TRIGGER_WEEKLY:
        {
            // Special case for Weekly repeating every 1 week
            if  (
                (pTT->Type.Weekly.WeeksInterval == 1)
                &&
                (pTT->Type.Weekly.rgfDaysOfTheWeek == TASK_WEEKDAYS)
                )
            {
                if (*(pDlgInfo->pGroupCookie) == NOTFCOOKIE_SCHEDULE_GROUP_WEEKLY)
                {
                    nIDTriggerType = IDC_CUSTOM_WEEKLY;

                    // Weekly -- Every 1 week on MTWTF
                    SetDlgItemInt(hwndDlg, IDC_CUSTOM_WEEKLY_REPEATWEEKS_EDIT, 1, FALSE);
                    SetDlgWeekDays(hwndDlg, TASK_WEEKDAYS);
                }
                else
                {
                    nIDTriggerType = IDC_CUSTOM_DAILY;

                    // Daily -- Every weekday
                    CheckRadioButton(hwndDlg, IDC_DAILY_GROUP_FIRST, IDC_DAILY_GROUP_LAST, IDC_CUSTOM_DAILY_EVERYWEEKDAY);
                    HandleDlgButtonClick(hwndDlg, IDC_CUSTOM_DAILY_EVERYWEEKDAY, NULL);
                }
            }
            else
            {
                nIDTriggerType = IDC_CUSTOM_WEEKLY;

                // Weekly -- Every N weeks on SMTWTFS
                 
                // Every N weeks
                SetDlgItemInt(hwndDlg, IDC_CUSTOM_WEEKLY_REPEATWEEKS_EDIT, pTT->Type.Weekly.WeeksInterval, FALSE);

                // on SMTWTFS
                SetDlgWeekDays(hwndDlg, pTT->Type.Weekly.rgfDaysOfTheWeek);
            }
            break;
        }

        case TASK_TIME_TRIGGER_MONTHLYDATE:
        {
            nIDTriggerType = IDC_CUSTOM_MONTHLY;

            // Monthly -- Day X of every Y month(s)
            CheckRadioButton(hwndDlg, IDC_MONTHLY_GROUP_FIRST, IDC_MONTHLY_GROUP_LAST, IDC_CUSTOM_MONTHLY_DAYOFMONTH);
            HandleDlgButtonClick(hwndDlg, IDC_CUSTOM_MONTHLY_DAYOFMONTH, NULL);

            // Day X
            SetDlgItemInt(  hwndDlg,
                            IDC_CUSTOM_MONTHLY_DAYOFMONTH_DAY_EDIT,
                            DayFromDaysFlags(pTT->Type.MonthlyDate.rgfDays),
                            FALSE);

            // of every Y month(s)
            SetDlgItemMonth(hwndDlg,
                            IDC_CUSTOM_MONTHLY_DAYOFMONTH_MONTH_EDIT,
                            pTT->Type.MonthlyDate.rgfMonths);
            break;
        }

        case TASK_TIME_TRIGGER_MONTHLYDOW:
        {
            nIDTriggerType = IDC_CUSTOM_MONTHLY;

            // Monthly -- The X Y of every Z month(s)
            CheckRadioButton(hwndDlg, IDC_MONTHLY_GROUP_FIRST, IDC_MONTHLY_GROUP_LAST, IDC_CUSTOM_MONTHLY_PERIODIC);
            HandleDlgButtonClick(hwndDlg, IDC_CUSTOM_MONTHLY_PERIODIC, NULL);

            // The X
            ComboBox_SetCurSel( GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY_PERIODIC_LIST1),
                                pTT->Type.MonthlyDOW.wWhichWeek-1);

            // Y
            ComboBox_SetCurSel( GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY_PERIODIC_LIST2),
                                DayIndexFromDaysOfTheWeekFlags(pTT->Type.MonthlyDOW.rgfDaysOfTheWeek));

            // of every Z month(s)
            SetDlgItemMonth(hwndDlg,
                            IDC_CUSTOM_MONTHLY_PERIODIC_EDIT,
                            pTT->Type.MonthlyDOW.rgfMonths);
            break;
        }

        default:
        {
            ASSERT(FALSE);
            break;
        }
    }

    CheckRadioButton(hwndDlg, IDC_TYPE_GROUP_FIRST, IDC_TYPE_GROUP_LAST, nIDTriggerType);
    HandleDlgButtonClick(hwndDlg, nIDTriggerType, NULL);

    SetTaskTriggerFromDlg(hwndDlg, pDlgInfo);
    SetDlgItemNextUpdate(hwndDlg, IDC_CUSTOM_NEXTUPDATE, pDlgInfo);

    // Set focus to the group name control.
    if (IsWindowVisible(GetDlgItem(hwndDlg, IDC_CUSTOM_GROUP_EDIT)))
        SetFocus(GetDlgItem(hwndDlg, IDC_CUSTOM_GROUP_EDIT));
    else
        SetFocus(GetDlgItem(hwndDlg, IDC_CUSTOM_GROUP_LIST));

    // Make sure we get asked to save for new groups.
    pDlgInfo->bDataChanged = ((pDlgInfo->dwFlags & SSUI_CREATENEWSCHEDULE) ? TRUE : FALSE);

    pDlgInfo->bScheduleNameChanged = FALSE;

    pDlgInfo->bInitializing = FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// SetDlgWeekDays
/////////////////////////////////////////////////////////////////////////////
void SetDlgWeekDays
(
    HWND hwndDlg,
    WORD rgfDaysOfTheWeek
)
{
    // If it doesn't have any days, stick one in arbitrarily.
    if (rgfDaysOfTheWeek == 0)
        rgfDaysOfTheWeek = TASK_SUNDAY;

    DWORD dwFirstDay = 0;
    EVAL(GetLocaleInfo( LOCALE_USER_DEFAULT,
                        LOCALE_IFIRSTDAYOFWEEK,
                        (char *)&dwFirstDay,
                        SIZEOF(DWORD)));

    dwFirstDay = LOCALE_SDAYNAME1 + (dwFirstDay - TEXT('0'));

    for (DWORD i = dwFirstDay; i <= dwFirstDay+7; i++)
    {
        BOOL    bCheck; 
        int     iDay = ((i <= LOCALE_SDAYNAME7) ? i
                                                : (LOCALE_SDAYNAME1 + (i - LOCALE_SDAYNAME7 - 1)));

        if (iDay == LOCALE_SDAYNAME1)
            bCheck = (rgfDaysOfTheWeek & TASK_MONDAY);
        else if (iDay == LOCALE_SDAYNAME2)
            bCheck = (rgfDaysOfTheWeek & TASK_TUESDAY);
        else if (iDay == LOCALE_SDAYNAME3)
            bCheck = (rgfDaysOfTheWeek & TASK_WEDNESDAY);
        else if (iDay == LOCALE_SDAYNAME4)
            bCheck = (rgfDaysOfTheWeek & TASK_THURSDAY);
        else if (iDay == LOCALE_SDAYNAME5)
            bCheck = (rgfDaysOfTheWeek & TASK_FRIDAY);
        else if (iDay == LOCALE_SDAYNAME6)
            bCheck = (rgfDaysOfTheWeek & TASK_SATURDAY);
        else if (iDay == LOCALE_SDAYNAME7)
            bCheck = (rgfDaysOfTheWeek & TASK_SUNDAY);
        else
            ASSERT(FALSE);

        CheckDlgButton( hwndDlg,
                        IDC_CUSTOM_WEEKLY_DAY1 + (i - dwFirstDay),
                        (bCheck ? BST_CHECKED : BST_UNCHECKED));
    }
}

/////////////////////////////////////////////////////////////////////////////
// GetDlgWeekDays
/////////////////////////////////////////////////////////////////////////////
WORD GetDlgWeekDays
(
    HWND hwndDlg
)
{
    WORD wDays = 0;

    DWORD dwFirstDay = 0;
    EVAL(GetLocaleInfo( LOCALE_USER_DEFAULT,
                        LOCALE_IFIRSTDAYOFWEEK,
                        (char *)&dwFirstDay,
                        SIZEOF(DWORD)));

    dwFirstDay = LOCALE_SDAYNAME1 + (dwFirstDay - TEXT('0'));

    for (DWORD i = dwFirstDay; i <= dwFirstDay+7; i++)
    {
        if (IsDlgButtonChecked( hwndDlg,
                                IDC_CUSTOM_WEEKLY_DAY1 + (i - dwFirstDay)) == BST_CHECKED)
        {
            int iDay = ((i <= LOCALE_SDAYNAME7) ? i
                                                : (LOCALE_SDAYNAME1 + (i - LOCALE_SDAYNAME7 - 1)));

            
            if (iDay == LOCALE_SDAYNAME1)
                wDays |= TASK_MONDAY;
            else if (iDay == LOCALE_SDAYNAME2)
                wDays |= TASK_TUESDAY;
            else if (iDay == LOCALE_SDAYNAME3)
                wDays |= TASK_WEDNESDAY;
            else if (iDay == LOCALE_SDAYNAME4)
                wDays |= TASK_THURSDAY;
            else if (iDay == LOCALE_SDAYNAME5)
                wDays |= TASK_FRIDAY;
            else if (iDay == LOCALE_SDAYNAME6)
                wDays |= TASK_SATURDAY;
            else if (iDay == LOCALE_SDAYNAME7)
                wDays |= TASK_SUNDAY;
            else
                ASSERT(FALSE);
        }
    }

    return wDays;
}

/////////////////////////////////////////////////////////////////////////////
// SetTaskTriggerFromDlg
/////////////////////////////////////////////////////////////////////////////
void SetTaskTriggerFromDlg
(
    HWND            hwndDlg,
    SSUIDLGINFO *   pDlgInfo
)
{
    BOOL            bTranslated;
    TASK_TRIGGER *  pTT = &pDlgInfo->ttTaskTrigger;

    ZeroMemory(pTT, SIZEOF(TASK_TRIGGER));
    pTT->cbTriggerSize = SIZEOF(TASK_TRIGGER);

    // Init start date to now.
    SYSTEMTIME st;
    GetLocalTime(&st);
    pTT->wBeginYear    = st.wYear;
    pTT->wBeginMonth   = st.wMonth;
    pTT->wBeginDay     = st.wDay;

    // Vary start of update
    if (IsDlgButtonChecked(hwndDlg, IDC_CUSTOM_MINIMIZENETUSE) == BST_CHECKED)
        pTT->wRandomMinutesInterval = DEFAULT_RANDOM_MINUTES_INTERVAL;
    else
        pTT->wRandomMinutesInterval = 0;

    // Initialize the 'Time' related settings.
    if (IsDlgButtonChecked(hwndDlg, IDC_CUSTOM_TIME_UPDATEAT) == BST_CHECKED)
    {
        // Update at N
        SYSTEMTIME st = { 0 };
        DateTime_GetSystemtime(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_UPDATEAT_TIME), &st);

        pTT->wStartHour    = st.wHour;
        pTT->wStartMinute  = st.wMinute;
    }
    else
    {
        // Repeat every N hours
        ASSERT(IsDlgButtonChecked(hwndDlg, IDC_CUSTOM_TIME_REPEATEVERY) == BST_CHECKED)

        // Between X and Y
        if (IsDlgButtonChecked(hwndDlg, IDC_CUSTOM_TIME_REPEATBETWEEN) == BST_CHECKED)
        {
            // Between X
            SYSTEMTIME st = { 0 };
            DateTime_GetSystemtime( GetDlgItem( hwndDlg, 
                                                IDC_CUSTOM_TIME_REPEATBETWEEN_START_TIME),
                                    &st);

            pTT->wStartHour    = st.wHour;
            pTT->wStartMinute  = st.wMinute;

            WORD wStartMins = (st.wHour * 60) + st.wMinute;

            DateTime_GetSystemtime( GetDlgItem( hwndDlg,
                                                IDC_CUSTOM_TIME_REPEATBETWEEN_END_TIME),
                                    &st);

            WORD wEndMins = (st.wHour * 60) + st.wMinute;

            // And Y
            if (wStartMins > wEndMins)
                pTT->MinutesDuration = (1440 - wStartMins) + wEndMins;
            else
                pTT->MinutesDuration = wEndMins - wStartMins;
        }
        else
        {
            // If we are not between a hour range, begin update at the start of the day.
            pTT->wStartHour         = 0;    // 12:00am
            pTT->wStartMinute       = 0;
            pTT->MinutesDuration    = (24 * 60);
        }

        // Set the interval.
        int iHours = GetDlgItemInt( hwndDlg,
                                    IDC_CUSTOM_TIME_REPEATEVERY_EDIT,
                                    &bTranslated,
                                    FALSE);

        ASSERT(bTranslated && (iHours > 0));

        if (pDlgInfo->dwRepeatHrsAreMins)
            pTT->MinutesInterval = iHours;
        else
            pTT->MinutesInterval = iHours * 60;
    }

    // Initialize the 'Days' related settings.

    // Daily schedule
    if (IsDlgButtonChecked(hwndDlg, IDC_CUSTOM_DAILY) == BST_CHECKED)
    {
        // Daily -- Every N days
        if (IsDlgButtonChecked(hwndDlg, IDC_CUSTOM_DAILY_EVERYNDAYS) == BST_CHECKED)
        {
            pTT->TriggerType = TASK_TIME_TRIGGER_DAILY;
            pTT->Type.Daily.DaysInterval = GetDlgItemInt(   hwndDlg,
                                                            IDC_CUSTOM_DAILY_EVERYNDAYS_EDIT,
                                                            &bTranslated,
                                                            FALSE);
        }
        else
        {
            // Daily -- Every Weekday
            ASSERT(IsDlgButtonChecked(hwndDlg, IDC_CUSTOM_DAILY_EVERYWEEKDAY));

            pTT->TriggerType = TASK_TIME_TRIGGER_WEEKLY;
            pTT->Type.Weekly.WeeksInterval = 1;
            pTT->Type.Weekly.rgfDaysOfTheWeek = TASK_WEEKDAYS;
                                                        
        }
    }
    // Weekly schedule
    else if (IsDlgButtonChecked(hwndDlg, IDC_CUSTOM_WEEKLY) == BST_CHECKED)
    {
        pTT->TriggerType = TASK_TIME_TRIGGER_WEEKLY;

        // Weekly -- Repeat every N days
        pTT->Type.Weekly.WeeksInterval = GetDlgItemInt( hwndDlg,
                                                        IDC_CUSTOM_WEEKLY_REPEATWEEKS_EDIT,
                                                        &bTranslated,
                                                        FALSE);

        // Weekly -- Days of the week
        pTT->Type.Weekly.rgfDaysOfTheWeek = GetDlgWeekDays(hwndDlg);
    }
    // Monthly schedule
    else
    {
        ASSERT(IsDlgButtonChecked(hwndDlg, IDC_CUSTOM_MONTHLY) == BST_CHECKED);

        // Monthly -- Day X of every Y month(s)
        if (IsDlgButtonChecked(hwndDlg, IDC_CUSTOM_MONTHLY_DAYOFMONTH) == BST_CHECKED)
        {
            pTT->TriggerType = TASK_TIME_TRIGGER_MONTHLYDATE;

            int iDate = GetDlgItemInt(  hwndDlg,
                                        IDC_CUSTOM_MONTHLY_DAYOFMONTH_DAY_EDIT,
                                        &bTranslated,
                                        FALSE);

            ASSERT(iDate > 0);

            // Day X
            pTT->Type.MonthlyDate.rgfDays = (1 << (iDate - 1));

            // of every Y month(s)
            pTT->Type.MonthlyDate.rgfMonths = GetDlgItemMonth(hwndDlg, IDC_CUSTOM_MONTHLY_DAYOFMONTH_MONTH_EDIT);
        }
        else
        {
            // Monthly -- The X Y of every Z month(s)
            ASSERT(IsDlgButtonChecked(hwndDlg, IDC_CUSTOM_MONTHLY_PERIODIC) == BST_CHECKED);

            pTT->TriggerType = TASK_TIME_TRIGGER_MONTHLYDOW;

            // The X
            int iWeek = ComboBox_GetCurSel(GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY_PERIODIC_LIST1))+1;
            ASSERT((iWeek >= TASK_FIRST_WEEK) && (iWeek <= TASK_LAST_WEEK));
            pTT->Type.MonthlyDOW.wWhichWeek = iWeek;

            // Y
            int iDaysOfWeek = ComboBox_GetCurSel(GetDlgItem(hwndDlg, IDC_CUSTOM_MONTHLY_PERIODIC_LIST2));
            ASSERT((iDaysOfWeek >= 0) && (iDaysOfWeek < ARRAYSIZE(s_rgwDaysOfTheWeek)));
            pTT->Type.MonthlyDOW.rgfDaysOfTheWeek = s_rgwDaysOfTheWeek[iDaysOfWeek];

            // of every Z month(s)
            pTT->Type.MonthlyDOW.rgfMonths = GetDlgItemMonth(hwndDlg, IDC_CUSTOM_MONTHLY_PERIODIC_EDIT);
        }
    }
}

/////////////////////////////////////////////////////////////////////////////
// SetTaskTriggerToDefaults
/////////////////////////////////////////////////////////////////////////////
void SetTaskTriggerToDefaults
(
    TASK_TRIGGER * pTT
)
{
    ASSERT(pTT != NULL);

    // REVIEW: Make this a const static struct and assign instead of function?
    ZeroMemory(pTT, SIZEOF(TASK_TRIGGER));

    pTT->cbTriggerSize              = SIZEOF(TASK_TRIGGER);
    pTT->wStartHour                 = DEFAULT_UPDATETIME_HRS;
    pTT->wStartMinute               = DEFAULT_UPDATETIME_MINS;
    pTT->TriggerType                = TASK_TIME_TRIGGER_DAILY;
    pTT->Type.Daily.DaysInterval    = DEFAULT_DAILY_EVERYNDAYS;
}

/////////////////////////////////////////////////////////////////////////////
// OnDataChanged
/////////////////////////////////////////////////////////////////////////////
void OnDataChanged
(
    HWND            hwndDlg,
    SSUIDLGINFO *   pDlgInfo
)
{
    // Ignore if we are setting up the dialog control values.
    if (pDlgInfo->bInitializing)
        return;

    // Update the TASK_TRIGGER to new user settings.
    SetTaskTriggerFromDlg(hwndDlg, pDlgInfo);

    // Calc and display next update time.
    SetDlgItemNextUpdate(hwndDlg, IDC_CUSTOM_NEXTUPDATE, pDlgInfo);

    // Data changed
    pDlgInfo->bDataChanged = TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// ApplyDlgChanges
/////////////////////////////////////////////////////////////////////////////
HRESULT ApplyDlgChanges
(
    HWND            hwndDlg,
    SSUIDLGINFO *   pDlgInfo
)
{
    HRESULT hrResult;

    if (ValidateDlgFields(hwndDlg, pDlgInfo))
    {
        HWND hwndCombo = GetDlgItem(hwndDlg, IDC_CUSTOM_GROUP_LIST);

        if (pDlgInfo->dwFlags & SSUI_CREATENEWSCHEDULE)
        {
            if (SUCCEEDED(hrResult = CreateScheduleGroupFromDlg(hwndDlg, pDlgInfo)))
            {
                pDlgInfo->dwFlags |= SSUI_SCHEDULECREATED;

                // Update the combobox with the new group, just
                // in case we aren't closing the dialog.
                TCHAR szGroupName[MAX_GROUPNAME_LEN];
                GetDlgGroupName(hwndDlg, szGroupName, ARRAYSIZE(szGroupName));

                EVAL(SUCCEEDED(SchedGroupComboBox_AddGroup( hwndCombo,
                                                            szGroupName,
                                                            pDlgInfo->pGroupCookie)));
            }
            else
            {
                // REVIEW: Error message! [jaym]
                MessageBeep(MB_ICONEXCLAMATION);
            }
        }
        else
        {
            ASSERT(pDlgInfo->dwFlags & SSUI_EDITSCHEDULE);

            TCHAR szGroupName[MAX_GROUPNAME_LEN];
            GetDlgGroupName(hwndDlg, szGroupName, ARRAYSIZE(szGroupName));

            WCHAR wszGroupName[MAX_GROUPNAME_LEN];
            MyStrToOleStrN(wszGroupName, MAX_GROUPNAME_LEN, szGroupName);

            GROUPINFO gi;
            gi.cbSize = SIZEOF(GROUPINFO);
            gi.pwzGroupname = wszGroupName;

            SetTaskTriggerFromDlg(hwndDlg, pDlgInfo);

#ifdef DEBUG
            DumpTaskTrigger(&pDlgInfo->ttTaskTrigger);
#endif  // DEBUG

            if (SUCCEEDED(hrResult = ModifyScheduleGroup(   pDlgInfo->pGroupCookie,
                                                            &pDlgInfo->ttTaskTrigger,
                                                            NULL,
                                                            &gi,
                                                            0)))
            {
                pDlgInfo->dwFlags |= SSUI_SCHEDULECHANGED;
//xnotfmgr

                // Reset combo list entry because name may have changed.
                NOTIFICATIONCOOKIE cookie;
                EVAL(SUCCEEDED(SchedGroupComboBox_GetCurGroup(hwndCombo, &cookie)));

                EVAL(SUCCEEDED(SchedGroupComboBox_RemoveGroup(hwndCombo, pDlgInfo->pGroupCookie)));
                EVAL(SUCCEEDED(SchedGroupComboBox_AddGroup(hwndCombo, szGroupName, pDlgInfo->pGroupCookie)));

                EVAL(SUCCEEDED(SchedGroupComboBox_SetCurGroup(hwndCombo, &cookie)));
            }
            else
            {
                // REVIEW: Error message! [jaym]
                MessageBeep(MB_ICONEXCLAMATION);
            }
        }
    }
    else
        hrResult = E_FAIL;

    return hrResult;
}

/////////////////////////////////////////////////////////////////////////////
// ValidateDlgFields
/////////////////////////////////////////////////////////////////////////////
BOOL ValidateDlgFields
(
    HWND            hwndDlg,
    SSUIDLGINFO *   pDlgInfo
)
{
    BOOL bResult = FALSE;   // Assume the worst.

    TCHAR szGroupName[MAX_GROUPNAME_LEN];
    GetDlgGroupName(hwndDlg, szGroupName, ARRAYSIZE(szGroupName));

    for (;;)
    {
        int cchGroupNameLen = GetDlgGroupNameLength(hwndDlg);

        // Check length of group name. Tell user if too long.
        if (cchGroupNameLen >= MAX_GROUPNAME_LEN)
        {
            SGMessageBox(   hwndDlg,
                            IDS_CUSTOM_ERROR_NAMETOOLONG,
                            MB_OK | MB_ICONEXCLAMATION);
            break;
        }

        // Make sure we have a description
        if (cchGroupNameLen <= 0)
        {
            SGMessageBox(   hwndDlg,
                            IDS_CUSTOM_ERROR_NAMEREQUIRED,
                            MB_OK | MB_ICONEXCLAMATION);
            break;
        }
        
        // Check to see if group name is already in use.
        if  (
            (
                pDlgInfo->bScheduleNameChanged
                ||
                (pDlgInfo->dwFlags & SSUI_CREATENEWSCHEDULE)
            )
            &&
            ScheduleGroupExists(szGroupName)
            )
        {
            SGMessageBox(   hwndDlg,
                            IDS_CUSTOM_ERROR_NAMEEXISTS,
                            MB_OK | MB_ICONEXCLAMATION);

            break;
        }

        // NOTE: Spin ranges are checked in RestrictDlgItemRange()

        bResult = TRUE;
        break;
    }

    if (!bResult)
    {
        // Set focus to the group name control.
        if (IsWindowVisible(GetDlgItem(hwndDlg, IDC_CUSTOM_GROUP_EDIT)))
            SetFocus(GetDlgItem(hwndDlg, IDC_CUSTOM_GROUP_EDIT));
        else
            SetFocus(GetDlgItem(hwndDlg, IDC_CUSTOM_GROUP_LIST));
    }

    return bResult;
}

/////////////////////////////////////////////////////////////////////////////
// GetDlgGroupName
/////////////////////////////////////////////////////////////////////////////
UINT GetDlgGroupName
(
    HWND    hwndDlg,
    LPTSTR  pszGroupName,
    int     cchGroupNameLen
)
{
    UINT uResult;

    if (IsWindowVisible(GetDlgItem(hwndDlg, IDC_CUSTOM_GROUP_EDIT)))
    {
        uResult = GetDlgItemText(   hwndDlg,
                                    IDC_CUSTOM_GROUP_EDIT,
                                    pszGroupName,
                                    cchGroupNameLen);
    }
    else if (IsWindowVisible(GetDlgItem(hwndDlg, IDC_CUSTOM_GROUP_LIST)))
    {
        uResult = GetDlgItemText(   hwndDlg,
                                    IDC_CUSTOM_GROUP_LIST,
                                    pszGroupName,
                                    cchGroupNameLen);
    }
    else
        uResult = 0;

    return uResult;
}

/////////////////////////////////////////////////////////////////////////////
// GetDlgGroupNameLength
/////////////////////////////////////////////////////////////////////////////
int GetDlgGroupNameLength
(
    HWND hwndDlg
)
{
    HWND hwndCtl;

    if  (
        IsWindowVisible(hwndCtl = GetDlgItem(hwndDlg, IDC_CUSTOM_GROUP_EDIT))
        ||
        IsWindowVisible(hwndCtl = GetDlgItem(hwndDlg, IDC_CUSTOM_GROUP_LIST))
        )
    {
        return GetWindowTextLength(hwndCtl);
    }
    else
        return 0;
}

/////////////////////////////////////////////////////////////////////////////
// CreateScheduleGroupFromDlg
/////////////////////////////////////////////////////////////////////////////
HRESULT CreateScheduleGroupFromDlg
(
    HWND          hwndDlg,
    SSUIDLGINFO * pDlgInfo
)
{
    HRESULT hrResult;

    ASSERT(hwndDlg != NULL);
    ASSERT(IsWindow(hwndDlg));
    ASSERT(pDlgInfo != NULL);

    for (;;)
    {
        *(pDlgInfo->pGroupCookie) = GUID_NULL;
        
        // Get the Trigger info from the dialog control states.
        SetTaskTriggerFromDlg(hwndDlg, pDlgInfo);

#ifdef DEBUG
        DumpTaskTrigger(&pDlgInfo->ttTaskTrigger);
#endif  // DEBUG

        TCHAR szGroupName[MAX_GROUPNAME_LEN];
        GetDlgGroupName(hwndDlg, szGroupName, ARRAYSIZE(szGroupName));

        WCHAR wszGroupName[MAX_GROUPNAME_LEN];
        MyStrToOleStrN(wszGroupName, MAX_GROUPNAME_LEN, szGroupName);

        GROUPINFO gi;
        gi.cbSize = SIZEOF(GROUPINFO);
        gi.pwzGroupname = wszGroupName;

        if (FAILED(hrResult = CreateScheduleGroup(  &pDlgInfo->ttTaskTrigger,
                                                    NULL,
                                                    &gi,
                                                    0,
                                                    pDlgInfo->pGroupCookie)))
        {
            TraceMsg(TF_ERROR, "CSGFD: Unable to CreateScheduleGroup");
            break;
        }

        break;
    }

    return hrResult;
}

/////////////////////////////////////////////////////////////////////////////
// RestrictDlgItemRange
/////////////////////////////////////////////////////////////////////////////
void RestrictDlgItemRange
(
    HWND            hwndDlg,
    int             idCtl,
    SSUIDLGINFO *   pDlgInfo
)
{
    // Special case repeat-every, which is based on the
    // time values of other controls in the dialog.
    if  (
        (idCtl == IDC_CUSTOM_TIME_REPEATEVERY_EDIT)
        &&
        (IsDlgButtonChecked(hwndDlg, IDC_CUSTOM_TIME_REPEATBETWEEN) == BST_CHECKED)
        )
    {
        RestrictTimeRepeatEvery(hwndDlg);
    }
    else
    {
        int     nMin;
        int     nMax;
        int     nDefault;
        BOOL    bTranslated;

        // Get predefined range
        switch (idCtl)
        {
            case IDC_CUSTOM_DAILY_EVERYNDAYS_EDIT:
            {
                nMin = DEFAULT_DAILY_EVERYNDAYS_MIN;
                nMax = DEFAULT_DAILY_EVERYNDAYS_MAX;
                nDefault = DEFAULT_DAILY_EVERYNDAYS;
                break;
            }

            case IDC_CUSTOM_WEEKLY_REPEATWEEKS_EDIT:
            {
                nMin = DEFAULT_WEEKLY_REPEATWEEKS_MIN;
                nMax = DEFAULT_WEEKLY_REPEATWEEKS_MAX;
                nDefault = DEFAULT_WEEKLY_REPEATWEEKS;
                break;
            }

            case IDC_CUSTOM_MONTHLY_DAYOFMONTH_DAY_EDIT:
            {
                SYSTEMTIME stLocal;
                GetLocalTime(&stLocal);

                nMin = DEFAULT_MONTHLY_DAY_MIN;
                nMax = DEFAULT_MONTHLY_DAY_MAX;
                nDefault = stLocal.wDay;
                break;
            }

            case IDC_CUSTOM_MONTHLY_DAYOFMONTH_MONTH_EDIT:
            {
                nMin = DEFAULT_MONTHLY_MONTHS_MIN;
                nMax = DEFAULT_MONTHLY_MONTHS_MAX;
                nDefault = DEFAULT_MONTHLY_MONTHS;
                break;
            }

            case IDC_CUSTOM_MONTHLY_PERIODIC_EDIT:
            {
                nMin = DEFAULT_MONTHLY_MONTHS_MIN;
                nMax = DEFAULT_MONTHLY_MONTHS_MAX;
                nDefault = DEFAULT_MONTHLY_MONTHS;
                break;
            }

            case IDC_CUSTOM_TIME_REPEATEVERY_EDIT:
            {
                nMin = DEFAULT_REPEATUPDATE_HRS_MIN;
                nMax = DEFAULT_REPEATUPDATE_HRS_MAX;
                nDefault = DEFAULT_REPEATUPDATE_HRS;
                break;
            }

            default:
            {
                ASSERT(FALSE);
                break;
            }
        }

        // Get current value
        BOOL    bChanged = FALSE;
        int     nCurrent = (int)GetDlgItemInt(hwndDlg, idCtl, &bTranslated, TRUE);

        if (bTranslated)
        {
            int nRestricted = min(nMax, max(nMin, nCurrent));

            if (nRestricted != nCurrent)
                SetDlgItemInt(hwndDlg, idCtl, nRestricted, TRUE);
        }
        else
            SetDlgItemInt(hwndDlg, idCtl, nDefault, TRUE);

    }
}

/////////////////////////////////////////////////////////////////////////////
// RestrictTimeRepeatEvery
/////////////////////////////////////////////////////////////////////////////
void RestrictTimeRepeatEvery
(
    HWND hwndDlg
)
{
    SYSTEMTIME st = { 0 };
    DateTime_GetSystemtime( GetDlgItem( hwndDlg, 
                                        IDC_CUSTOM_TIME_REPEATBETWEEN_START_TIME),
                            &st);
    WORD wStartMins = (st.wHour * 60) + st.wMinute;

    DateTime_GetSystemtime( GetDlgItem( hwndDlg,
                                        IDC_CUSTOM_TIME_REPEATBETWEEN_END_TIME),
                            &st);
    WORD wEndMins = (st.wHour * 60) + st.wMinute;

    WORD nHours;
    if (wStartMins > wEndMins)
        nHours = ((1440 - wStartMins) + wEndMins) / 60;
    else
        nHours = (wEndMins - wStartMins) / 60;

    if (nHours <= 0)
        nHours = DEFAULT_REPEATUPDATE_HRS_MIN;

    UpDown_SetRange(GetDlgItem(hwndDlg, IDC_CUSTOM_TIME_REPEATEVERY_SPIN),
                    DEFAULT_REPEATUPDATE_HRS_MIN, nHours);

    BOOL bTranslated;
    int nRepeatEvery = GetDlgItemInt(   hwndDlg,
                                        IDC_CUSTOM_TIME_REPEATEVERY_EDIT,
                                        &bTranslated,
                                        FALSE);

    int nRestricted = min(nHours, max(1, nRepeatEvery));
    if (nRestricted != nRepeatEvery)
    {
        SetDlgItemInt(  hwndDlg,
                        IDC_CUSTOM_TIME_REPEATEVERY_EDIT,
                        nRestricted,
                        FALSE);
    }
}

/////////////////////////////////////////////////////////////////////////////
// Helper functions
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// GetSchedGroupTaskTrigger
/////////////////////////////////////////////////////////////////////////////
HRESULT GetSchedGroupTaskTrigger
(
    PNOTIFICATIONCOOKIE pGroupCookie,
    TASK_TRIGGER *      pTT
)
{
//xnotfmgr

    INotificationMgr *  pNotificationMgr = NULL;
    IScheduleGroup *    pScheduleGroup = NULL;
    HRESULT             hrResult;

    ASSERT(pGroupCookie != NULL);
    ASSERT(pTT != NULL);
    
    for (;;)
    {
        ZeroMemory(pTT, SIZEOF(TASK_TRIGGER));
        pTT->cbTriggerSize = SIZEOF(TASK_TRIGGER);

        if (FAILED(hrResult = GetNotificationMgr(&pNotificationMgr)))
        {
            TraceMsg(TF_ERROR, "GSGTaskTrigger: Unable to get NotificationMgr");
            break;
        }

        if (FAILED(hrResult = pNotificationMgr->FindScheduleGroup(  pGroupCookie,
                                                                    &pScheduleGroup,
                                                                    0)))
        {
            TraceMsg(TF_ERROR, "GSGTaskTrigger: Unable to locate schedule group");
            break;
        }

        if (FAILED(hrResult = pScheduleGroup->GetAttributes(pTT,
                                                            NULL,
                                                            NULL,
                                                            NULL,
                                                            NULL,
                                                            NULL)))
        {
            TraceMsg(TF_ERROR, "GSGTaskTrigger: Unable to get schedule group attributes");
            break;
        }

        break;
    }

    SAFERELEASE(pScheduleGroup);
    SAFERELEASE(pNotificationMgr);

    return hrResult;
}

/////////////////////////////////////////////////////////////////////////////
// CreateScheduleGroup
/////////////////////////////////////////////////////////////////////////////
HRESULT CreateScheduleGroup
(
    /* [in] */  PTASK_TRIGGER       pTaskTrigger,
    /* [in] */  PTASK_DATA          pTaskData,
    /* [in] */  PGROUPINFO          pGroupInfo,
    /* [in] */  GROUPMODE           grfGroupMode,
    /* [out] */ PNOTIFICATIONCOOKIE pGroupCookie
)
{
//xnotfmgr

    INotificationMgr *  pNotificationMgr = NULL;
    IScheduleGroup *    pScheduleGroup = NULL;
    HRESULT             hrResult;

    for (;;)
    {
        if (pGroupCookie == NULL)
            return E_INVALIDARG;

        if (FAILED(hrResult = GetNotificationMgr(&pNotificationMgr)))
        {
            TraceMsg(TF_ERROR, "CSG: Unable to get NotificationMgr");
            break;
        }

        if (FAILED(hrResult = pNotificationMgr->CreateScheduleGroup(0,
                                                                    &pScheduleGroup,
                                                                    pGroupCookie,
                                                                    0)))
        {
            TraceMsg(TF_ERROR, "CSG: Unable to CreateScheduleGroup");
            break;
        }

        ASSERT(!pTaskData);
        if (FAILED(hrResult = pScheduleGroup->SetAttributes(pTaskTrigger,
                                                            pTaskData,
                                                            pGroupCookie,
                                                            pGroupInfo,
                                                            grfGroupMode)))
        {
            TraceMsg(TF_ERROR, "CSG: Unable to SetAttributes");
            break;
        }

        break;
    }

    SAFERELEASE(pScheduleGroup);
    SAFERELEASE(pNotificationMgr);

    return hrResult;
}

/////////////////////////////////////////////////////////////////////////////
// ModifyScheduleGroup
/////////////////////////////////////////////////////////////////////////////
HRESULT ModifyScheduleGroup
(
    /* [in] */  PNOTIFICATIONCOOKIE pGroupCookie,
    /* [in] */  PTASK_TRIGGER       pTaskTrigger,
    /* [in] */  PTASK_DATA          pTaskData,
    /* [in] */  PGROUPINFO          pGroupInfo,
    /* [in] */  GROUPMODE           grfGroupMode
)
{
//xnotfmgr

    INotificationMgr *  pNotificationMgr = NULL;
    IScheduleGroup *    pScheduleGroup = NULL;
    HRESULT             hrResult;

    // This might take a while.
    WAITCURSOR Hourglass;

    for (;;)
    {
        if  (
            (pGroupCookie == NULL)
            ||
            (*pGroupCookie == GUID_NULL)
            )
        {
            return E_INVALIDARG;
        }

        if (FAILED(hrResult = GetNotificationMgr(&pNotificationMgr)))
        {
            TraceMsg(TF_ERROR, "MSG: Unable to get NotificationMgr");
            break;
        }

        if (FAILED(hrResult = pNotificationMgr->FindScheduleGroup(  pGroupCookie,
                                                                    &pScheduleGroup,
                                                                    0)))
        {
            TraceMsg(TF_ERROR, "MSG: Unable to locate schedule group");
            break;
        }

        ASSERT(!pTaskData);
        if (FAILED(hrResult = pScheduleGroup->SetAttributes(pTaskTrigger,
                                                            pTaskData,
                                                            pGroupCookie,
                                                            pGroupInfo,
                                                            grfGroupMode)))
        {
            TraceMsg(TF_ERROR, "MSG: Unable to SetAttributes");
            break;
        }

        {
            LPENUMNOTIFICATION  pEnum = NULL;
            NOTIFICATIONITEM    item = {0};
            OOEBuf  ooeBuf;
            HRESULT hr;

            if (FAILED(hr = pScheduleGroup->GetEnumNotification(0, &pEnum)))
            {
                TraceMsg(TF_ERROR, "MSG: Unable to GetEnumNotification");
                break;
            }

            memset(&ooeBuf, 0, sizeof(ooeBuf));

            item.cbSize = sizeof(NOTIFICATIONITEM);
            ULONG   cItems = 0;
            DWORD   dwBufSize = 0;

            hr = pEnum->Next(1, &item, &cItems);
            while (SUCCEEDED(hr) && cItems) {
                ASSERT(item.pNotification);
                hr = LoadOOEntryInfo(&ooeBuf, &item, &dwBufSize);
                SAFERELEASE(item.pNotification);
                if (hr == S_OK) {
                    LPMYPIDL pooi = COfflineFolderEnum::NewPidl(dwBufSize);
                    if (pooi)   {
                        CopyToMyPooe(&ooeBuf, &(pooi->ooe));
                        _GenerateEvent( SHCNE_UPDATEITEM,
                                        (LPITEMIDLIST)pooi,
                                        NULL);
                        COfflineFolderEnum::FreePidl(pooi);
                    } else  {
                        hr = E_OUTOFMEMORY;     //  Ignore it?
                    }
                }
                ZeroMemory(&ooeBuf, sizeof(ooeBuf));
                item.cbSize = sizeof(NOTIFICATIONITEM);
                cItems = 0;
                dwBufSize = 0;

                hr = pEnum->Next(1, &item, &cItems);
            }
            SAFERELEASE(item.pNotification);
            SAFERELEASE(pEnum);
        }

        break;
    }

    SAFERELEASE(pScheduleGroup);
    SAFERELEASE(pNotificationMgr);

    return hrResult;
}

/////////////////////////////////////////////////////////////////////////////
// DeleteScheduleGroup
/////////////////////////////////////////////////////////////////////////////
HRESULT DeleteScheduleGroup
(
    /* [in] */  PNOTIFICATIONCOOKIE pGroupCookie
)
{
//xnotfmgr

    INotificationMgr *  pNotificationMgr = NULL;
    HRESULT             hrResult;

    for (;;)
    {
        if  (
            (pGroupCookie == NULL)
            ||
            (*pGroupCookie == GUID_NULL)
            )
        {
            return E_INVALIDARG;
        }

        if (FAILED(hrResult = GetNotificationMgr(&pNotificationMgr)))
        {
            TraceMsg(TF_ERROR, "DSG: Unable to get NotificationMgr");
            break;
        }

        if (FAILED(hrResult = pNotificationMgr->RevokeScheduleGroup(pGroupCookie,
                                                                    NULL,
                                                                    0)))
        {
            TraceMsg(TF_ERROR, "DSG: RevokeScheduleGroup FAILED");
            break;
        }

        break;
    }

    SAFERELEASE(pNotificationMgr);

    return hrResult;
}

/////////////////////////////////////////////////////////////////////////////
// ScheduleGroupExists
/////////////////////////////////////////////////////////////////////////////
BOOL ScheduleGroupExists
(
    LPCTSTR lpszGroupName
)
{
//xnotfmgr
    INotificationMgr *      pNotificationMgr = NULL;
    IEnumScheduleGroup *    pEnumSchedGroup = NULL;
    BOOL                    bResult = FALSE;

    for (;;)
    {
        if (FAILED(GetNotificationMgr(&pNotificationMgr)))
        {
            TraceMsg(TF_ERROR, "SGE: Unable to get NotificationMgr");
            break;
        }

        if (FAILED(pNotificationMgr->GetEnumScheduleGroup(0, &pEnumSchedGroup)))
        {
            TraceMsg(TF_ERROR, "SGE: Unable to GetEnumScheduleGroup");
            break;
        }

        // Iterate through all the schedule groups
        ULONG               celtFetched;
        IScheduleGroup *    pScheduleGroup = NULL;
        while   (
                SUCCEEDED(pEnumSchedGroup->Next(1,
                                                &pScheduleGroup,
                                                &celtFetched))
                &&
                (celtFetched != 0)
                )
        {
            GROUPINFO info = { 0 };

            ASSERT(pScheduleGroup != NULL);

            // Get the schedule group attributes.
            if  (
                SUCCEEDED(pScheduleGroup->GetAttributes(NULL,
                                                        NULL,
                                                        NULL,
                                                        &info,
                                                        NULL,
                                                        NULL))
                )
            {
                ASSERT(info.cbSize == SIZEOF(GROUPINFO));
                ASSERT(info.pwzGroupname != NULL);

                TCHAR szGroupName[MAX_GROUPNAME_LEN];
                MyOleStrToStrN(szGroupName, MAX_GROUPNAME_LEN, info.pwzGroupname);
                SAFEDELETE(info.pwzGroupname);

                // See if this is the schedule group name we are looking for.
                if (StrCmpI(lpszGroupName, szGroupName) == 0)
                {
                    bResult = TRUE;
                    break;
                }
            }
            else
                TraceMsg(TF_ERROR, "SGE: Unable to GetAttributes");

            SAFERELEASE(pScheduleGroup);
        }

        break;
    }

    SAFERELEASE(pEnumSchedGroup);
    SAFERELEASE(pNotificationMgr);

    return bResult;
}

/////////////////////////////////////////////////////////////////////////////
// SetDlgItemMonth
/////////////////////////////////////////////////////////////////////////////
BOOL SetDlgItemMonth
(
    HWND    hwndDlg,
    int     idCtl,
    WORD    rgfMonths
)
{
    return SetDlgItemInt(   hwndDlg,
                            idCtl,
                            MonthCountFromMonthsFlags(rgfMonths),
                            FALSE);
}

/////////////////////////////////////////////////////////////////////////////
// MonthCountFromMonthsFlags
/////////////////////////////////////////////////////////////////////////////
int MonthCountFromMonthsFlags
(
    WORD rgfMonths
)
{
    for (int iMonths = 0; iMonths < ARRAYSIZE(c_rgwMonthMaps); iMonths++)
    {
        if (c_rgwMonthMaps[iMonths] == rgfMonths)
            break;
    }

    // If we didn't find it, assume every month
    if (iMonths == ARRAYSIZE(c_rgwMonthMaps))
        iMonths = 0;

    return iMonths+1;
}

/////////////////////////////////////////////////////////////////////////////
// DayIndexFromDaysOfTheWeekFlags
/////////////////////////////////////////////////////////////////////////////
int DayIndexFromDaysOfTheWeekFlags
(
    WORD rgfDaysOfTheWeek
)
{
    int idxDay;

    for (idxDay = 0; idxDay < ARRAYSIZE(s_rgwDaysOfTheWeek); idxDay++)
    {
        if (s_rgwDaysOfTheWeek[idxDay] == rgfDaysOfTheWeek)
            break;
    }

    if (idxDay == ARRAYSIZE(s_rgwDaysOfTheWeek))
        idxDay = 0;

    return idxDay;
}

/////////////////////////////////////////////////////////////////////////////
// GetDlgItemMonth
/////////////////////////////////////////////////////////////////////////////
WORD GetDlgItemMonth
(
    HWND    hwndDlg,
    int     idCtl
)
{
    BOOL bTranslated;

    int iVal = GetDlgItemInt(   hwndDlg,
                                idCtl,
                                &bTranslated,
                                FALSE);

    if  (
        bTranslated
        &&
        ((iVal > 0) && (iVal <= ARRAYSIZE(c_rgwMonthMaps)))
        )
    {
        return c_rgwMonthMaps[iVal-1];
    }

    return 0;
}

/////////////////////////////////////////////////////////////////////////////
// SetDlgItemNextUpdate
/////////////////////////////////////////////////////////////////////////////
void SetDlgItemNextUpdate
(
    HWND            hwndDlg,
    int             idCtl,
    SSUIDLGINFO *   pDlgInfo
)
{
//xnotfmgr

    WORD        cDate = 1;
    FILETIME    dateNextUpdate;
    TCHAR       szNextUpdate[128];

    SYSTEMTIME  stBegin;
    GetLocalTime(&stBegin);

    if  (
        (pDlgInfo->pfnGetRunTimes != NULL)
        &&
        SUCCEEDED(pDlgInfo->pfnGetRunTimes( pDlgInfo->ttTaskTrigger,
                                            NULL,
                                            &stBegin,
                                            NULL,
                                            &cDate,
                                            &dateNextUpdate))
        &&
        (cDate != 0)
        )
    {
        FileTimeToSystemTime(&dateNextUpdate, &stBegin);
    }
    else
        GetLocalTime(&stBegin);

    LPTSTR pszNextUpdate = szNextUpdate;
    GetDateFormat(  LOCALE_USER_DEFAULT,
                    DATE_LONGDATE,
                    &stBegin,
                    NULL,
                    pszNextUpdate,
                    ARRAYSIZE(szNextUpdate) / 2);

    pszNextUpdate += lstrlen(pszNextUpdate); *pszNextUpdate++ = TEXT(' ');

    GetTimeFormat(  LOCALE_USER_DEFAULT,
                    TIME_NOSECONDS,
                    &stBegin,
                    NULL,
                    pszNextUpdate,
                    ARRAYSIZE(szNextUpdate) / 2);

    SetDlgItemText(hwndDlg, idCtl, szNextUpdate);
}

/////////////////////////////////////////////////////////////////////////////
// IncTime
/////////////////////////////////////////////////////////////////////////////
DWORD IncTime
(
    DWORD   dwTime,
    int     iIncHours,
    int     iIncMinutes
)
{
    int iMins = (LOWORD(dwTime) * 60) + HIWORD(dwTime);
    int iIncMins = (iIncHours * 60) + iIncMinutes;
    int iDeltaMins = iMins + iIncMins;

    if (iDeltaMins > ((24 * 60) - 1))
        iDeltaMins = iDeltaMins - (24 * 60);
    else if (iDeltaMins < 0)
        iDeltaMins = ((24 * 60) - 1);

    return MAKELONG((iDeltaMins / 60), (iDeltaMins % 60));
}

/////////////////////////////////////////////////////////////////////////////
// DayFromDaysFlags
/////////////////////////////////////////////////////////////////////////////
int DayFromDaysFlags
(
    DWORD rgfDays
)
{
    DWORD   dwMask = 1;
    int     iDay = 1;

    while (dwMask)
    {
        if (rgfDays & dwMask)
            break;

        dwMask <<= 1;
        iDay++;
    }

    if (iDay > 31)
        iDay = 1;

    return iDay;
}

/////////////////////////////////////////////////////////////////////////////
// Schedule group combo box helper functions
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// SchedGroupComboBox_Fill
/////////////////////////////////////////////////////////////////////////////
HRESULT SchedGroupComboBox_Fill
(
    HWND hwndCombo
)
{
//xnotfmgr
    INotificationMgr *      pNotificationMgr = NULL;
    IEnumScheduleGroup *    pEnumSchedGroup = NULL;
    HRESULT                 hrResult;

    ASSERT(hwndCombo != NULL);
    ASSERT(IsWindow(hwndCombo));

    for (;;)
    {
        SchedGroupComboBox_Clear(hwndCombo);

        if (FAILED(hrResult = GetNotificationMgr(&pNotificationMgr)))
        {
            TraceMsg(TF_ERROR, "FCBSG: Unable to get NotificationMgr");
            break;
        }

        if (FAILED(hrResult = pNotificationMgr->GetEnumScheduleGroup(0, &pEnumSchedGroup)))
        {
            TraceMsg(TF_ERROR, "FCBSG: Unable to GetEnumScheduleGroup");
            break;
        }

        // Iterate through all the schedule gropus
        ULONG               celtFetched;
        IScheduleGroup *    pScheduleGroup = NULL;
        while   (
                SUCCEEDED(hrResult = pEnumSchedGroup->Next( 1,
                                                            &pScheduleGroup,
                                                            &celtFetched))
                &&
                (celtFetched != 0)
                )
        {
            GROUPINFO           info = { 0 };
            NOTIFICATIONCOOKIE  cookie = { 0 };

            ASSERT(pScheduleGroup != NULL);

            // Get the schedule group attributes.
            if  (
                SUCCEEDED(pScheduleGroup->GetAttributes(NULL,
                                                        NULL,
                                                        &cookie,
                                                        &info,
                                                        NULL,
                                                        NULL))
                &&
                (cookie != NOTFCOOKIE_SCHEDULE_GROUP_MANUAL)
                )
            {
                ASSERT(info.cbSize == SIZEOF(GROUPINFO));
                ASSERT(info.pwzGroupname != NULL);

                TCHAR szGroupName[MAX_GROUPNAME_LEN];
                MyOleStrToStrN(szGroupName, MAX_GROUPNAME_LEN, info.pwzGroupname);
                SAFEDELETE(info.pwzGroupname);

                EVAL(SUCCEEDED(SchedGroupComboBox_AddGroup( hwndCombo,
                                                            szGroupName,
                                                            &cookie)));
            }
            else
                TraceMsg(TF_ERROR, "FCBSG: Unable to GetAttributes");

            SAFERELEASE(pScheduleGroup);
        }

        break;
    }

    SAFERELEASE(pEnumSchedGroup);
    SAFERELEASE(pNotificationMgr);

    return hrResult;
}

/////////////////////////////////////////////////////////////////////////////
// SchedGroupComboBox_Clear
/////////////////////////////////////////////////////////////////////////////
HRESULT SchedGroupComboBox_Clear
(
    HWND hwndCombo
)
{
//xnotfmgr
    ASSERT(hwndCombo != NULL);
    ASSERT(IsWindow(hwndCombo));

    for (int i = 0; i < ComboBox_GetCount(hwndCombo); i++)
    {
        PNOTIFICATIONCOOKIE pCookie = (PNOTIFICATIONCOOKIE)ComboBox_GetItemData(hwndCombo, i);

        if ((pCookie != NULL) && ((DWORD)pCookie != CB_ERR))
            MemFree(pCookie);
    }

    ComboBox_ResetContent(hwndCombo);

    return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// SchedGroupComboBox_AddGroup
/////////////////////////////////////////////////////////////////////////////
HRESULT SchedGroupComboBox_AddGroup
(
    HWND                hwndCombo,
    LPCTSTR             pszGroupName,
    PNOTIFICATIONCOOKIE pGroupCookie
)
{
//xnotfmgr
    ASSERT(hwndCombo != NULL);
    ASSERT(IsWindow(hwndCombo));
    ASSERT(pszGroupName != NULL);
    ASSERT(pGroupCookie != NULL);

    // Add the schedule group name to the combobox.
    int iIndex = ComboBox_AddString(hwndCombo, pszGroupName);
    if ((iIndex != CB_ERR) && (iIndex != CB_ERRSPACE))
    {
        // Attach the schedule group cookie to the item data.
        PNOTIFICATIONCOOKIE pCookie = (PNOTIFICATIONCOOKIE)MemAlloc(LPTR,
                                                                    SIZEOF(NOTIFICATIONCOOKIE));
        if (pCookie == NULL)
            return E_OUTOFMEMORY;

        *pCookie = *pGroupCookie;
        ComboBox_SetItemData(hwndCombo, iIndex, (DWORD)pCookie);

        return S_OK;
    }
    else

    ASSERT(FALSE);
    return E_FAIL;
}

/////////////////////////////////////////////////////////////////////////////
// SchedGroupComboBox_RemoveGroup
/////////////////////////////////////////////////////////////////////////////
HRESULT SchedGroupComboBox_RemoveGroup
(
    HWND                hwndCombo,
    PNOTIFICATIONCOOKIE pGroupCookie
)
{
//xnotfmgr
    ASSERT(hwndCombo != NULL);
    ASSERT(IsWindow(hwndCombo));
    ASSERT(pGroupCookie != NULL);
    ASSERT(*pGroupCookie != GUID_NULL);

    for (int i = 0 ; i < ComboBox_GetCount(hwndCombo); i++)
    {
        PNOTIFICATIONCOOKIE pCookie = (PNOTIFICATIONCOOKIE)ComboBox_GetItemData(hwndCombo, i);

        ASSERT(pCookie != NULL);

        if (*pCookie == *pGroupCookie)
        {
            MemFree(pCookie);

            EVAL(ComboBox_DeleteString(hwndCombo, i));
            return S_OK;
        }
    }

    ASSERT(FALSE);
    return E_FAIL;
}

/////////////////////////////////////////////////////////////////////////////
// SchedGroupComboBox_SetCurGroup
/////////////////////////////////////////////////////////////////////////////
HRESULT SchedGroupComboBox_SetCurGroup
(
    HWND                hwndCombo,
    PNOTIFICATIONCOOKIE pGroupCookie
)
{
//xnotfmgr
    ASSERT(hwndCombo != NULL);
    ASSERT(IsWindow(hwndCombo));
    if (pGroupCookie == NULL || *pGroupCookie == GUID_NULL)
        return E_INVALIDARG;

    for (int i = 0 ; i < ComboBox_GetCount(hwndCombo); i++)
    {
        PNOTIFICATIONCOOKIE pData = (PNOTIFICATIONCOOKIE)ComboBox_GetItemData(hwndCombo, i);

        if (pData && *pData == *pGroupCookie)
        {
            EVAL(ComboBox_SetCurSel(hwndCombo, i) != -1);
            return S_OK;
        }
    }

    ASSERT(FALSE);
    return E_FAIL;
}

/////////////////////////////////////////////////////////////////////////////
// SchedGroupComboBox_GetCurGroup
/////////////////////////////////////////////////////////////////////////////
HRESULT SchedGroupComboBox_GetCurGroup
(
    HWND                hwndCombo,
    PNOTIFICATIONCOOKIE pGroupCookie
)
{
//xnotfmgr
    ASSERT(hwndCombo != NULL);
    ASSERT(IsWindow(hwndCombo));
    ASSERT(pGroupCookie != NULL);

    int iIndex = ComboBox_GetCurSel(hwndCombo);
    if (iIndex != CB_ERR)
    {
        PNOTIFICATIONCOOKIE pCookie = (PNOTIFICATIONCOOKIE)ComboBox_GetItemData(hwndCombo, iIndex);

        if (pCookie == NULL)
            return E_INVALIDARG;

        *pGroupCookie = *pCookie;

        return S_OK;
    }

    ASSERT(FALSE);
    return E_FAIL;
}


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// wrappers for schedui combobox functions, to fill list and get current selection,
// that are aware of a NULL itemdata field representing the Publisher's Recommended
// Schedule entry (NULL itemdata means no groupCookie means use task trigger).
// 
////////////////////////////////////////////////////////////////////////////////

HRESULT FillScheduleList (HWND hwndCombo, POOEBuf pBuf)
{
    //first, fill with schedule groups
    HRESULT hr = SchedGroupComboBox_Fill (hwndCombo);

    //then, if channel, add "publisher schedule" to list
    if (pBuf->bChannel)
    {
        TCHAR tcBuf[128];

        MLLoadString(IDS_SCHEDULE_PUBLISHER, tcBuf, ARRAYSIZE(tcBuf));
        ComboBox_AddString (hwndCombo, tcBuf);
        //leave itemdata (cookie ptr) NULL
    }

    return hr;
}

HRESULT SetScheduleGroup (HWND hwndCombo, CLSID* pGroupCookie, POOEBuf pBuf)
{
//xnotfmgr
    HRESULT hr;

    if (pBuf->bChannel && (pBuf->fChannelFlags & CHANNEL_AGENT_DYNAMIC_SCHEDULE))
        hr = E_FAIL;    //force to Publisher's Schedule
    else
        hr = SchedGroupComboBox_SetCurGroup (hwndCombo, pGroupCookie);

    if (FAILED (hr))
    {
        if (pBuf->bChannel)
        {
            //we are a channel; look for Publisher's Schedule (null cookie ptr)
            for (int i = 0; i < ComboBox_GetCount (hwndCombo); i++)
            {
                if (!ComboBox_GetItemData(hwndCombo, i))
                {
                    EVAL(ComboBox_SetCurSel(hwndCombo, i) != -1);
                    return S_OK;
                }
            }
            hr = E_FAIL;
        }
    }

    return hr;
}
