# This sample wraps several custom COM interfaces, for which no
# typelibrary is present.  The interfaces are declared in the MSVC
# MSTASK.H include file.
#
# generated by 'xml2py'
# flags 'mstask.xml -r ITask.* -m comtypes -o mstask.py'
#
# and then:
# - reordered manually
# - removed the HWND and HWND__ definitions, replaced with 'HWND = c_void_p'
#
# - filled in _iid_ entries, and completed the idl attributes and
# parameter names in the COMMETHOD definitions by looking into the
# header file
#
# - fixed the LPCWSTR and LPWSTR definitions
#

# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# PROBLEMS:
#
# memory leaks: Must call CoTaskMemFree in the methods returning
# LPWSTR items. See MSDN docs.
# Should this be solved by using subclasses of c_wchar_p, with a customized
# _from_outarg_ slot?
#
# refcount leaks!
#

from ctypes import *
from comtypes import IUnknown
from comtypes import HRESULT
from comtypes import IID
from comtypes import STDMETHOD, GUID, COMMETHOD
from comtypes import DWORD
from comtypes import defaultvalue

# The code generation emitted
#   WCHAR = c_wchar
#   LPCWSTR = POINTER(WCHAR)
#   LPWSTR = POINTER(WCHAR)
# but we redefine because LPCWSTR and LPWSTR are always zero-terminated strings here.

LPCWSTR = c_wchar_p #POINTER(WCHAR)
LPWSTR = c_wchar_p #POINTER(WCHAR)
BYTE = c_ubyte
HWND = c_void_p # XXX
WORD = c_ushort

################################################################

# SYSTEMTIME should probably move elsewhere - wintypes.py ?
class _SYSTEMTIME(Structure):
    _fields_ = [
        ('wYear', WORD),
        ('wMonth', WORD),
        ('wDayOfWeek', WORD),
        ('wDay', WORD),
        ('wHour', WORD),
        ('wMinute', WORD),
        ('wSecond', WORD),
        ('wMilliseconds', WORD),
    ]
    def __repr__(self):
        return "SystemTime(%s/%s/%s %d:%02d:%2.3f)" % \
               (self.wYear, self.wMonth, self.wDay,
                self.wHour, self.wMinute, self.wSecond + self.wMilliseconds / 1000.)
LPSYSTEMTIME = POINTER(_SYSTEMTIME)
SYSTEMTIME = _SYSTEMTIME

TASK_FLAG_INTERACTIVE                  = (0x1)
TASK_FLAG_DELETE_WHEN_DONE             = (0x2)
TASK_FLAG_DISABLED                     = (0x4)
TASK_FLAG_START_ONLY_IF_IDLE           = (0x10)
TASK_FLAG_KILL_ON_IDLE_END             = (0x20)
TASK_FLAG_DONT_START_IF_ON_BATTERIES   = (0x40)
TASK_FLAG_KILL_IF_GOING_ON_BATTERIES   = (0x80)
TASK_FLAG_RUN_ONLY_IF_DOCKED           = (0x100)
TASK_FLAG_HIDDEN                       = (0x200)
TASK_FLAG_RUN_IF_CONNECTED_TO_INTERNET = (0x400)
TASK_FLAG_RESTART_ON_IDLE_RESUME       = (0x800)
TASK_FLAG_SYSTEM_REQUIRED              = (0x1000)
TASK_FLAG_RUN_ONLY_IF_LOGGED_ON        = (0x2000)

_TASK_TRIGGER_TYPE = c_int # enum
TASK_TIME_TRIGGER_ONCE = 0
TASK_TIME_TRIGGER_DAILY = 1
TASK_TIME_TRIGGER_WEEKLY = 2
TASK_TIME_TRIGGER_MONTHLYDATE = 3
TASK_TIME_TRIGGER_MONTHLYDOW = 4
TASK_EVENT_TRIGGER_ON_IDLE = 5
TASK_EVENT_TRIGGER_AT_SYSTEMSTART = 6
TASK_EVENT_TRIGGER_AT_LOGON = 7
TASK_TRIGGER_TYPE = _TASK_TRIGGER_TYPE

class _TASK_TRIGGER(Structure):
    pass
PTASK_TRIGGER = POINTER(_TASK_TRIGGER)

class _DAILY(Structure):
    _fields_ = [('DaysInterval', WORD)]
DAILY = _DAILY

class _WEEKLY(Structure):
    _fields_ = [
        ('WeeksInterval', WORD),
        ('rgfDaysOfTheWeek', WORD),
        ]
WEEKLY = _WEEKLY

class _MONTHLYDATE(Structure):
    _fields_ = [
        ('rgfDays', DWORD),
        ('rgfMonths', WORD),
        ]
MONTHLYDATE = _MONTHLYDATE

class _MONTHLYDOW(Structure):
    _fields_ = [
        ('wWhichWeek', WORD),
        ('rgfDaysOfTheWeek', WORD),
        ('rgfMonths', WORD),
        ]
MONTHLYDOW = _MONTHLYDOW

class _TRIGGER_TYPE_UNION(Union):
    _fields_ = [
        ('Daily', DAILY),
        ('Weekly', WEEKLY),
        ('MonthlyDate', MONTHLYDATE),
        ('MonthlyDOW', MONTHLYDOW),
        ]
TRIGGER_TYPE_UNION = _TRIGGER_TYPE_UNION

_TASK_TRIGGER._fields_ = [
    ('cbTriggerSize', WORD),
    ('Reserved1', WORD),
    ('wBeginYear', WORD),
    ('wBeginMonth', WORD),
    ('wBeginDay', WORD),
    ('wEndYear', WORD),
    ('wEndMonth', WORD),
    ('wEndDay', WORD),
    ('wStartHour', WORD),
    ('wStartMinute', WORD),
    ('MinutesDuration', DWORD),
    ('MinutesInterval', DWORD),
    ('rgFlags', DWORD),
    ('TriggerType', TASK_TRIGGER_TYPE),
    ('Type', TRIGGER_TYPE_UNION),
    ('Reserved2', WORD),
    ('wRandomMinutesInterval', WORD),
]

################################################################

CLSID_CTaskScheduler = GUID("{148BD52A-A2AB-11CE-B11F-00AA00530503}")
class ITaskScheduler(IUnknown):
    _iid_ = GUID('{148BD527-A2AB-11CE-B11F-00AA00530503}')

    def __iter__(self):
        return self.Enum()

    def NewWorkItem(self, name):
        task = self._NewWorkItem(name, byref(CLSID_CTask), byref(ITask._iid_))
        return task.QueryInterface(ITask)

class IScheduledWorkItem(IUnknown):
    _iid_ = GUID('{a6b952f0-a4b1-11d0-997d-00aa006887ec}')

class ITaskTrigger(IUnknown):
    _iid_ = GUID('{148BD52B-A2AB-11CE-B11F-00AA00530503}')


class IEnumWorkItems(IUnknown):
    _iid_ = GUID('{148BD528-A2AB-11CE-B11F-00AA00530503}')

    def __iter__(self):
        return self

    def next(self):
         arr, fetched = self.Next(1)
         if fetched == 0:
             raise StopIteration
         result = arr[0]
##         windll.ole32.CoTaskMemFree(arr)
         return result

CLSID_CTask = GUID("{148BD520-A2AB-11CE-B11F-00AA00530503}")
class ITask(IScheduledWorkItem):
    _iid_ = GUID('{148BD524-A2AB-11CE-B11F-00AA00530503}')

################################################################

ITaskScheduler._methods_ = [
    COMMETHOD([], HRESULT, 'SetTargetComputer',
               ( ["in"], LPCWSTR )
               ),
    COMMETHOD([], HRESULT, 'GetTargetComputer',
               ( ["out"], POINTER(LPWSTR) ),
               ),
    COMMETHOD([], HRESULT, 'Enum',
               ( ["out"], POINTER(POINTER(IEnumWorkItems)), "ppEnumWorkItems" ),
               ),
    COMMETHOD([], HRESULT, 'Activate',
               ( ["in"], LPCWSTR, "pwszName" ),
               ( ["in"], POINTER(IID), "riid" ),
               ( ["out"], POINTER(POINTER(IUnknown)), "ppUnk" ),
               ),
    COMMETHOD([], HRESULT, 'Delete',
               ( ["in"], LPCWSTR, "pwszName" ),
               ),
    COMMETHOD([], HRESULT, 'NewWorkItem',
               ( ["in"], LPCWSTR, "pwszTaskName" ),
               ( ["in"], POINTER(IID), "rclsid"),
               ( ["in"], POINTER(IID), "riid"),
               ( ["out"], POINTER(POINTER(IUnknown)), "ppUnk"),
               ),
    COMMETHOD([], HRESULT, 'AddWorkItem',
               ( ["in"], LPCWSTR, "pwszTaskName" ),
               ( ["in"], POINTER(IScheduledWorkItem), "pWorkItem" ),
               ),
    COMMETHOD([], HRESULT, 'IsOfType',
               ( ["in"], LPCWSTR, "pwszName" ),
               ( ["in"], POINTER(IID), "riid"),
               ),
]

ITaskTrigger._methods_ = [
# C:/PROGRA~1/MICROS~3.NET/Vc7/PLATFO~1/Include/mstask.h 228
    # C:/PROGRA~1/MICROS~3.NET/Vc7/PLATFO~1/Include/mstask.h 231
    COMMETHOD([], HRESULT, 'SetTrigger',
               ( [], PTASK_TRIGGER ),
             ),
    # C:/PROGRA~1/MICROS~3.NET/Vc7/PLATFO~1/Include/mstask.h 234
    COMMETHOD([], HRESULT, 'GetTrigger',
               ( [], PTASK_TRIGGER ),
             ),
    # C:/PROGRA~1/MICROS~3.NET/Vc7/PLATFO~1/Include/mstask.h 237
    COMMETHOD([], HRESULT, 'GetTriggerString',
               ( [], POINTER(LPWSTR) ),
             ),
]

IEnumWorkItems._methods_ = [
    COMMETHOD([], HRESULT, 'Next',
              ( ["in", defaultvalue(1)], DWORD, "celt"),
              ( ["out"], POINTER(POINTER(LPWSTR)), "rgpwszNames" ),
              ( ["out"], POINTER(DWORD), "pceltFetched" ),
              ),
    COMMETHOD([], HRESULT, 'Skip',
              ( ["in"], DWORD )),
    COMMETHOD([], HRESULT, 'Reset'),
    COMMETHOD([], HRESULT, 'Clone',
              ( ["out"], POINTER(POINTER(IEnumWorkItems)) ))
]

IScheduledWorkItem._methods_ = [
    COMMETHOD([], HRESULT, 'CreateTrigger',
               ( ["out"], POINTER(WORD) ),
               ( ["out"], POINTER(POINTER(ITaskTrigger)) )),
    COMMETHOD([], HRESULT, 'DeleteTrigger',
               ( ["in"], WORD )),
    COMMETHOD([], HRESULT, 'GetTriggerCount',
               ( ["out"], POINTER(WORD), "pwCount")),
    COMMETHOD([], HRESULT, 'GetTrigger',
               ( ["in"], WORD ),
               ( ["out"], POINTER(POINTER(ITaskTrigger)) )),
    COMMETHOD([], HRESULT, 'GetTriggerString',
              ( ["in"], WORD ),
              ( ["out"], POINTER(LPWSTR) )),
    # C:/PROGRA~1/MICROS~3.NET/Vc7/PLATFO~1/Include/mstask.h 397
    COMMETHOD([], HRESULT, 'GetRunTimes',
               ( ["in"], LPSYSTEMTIME ),
               ( ["in"], LPSYSTEMTIME ),
               ( ["out", "in"], POINTER(WORD) ),
               ( ["out"], POINTER(LPSYSTEMTIME) ),
             ),
    # C:/PROGRA~1/MICROS~3.NET/Vc7/PLATFO~1/Include/mstask.h 400
    # Hm. The docs say [out], but the include file says [in][out].
    COMMETHOD([], HRESULT, 'GetNextRunTime',
               ( ["out"], POINTER(SYSTEMTIME) ),
             ),
    # C:/PROGRA~1/MICROS~3.NET/Vc7/PLATFO~1/Include/mstask.h 404
    COMMETHOD([], HRESULT, 'SetIdleWait',
               ( [], WORD ),
               ( [], WORD ),
             ),
    # C:/PROGRA~1/MICROS~3.NET/Vc7/PLATFO~1/Include/mstask.h 408
    COMMETHOD([], HRESULT, 'GetIdleWait',
               ( [], POINTER(WORD) ),
               ( [], POINTER(WORD) ),
             ),
    # C:/PROGRA~1/MICROS~3.NET/Vc7/PLATFO~1/Include/mstask.h 410
    COMMETHOD([], HRESULT, 'Run',
             ),
    # C:/PROGRA~1/MICROS~3.NET/Vc7/PLATFO~1/Include/mstask.h 412
    COMMETHOD([], HRESULT, 'Terminate',
             ),

    COMMETHOD([], HRESULT, 'EditWorkItem',
               ( ["in", defaultvalue(None)], HWND, 'hParent' ),
               ( ["in", defaultvalue(0)], DWORD, 'dwReserved' )),
    # C:/PROGRA~1/MICROS~3.NET/Vc7/PLATFO~1/Include/mstask.h 419
    COMMETHOD([], HRESULT, 'GetMostRecentRunTime',
               ( [], POINTER(SYSTEMTIME) ),
             ),
    # C:/PROGRA~1/MICROS~3.NET/Vc7/PLATFO~1/Include/mstask.h 422
    COMMETHOD([], HRESULT, 'GetStatus',
               ( [], POINTER(HRESULT) ),
             ),
    # C:/PROGRA~1/MICROS~3.NET/Vc7/PLATFO~1/Include/mstask.h 425
    COMMETHOD([], HRESULT, 'GetExitCode',
               ( [], POINTER(DWORD) ),
             ),
    # C:/PROGRA~1/MICROS~3.NET/Vc7/PLATFO~1/Include/mstask.h 428
    COMMETHOD([], HRESULT, 'SetComment',
               ( [], LPCWSTR ),
             ),
    # C:/PROGRA~1/MICROS~3.NET/Vc7/PLATFO~1/Include/mstask.h 431
    COMMETHOD([], HRESULT, 'GetComment',
               ( [], POINTER(LPWSTR) ),
             ),
    # C:/PROGRA~1/MICROS~3.NET/Vc7/PLATFO~1/Include/mstask.h 434
    COMMETHOD([], HRESULT, 'SetCreator',
               ( [], LPCWSTR ),
             ),
    # C:/PROGRA~1/MICROS~3.NET/Vc7/PLATFO~1/Include/mstask.h 437
    COMMETHOD([], HRESULT, 'GetCreator',
               ( [], POINTER(LPWSTR) ),
             ),
    # C:/PROGRA~1/MICROS~3.NET/Vc7/PLATFO~1/Include/mstask.h 441
    COMMETHOD([], HRESULT, 'SetWorkItemData',
               ( [], WORD ),
               ( [], POINTER(BYTE) ),
             ),
    # C:/PROGRA~1/MICROS~3.NET/Vc7/PLATFO~1/Include/mstask.h 445
    COMMETHOD([], HRESULT, 'GetWorkItemData',
               ( [], POINTER(WORD) ),
               ( [], POINTER(POINTER(BYTE)) ),
             ),
    # C:/PROGRA~1/MICROS~3.NET/Vc7/PLATFO~1/Include/mstask.h 448
    COMMETHOD([], HRESULT, 'SetErrorRetryCount',
               ( [], WORD ),
             ),
    # C:/PROGRA~1/MICROS~3.NET/Vc7/PLATFO~1/Include/mstask.h 451
    COMMETHOD([], HRESULT, 'GetErrorRetryCount',
               ( [], POINTER(WORD) ),
             ),
    # C:/PROGRA~1/MICROS~3.NET/Vc7/PLATFO~1/Include/mstask.h 454
    COMMETHOD([], HRESULT, 'SetErrorRetryInterval',
               ( [], WORD ),
             ),
    # C:/PROGRA~1/MICROS~3.NET/Vc7/PLATFO~1/Include/mstask.h 457
    COMMETHOD([], HRESULT, 'GetErrorRetryInterval',
               ( ["out"], POINTER(WORD) )),
    COMMETHOD([], HRESULT, 'SetFlags',
               ( ["in"], DWORD )),
    COMMETHOD([], HRESULT, 'GetFlags',
               ( ["out"], POINTER(DWORD) )),
    COMMETHOD([], HRESULT, 'SetAccountInformation',
               ( ["in"], LPCWSTR, 'pwszAccountName' ),
               ( ["in"], LPCWSTR, 'pwszPassword' )),
    COMMETHOD([], HRESULT, 'GetAccountInformation',
               ( ["out"], POINTER(LPWSTR), "ppwszAccountName" )),
]

# THIS CODE MUST BE EXECUTED AFTER IScheduledWorkItem._methods_ = ... !!!
ITask._methods_ = [
    COMMETHOD([], HRESULT, 'SetApplicationName',
               ( ["in"], LPCWSTR )),
    COMMETHOD([], HRESULT, 'GetApplicationName',
               ( ["out"], POINTER(LPWSTR) )),
    COMMETHOD([], HRESULT, 'SetParameters',
               ( ["in"], LPCWSTR )),
    COMMETHOD([], HRESULT, 'GetParameters',
               ( ["out"], POINTER(LPWSTR) )),
    COMMETHOD([], HRESULT, 'SetWorkingDirectory',
               ( ["int"], LPCWSTR )),
    COMMETHOD([], HRESULT, 'GetWorkingDirectory',
               ( ["out"], POINTER(LPWSTR) )),
    COMMETHOD([], HRESULT, 'SetPriority',
               ( ["in"], DWORD )),
    COMMETHOD([], HRESULT, 'GetPriority',
               ( ["out"], POINTER(DWORD) )),
    COMMETHOD([], HRESULT, 'SetTaskFlags',
               ( ["in"], DWORD )),
    COMMETHOD([], HRESULT, 'GetTaskFlags',
               ( ["out"], POINTER(DWORD) )),
    COMMETHOD([], HRESULT, 'SetMaxRunTime',
               ( ["in"], DWORD )),
    COMMETHOD([], HRESULT, 'GetMaxRunTime',
               ( ["out"], POINTER(DWORD) ))
]
# create properties form Get/Set methods
ITask.ApplicationName = property(ITask.GetApplicationName, ITask.SetApplicationName)
ITask.Parameters = property(ITask.GetParameters, ITask.SetParameters)
ITask.WorkingDirectory = property(ITask.GetWorkingDirectory, ITask.SetWorkingDirectory)
ITask.Priority = property(ITask.GetPriority, ITask.SetPriority)
ITaskTaskFlags = property(ITask.GetTaskFlags, ITask.SetTaskFlags)
ITask.MaxRunTime = property(ITask.GetMaxRunTime, ITask.SetMaxRunTime)

################################################################

if __name__ == "__main__":
    from comtypes import CoCreateInstance
    scheduler = CoCreateInstance(CLSID_CTaskScheduler, ITaskScheduler)

    for taskname in scheduler: # calls Enum automatically
        print "%s:" % taskname,
        task = scheduler.Activate(taskname, byref(ITask._iid_))
        task = task.QueryInterface(ITask)
        print (task.Parameters, task.MaxRunTime, task.ApplicationName)
        print "PRI", (task.Priority, task.WorkingDirectory)
##        print task.GetErrorRetryInterval()
        try:
            print "Account Info", task.GetAccountInformation()
        except WindowsError:
            print "Nothing"
##        print task.EditWorkItem(None, 0)
        next = task.GetNextRunTime()
        print task.GetRunTimes(byref(next), None, 3)
##        t = SYSTEMTIME()
##        t.wYear = 2000
##        t.wMonth = 1
##        t.wDay = 1
##        t.wMinute = 1
##        t.wSecond = 1
##        t = byref(t)
##        for i in range(20):
##            idx, t = task.GetRunTimes(t, None, 2)
##            print idx, t[1].dump()
##            t = pointer(t[1])

        for i in range(task.GetTriggerCount()):
            try:
                print i, task.GetTriggerString(i).encode("mbcs")
            except Exception:
                import traceback
                traceback.print_exc()

    print scheduler.NewWorkItem("blahblah")

##    print scheduler.Enum()
##    print scheduler.Enum().Clone()

##    for i in range (50):
##        for item in scheduler:
##            pass # leaks 10 refs per loop
