# -*- tcl -*-
# ### ### ### ######### ######### #########
##
# Transfer class. Sending of data.
##
# Utilizes data source and connect components to handle the
# general/common parts.

# ### ### ### ######### ######### #########
## Requirements

package require snit
package require transfer::data::source ; # Data source
package require transfer::connect      ; # Connection startup

# ### ### ### ######### ######### #########
## Implementation

snit::type ::transfer::transmitter {

    # ### ### ### ######### ######### #########
    ## Convenient fire and forget file/channel transmission operations.

    typemethod {stream channel} {chan host port args} {
	# Select stream configuration ( host => active, otherwise
	# passive)
	if {$host eq {}} {
	    set cmd [linsert $args 0 $type %AUTO% \
			 -channel $chan -port $port \
			 -mode passive -translation binary]
	} else {
	    set cmd [linsert $args 0 $type %AUTO% \
			 -channel $chan -host $host -port $port \
			 -mode active -translation binary]
	}

	# Create a transient transmitter controller, and wrap our own
	# internal completion handling around the user supplied
	# callback.

	set transmitter [eval $cmd]
	$transmitter configure \
	    -command [mytypemethod Done \
			  $chan [$transmitter cget -command]]

	# Begin transmission (or wait for other side to connect).
	return [$transmitter start]
    }

    typemethod {stream file} {file host port args} {
	set chan [open $file r]
	fconfigure $chan -translation binary

	return [eval [linsert $args 0 $type stream channel $chan $host $port]]
    }

    typemethod Done {chan command transmitter n {err {}}} {
	close $chan
	$transmitter destroy

	if {![llength $command]} return

	after 0 [linsert $command end $n $err]
	return
    }

    # ### ### ### ######### ######### #########
    ## API

    ## Data source sub component

    delegate option -string   to mysource
    delegate option -channel  to mysource
    delegate option -file     to mysource
    delegate option -variable to mysource
    delegate option -size     to mysource
    delegate option -progress to mysource

    ## Connection management sub component

    delegate option -host        to myconnect
    delegate option -port        to myconnect
    delegate option -mode        to myconnect
    delegate option -translation to myconnect
    delegate option -encoding    to myconnect
    delegate option -eofchar     to myconnect
    delegate option -socketcmd   to myconnect

    ## Transmitter configuration, and API

    option -command   -default {}
    option -blocksize -default 1024 -type {snit::integer -min 1}

    constructor {args} {}

    method start {} {}
    method busy  {} {}

    # ### ### ### ######### ######### #########
    ## Implementation

    constructor {args} {
	set mybusy 0
	install mysource  using ::transfer::data::source ${selfns}::source
	install myconnect using ::transfer::connect      ${selfns}::conn
	$self configurelist $args
	return
    }

    method start {} {
	if {$mybusy} {
	    return -code error "Object is busy"
	}

	if {![$mysource valid msg]} {
	    return -code error $msg
	}

	if {$options(-command) eq ""} {
	    return -code error "Completion callback is missing"
	}

	set mybusy 1
	return [$myconnect connect [mymethod Begin]]
    }

    method busy {} {
	return $mybusy
    }

    # ### ### ### ######### ######### #########
    ## Internal helper commands.

    method Begin {__ sock} {
	# __ <=> myconnect
	$mysource transmit $sock \
		$options(-blocksize) \
		[mymethod Done $sock]
	return
    }

    method Done {sock args} {
	# args is either (n),
	#             or (n errormessage)

	set mybusy 0
	close $sock
	$self Complete $args
	return
    }

    method Complete {arguments} {
	# 8.5: {*}$options(-command) $self {*}$arguments
	set     cmd $options(-command)
	lappend cmd $self
	foreach a $arguments {lappend cmd $a}

	uplevel #0 $cmd
	return
    }

    # ### ### ### ######### ######### #########
    ## Data structures

    component mysource    ; # Data source providing the bytes to transfer
    component myconnect   ; # Connector controlling where to the data transfered to.
    variable  mybusy    0 ; # Transfer status.

    ##
    # ### ### ### ######### ######### #########
}

# ### ### ### ######### ######### #########
## Ready

package provide transfer::transmitter 0.3
