# A sample originally provided by Richard Bell, and modified by Mark Hammond.

# This sample demonstrates how to use COM events in a free-threaded world.
# In this world, there is no need to marshall calls across threads, so
# no message loops are needed at all.  This means regular cross-thread
# sychronization can be used.  In this sample we just wait on win32 event
# objects.

# See also ieEventsApartmentThreaded.py for how to do this in an
# aparment-threaded world, where thread-marshalling complicates things.

# NOTE: This example uses Internet Explorer, but it should not be considerd
# a "best-practices" for writing against IE events, but for working with
# events in general. For example:
# * The first OnDocumentComplete event is not a reliable indicator that the
#   URL has completed loading
# * As we are demonstrating the most efficient way of handling events, when
#   running this sample you will see an IE Windows briefly appear, but
#   vanish without ever being repainted.

import sys

sys.coinit_flags = 0  # specify free threading

import os
import win32api
import win32event
import win32com.client
import pythoncom
import time

# The print statements indicate that COM has actually started another thread
# and will deliver the events to that thread (ie, the events do not actually
# fire on our main thread.
class ExplorerEvents:
    def __init__(self):
        # We reuse this event for all events.
        self.event = win32event.CreateEvent(None, 0, 0, None)

    def OnDocumentComplete(self, pDisp=pythoncom.Empty, URL=pythoncom.Empty):
        #
        # Caution:  Since the main thread and events thread(s) are different
        # it may be necessary to serialize access to shared data.  Because
        # this is a simple test case, that is not required here.  Your
        # situation may be different.   Caveat programmer.
        #
        thread = win32api.GetCurrentThreadId()
        print("OnDocumentComplete event processed on thread %d" % thread)
        # Set the event our main thread is waiting on.
        win32event.SetEvent(self.event)

    def OnQuit(self):
        thread = win32api.GetCurrentThreadId()
        print("OnQuit event processed on thread %d" % thread)
        win32event.SetEvent(self.event)


def TestExplorerEvents():
    iexplore = win32com.client.DispatchWithEvents(
        "InternetExplorer.Application", ExplorerEvents
    )

    thread = win32api.GetCurrentThreadId()
    print("TestExplorerEvents created IE object on thread %d" % thread)

    iexplore.Visible = 1
    try:
        iexplore.Navigate(win32api.GetFullPathName("..\\readme.html"))
    except pythoncom.com_error as details:
        print("Warning - could not open the test HTML file", details)

    # In this free-threaded example, we can simply wait until an event has
    # been set - we will give it 2 seconds before giving up.
    rc = win32event.WaitForSingleObject(iexplore.event, 2000)
    if rc != win32event.WAIT_OBJECT_0:
        print("Document load event FAILED to fire!!!")

    iexplore.Quit()
    # Now we can do the same thing to wait for exit!
    # Although Quit generates events, in this free-threaded world we
    # do *not* need to run any message pumps.

    rc = win32event.WaitForSingleObject(iexplore.event, 2000)
    if rc != win32event.WAIT_OBJECT_0:
        print("OnQuit event FAILED to fire!!!")

    iexplore = None
    print("Finished the IE event sample!")


if __name__ == "__main__":
    TestExplorerEvents()
