# -*- tcl -*-
# # ## ### ##### ######## #############
# (C) 2009 Andreas Kupries

# @@ Meta Begin
# Package tcl::chan::events 1.1
# Meta as::author {Andreas Kupries}
# Meta as::copyright 2009
# Meta as::license BSD
# Meta description Support package handling a core
# Meta description aspect of reflected base channels
# Meta description (timer
# Meta description driven file event support). Controls a
# Meta description timer generating the expected read/write
# Meta description events. It is expected that this class
# Meta description is used as either one superclass of the
# Meta description class C for a specific channel, or is
# Meta description mixed into C.
# Meta platform tcl
# Meta require tcl::chan::core
# Meta require TclOO
# Meta require {Tcl 8.5}
# @@ Meta End

# TODO :: set/get accessor methods for the timer delay

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

package require Tcl 8.5 9
if {[catch {package require tcl::oo}]} {
	package require TclOO
}
package require tcl::chan::core

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

oo::class create ::tcl::chan::events {
    superclass ::tcl::chan::core ; # -> initialize, finalize, destructor

    constructor {} {
	array set allowed {
	    read  0
	    write 0
	}
	set requested {}
	set delay     10
	return
    }

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

    method finalize {c} {
	my disallow read write
	next $c
    }

    # Allow/disallow the posting of events based on the
    # events requested by Tcl's IO system, and the mask of
    # events the instance's channel can handle, per all
    # preceding calls of allow and disallow.

    method watch {c requestmask} {
	if {$requestmask eq $requested} return
	set requested $requestmask
	my Update
	return
    }

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

    # Declare that the named events are handled by the
    # channel. This may start a timer to periodically post
    # these events to the instance's channel.

    method allow {args} {
	my Allowance $args yes
	return
    }

    # Declare that the named events are not handled by the
    # channel. This may stop the periodic posting of events
    # to the instance's channel.

    method disallow {args} {
	my Allowance $args no
	return
    }

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

    # Event System State - Timer driven

    variable timer allowed requested posting delay    

    # channel   = The channel to post events to - provided by superclass
    # timer     = Timer controlling the posting.
    # allowed   = Set of events allowed to post.
    # requested = Set of events requested by core.
    # posting   = Set of events we are posting.
    # delay     = Millisec interval between posts.

    # 'allowed' is an Array (event name -> boolean). The
    # value is true if the named event is allowed to be
    # posted.

    # Common code used by both allow and disallow to enter
    # the state change.

    method Allowance {events enable} {
	set changed no
	foreach event $events {
	    if {$allowed($event) == $enable} continue
	    set allowed($event) $enable
	    set changed yes
	}
	if {!$changed} return
	my Update
	return
    }

    # Merge the current event allowance and the set of
    # requested events into one datum, the set of events to
    # post. From that then derive whether we need a timer or
    # not and act accordingly.

    method Update {} {
	catch { after cancel $timer }
	set posting {}
	foreach event $requested {
	    if {!$allowed($event)} continue
	    lappend posting $event
	}
	if {[llength $posting]} {
	    set timer [after $delay \
			   [namespace code [list my Post]]]
	} else {
	    catch { unset timer }
	}
	return
    }

    # Post the current set of events, then reschedule to
    # make this periodic.

    method Post {} {
	my variable channel
	set timer [after $delay \
		       [namespace code [list my Post]]]
	chan postevent $channel $posting
	return
    }
}

# # ## ### #####
package provide tcl::chan::events 1.1
return
