#!/usr/bin/tclsh

fconfigure stdout -buffering none
set level 0
proc L {args} {
    global level
    puts [string repeat { } $level]$args
    return
}
proc --> {args} { global level ; L Enter/ {*}$args [info level -1] ; incr level  4 }
proc <-- {args} { global level ; incr level -4 ; L /Exit {*}$args [info level -1] }

proc getstr {fd var} { -->
    upvar 1 $var line
    set cnt [gets $fd line]
    L "gets ($cnt): '$line'"
    <--
    return $cnt
}

proc xyzrequest {s} { -->
    set f [chan create read refchan]
    fconfigure $f -blocking 0
    chan event $f readable [list xyzexec $f $s]
    refchan::output $f "Content-Type: text/html\n"
    refchan::output $f "Line 1"
    refchan::output $f "Line 2"
    refchan::output $f "Line 3"
    refchan::done $f
    <--
}

proc xyzexec {f s} { --> CALLBACK
    if {[eof $f]} {
	# Should not happen - error
    } elseif {[getstr $f line] == -1} {
	# Not a full line available
	L "Not a full line"
	<-- CALLBACK
	return
    } elseif {![string length $line]} {
	# Empty line indicates the end of header information
	L "Empty line"
	<-- CALLBACK
	return
    } elseif {[regexp {^(\S+)\s*:\s*(.+)} $line -> var val]} {
	# Found a valid header line
	L "Header: $var=$val"
	<-- CALLBACK
	return
    }
    L "Invalid output"
    close $f
    L "Connection from webhost($f) closed, $f"
    <-- CALLBACK
    return
}

namespace eval refchan {
    namespace ensemble create -subcommands {
        initialize finalize watch read write
    }

    proc initialize {id mode} { --> HANDLER
        variable chan
        variable buffer

        set chan($id) [dict create watch {} eof 0 event ""]
        set buffer($id) ""

	<-- HANDLER
        return [namespace ensemble configure refchan -subcommands]
    }

    proc watch {id spec} { --> HANDLER
        variable chan
        dict set chan($id) watch $spec
        postevent $id
	<-- HANDLER
    }

    proc read {id count} { --> HANDLER
        variable chan
        variable buffer
        if {[info exists chan($id)]} {
            L STATE = $chan($id)
            if {$buffer($id) eq ""} {
                if {[dict get $chan($id) eof]} {
                    L "=> EOF"
		    <-- HANDLER
                    return ""
                } else {
                    L "=> EAGAIN"
		    <-- HANDLER
                    return -code error EAGAIN
                }
            }
            # Bug in Tcl? Work-around: Only provide one line per request
            set x [string first \n $buffer($id)]
            # if {$x >= 0} {set count [expr {$x + 1}]}
            set rc [string replace $buffer($id) $count end]
            set buffer($id) [string range $buffer($id) $count end]
            L "\t[info level 0] -> '$rc'"
	    <-- HANDLER
            return $rc
        } else {
	    L "\t[info level 0] => EOF"
	    <-- HANDLER
            return ""
        }
    }

    proc finalize id { --> HANDLER
        variable chan
        set after [dict get $chan($id) event]
        after cancel $after
        unset chan($id)
	<-- HANDLER
    }




    # Code to full the channel buffer with data to be read ...

    proc output {id str} { --> FILL
        variable chan
        variable buffer
        if {[info exists chan($id)]} {
            append buffer($id) $str \n
            postevent $id
        }
	<-- FILL
    }

    proc done {id} { --> FILL
        variable chan
        dict set chan($id) eof 1
        postevent $id
	<-- FILL
    }

    proc postevent {id} { --> SIGNAL
        variable chan
        variable buffer
        dict with chan($id) {}
        after cancel $event
        if {"read" in $watch} {
            if {$buffer($id) ne "" || $eof} {
                dict set chan($id) event [after 10 [list refchan::postevent $id]]
		L [list chan postevent $id read]
                chan postevent $id read
            }
        }

	<-- SIGNAL
    }
}

xyzrequest stdout

if {[catch {vwait forever}]} exit
