#  Copyright (C) 1999-2021
#  Smithsonian Astrophysical Observatory, Cambridge, MA, USA
#  For conditions of distribution and use, see copyright notice in "copyright"

package provide DS9 1.0

# used by Analysis and SAMP (env(SAMP_HUB) and ds9.set))
proc GetFileURL {url fname} {
    upvar $fname fn

    ParseURL $url rr
    switch -- $rr(scheme) {
	ftp {GetFileFTP $rr(authority) $rr(path) $fn}
	file {set fn $rr(path)}
	http -
	https -
	default {GetFileHTTP $url $fn}
    }
}

proc GetFileFTP {host path fn} {
    global debug

    set ftp [ftp::Open $host {ftp} {-ds9@} -mode passive]
    if {$ftp > -1} {
	set ftp::VERBOSE $debug(tcl,ftp)
	set "ftp::ftp${ftp}(Output)" FTPLog
	ftp::Type $ftp binary
	ftp::Get $ftp $path $fn
	ftp::Close $ftp
    }
}

proc GetFileHTTP {url fn} {
    global ihttp

    # this is such a freaking kludge
    ParseURL $url foo
    if {$foo(authority) == {cda.harvard.edu}} {
	set ka 1
    } else {
	set ka 0
    }

    set ch [open $fn w]
    if {[catch {http::geturl $url \
		    -channel $ch \
		    -keepalive $ka \
		    -binary 1 \
    		    -timeout $ihttp(timeout) \
		    -headers "[ProxyHTTP]"} token]} {
	catch {close $ch}

	Error "[msgcat::mc {Unable to locate URL}] $url"
	return
    }
    global errorInfo
    set errorInfo {}

    close $ch

    if {[info exists token]} {
	if {$foo(authority) == {cda.harvard.edu}} {
	    upvar 0 $token state
	    http::CloseSocket $state(sock) $token
	}

	HTTPLog $token
	http::cleanup $token
    }
}

# gets fits file via url and loads
# sync with redirection
# used by command line, SAMP (load.image.fits/load.table.fits), SIA, OpenURLFITS
proc LoadURLFits {url layer mode multi} {
    if {[string length $url] == 0} {
	return
    }

    ParseURL $url r
    switch -- $r(scheme) {
	ftp {LoadURLFitsFTP $r(authority) $r(path) $layer $mode $multi}
	file {LoadURLFitsFile $r(path) $layer $mode $multi}
	http -
	https -
	default {LoadURLFitsHTTP $url $layer $mode $multi}
    }
}

proc LoadURLFitsFTP {host path layer mode multi} {
    global loadParam
    global ds9
    global debug

    set ftp [ftp::Open $host "ftp" "-ds9@" -mode passive]
    if {$ftp > -1} {
	set fn [tmpnam [file extension $path]]
	set ftp::VERBOSE $debug(tcl,ftp)
	set "ftp::ftp${ftp}(Output)" FTPLog
	ftp::Type $ftp binary
	if {[ftp::Get $ftp $path $fn]} {
	    LoadURLFitsFile $fn $layer $mode $multi
	}

	ftp::Close $ftp

	if {[file exists $fn]} {
	    catch {file delete -force $fn}
	}
    }
}

proc LoadURLFitsFile {fn layer mode multi} {
    global loadParam

    # alloc it because we can't assume it will last
    set loadParam(file,type) fits
    set loadParam(file,mode) $mode
    set loadParam(load,type) allocgz
    set loadParam(file,name) $fn
    set loadParam(file,fn) $loadParam(file,name)
    set loadParam(load,layer) $layer

    if {$multi} {
	MultiLoad $layer $mode
    }
    ProcessLoad
}

proc LoadURLFitsHTTP {url layer mode multi} {
    global ds9
    global ihttp

    ParseURL $url r
    set fn [tmpnam [file extension $r(path)]]

    set ch [open $fn w]
    if {[catch {set token [http::geturl $url \
			       -channel $ch \
			       -binary 1 \
			       -timeout $ihttp(timeout) \
			       -headers "[ProxyHTTP]"]}]} {
	catch {close $ch}

	Error "[msgcat::mc {Unable to locate URL}] $url"
	return
    }
    global errorInfo
    set errorInfo {}
    
    close $ch

    upvar #0 $token t

    # Code
    set code [http::ncode $token]

    # Meta
    set meta $t(meta)

    # Mime-type
    # we want to strip and extra info after ';'
    regexp -nocase {([^;])*} $t(type) mime

    # Content-Encoding
    set encoding {}
    foreach {name value} $meta {
	if {[regexp -nocase ^content-encoding $name]} {
	    switch -- [string tolower $value] {
		compress -
		bzip2 {set encoding bzip2}
		Z {set encoding compress}
		pack -
		z {set encoding pack}
		default {}
	    }
	}
    }

    HTTPLog $token
    # Result?
    switch -- $code {
	200 -
	203 {}

	201 -
	300 -
	301 -
	302 -
	303 -
	305 -
	307 {
	    foreach {name value} $meta {
		if {[regexp -nocase ^location$ $name]} {
		    global debug
		    if {$debug(tcl,http)} {
			puts stderr "LoadURLFitsHTTP redirect $code to $value"
		    }
		    # clean up and resubmit
		    http::cleanup $token
		    unset token

		    if {[file exists $fn]} {
			catch {file delete -force $fn}
		    }
		    set url $value
		    LoadURLFitsHTTP $url $layer $mode $multi
		    return
		}
	    }
	}

	default {
	    Error "HTTP [msgcat::mc {Error}] $code"
	    catch {http::reset $token}
	    return
	}
    }

    http::cleanup $token

    global debug
    if {$debug(tcl,http)} {
	puts stderr "LoadURLFitsHTTP: fn $fn : code $code : meta $meta : mime $mime : encoding $encoding"
    }

    switch -- [string tolower $mime] {
	"text/plain" -
	"text/html" -
	"application/octet-stream" {
	    # it never fails, someone can't get there mime types correct. 
	    # Override the mime type based on path
	    switch -- [file extension $fn] {
		.bz2 {set var(encoding) bzip2}
		.Z {set var(encoding) compress}
		.z {set var(encoding) pack}
	    }
	}

	"image/fits" -
	"application/fits" {}

	"application/fits-image" -
	"application/fits-table" -
	"application/fits-group" {}

	"image/x-fits" -
	"binary/x-fits" -
	"application/x-fits" {}

	"image/x-gfits" -
	"binary/x-gfits" -
	"image/gz-fits" -
	"application/x-gzip" -
	"display/gz-fits" {}

	"image/fits-hcompress" -
	"image/x-fits-h" {}

	"image/bz2-fits" -
	"display/bz2-fits" {set encoding bzip2}

	"image/x-cfits" -
	"binary/x-cfits" {set encoding compress}

	"image/x-zfits" -
	"binary/x-zfits" {set encoding pack}

	default {
	    Error "[msgcat::mc {File not Found or Unable to load FITS data MIME type}] $mime"
	    return
	}
    }

    # alloc it because we are going to delete it after load
    global loadParam

    set loadParam(file,type) fits
    set loadParam(file,mode) $mode
    set loadParam(load,type) allocgz
    set loadParam(file,name) $fn
    set loadParam(file,fn) $loadParam(file,name)
    set loadParam(load,layer) $layer

    # may have to convert the file, based on content-encoding
    switch -- "$encoding" {
	bzip2 {
	    catch {set ch [open "| bunzip2 < $fn " r]}
	    set loadParam(load,type) channel
	    set loadParam(channel,name) $ch
	}
	compress {
	    catch {set ch [open "| uncompress < $fn " r]}
	    set loadParam(load,type) channel
	    set loadParam(channel,name) $ch
	}
	pack {
	    catch {set ch [open "| pcat $fn " r]}
	    set loadParam(load,type) channel
	    set loadParam(channel,name) $ch
	}
    }

    # we have to wait until the file has been downloaded
    if {$multi} {
	MultiLoad $layer $mode
    }
    ProcessLoad
    FinishLoad

    if {[file exists $fn]} {
	catch {file delete -force $fn}
    }
}

proc OpenURLFits {{layer {}} {mode {}}} {
    global fitsurl

    set url $fitsurl
    if {[EntryDialog [msgcat::mc {URL}] [msgcat::mc {Enter URL}] 80 url]} {
	LoadURLFits $url $layer $mode 1
	FinishLoad

	set fitsurl $url
    }
}

proc ProcessURLFitsCmd {varname iname} {
    upvar $varname var
    upvar $iname i

    urlfits::YY_FLUSH_BUFFER
    urlfits::yy_scan_string [lrange $var $i end]
    urlfits::yyparse
    incr i [expr $urlfits::yycnt-1]
}

proc LoadURLFitsCmd {url} {
    global current
    
    if {$current(frame) != {}} {
	switch -- [$current(frame) get type] {
	    base {set multi 1}
	    rgb {set multi 0}
	    3d {set multi 1}
	}
    } else {
	set multi 1
    }

    LoadURLFits $url {} {} $multi
}
