# Winup - animated notify window.
# Writted by Rejjin 
# 
# $Id: winup.tcl 464 2011-01-23 03:00:12Z Rejjin $

namespace eval winup {
variable config
variable toolbar_button
variable photos
variable threaded 0

	set config(script_dir) [file dirname [info script]]
	
	::msgcat::mcload [file join $config(script_dir) msgs]
	
	lappend ::auto_path $config(script_dir)
	
	package require fly
	package require resize
	package require base64
	package require Img
	
	if {![catch {package require Thread}] && \
		![catch {package require ThreadedResize}]} {
			set threaded 1
	}
	
	custom::defgroup Plugins [::msgcat::mc "Plugins options."] -group Tkabber	
    custom::defgroup Winup [::msgcat::mc "Winup custom options."] -group Plugins
	
	custom::defvar config(add_toolbar_button) 1 \
	[::msgcat::mc "Add toolbar button, for fast change state."] \
	-command [namespace current]::add_or_delete_toolbar_button \
	-group Winup -type boolean
	
	custom::defvar config(-anchor) se \
	[::msgcat::mc "Position of window."] \
	-type options -group Winup -values [list \
		n [::msgcat::mc "Up"] \
		ne [::msgcat::mc "Up-right"] \
		e [::msgcat::mc "Right"] \
		se [::msgcat::mc "Right-down"] \
		s [::msgcat::mc "Down"] \
		sw [::msgcat::mc "Down-left"] \
		w [::msgcat::mc "Left"] \
		nw [::msgcat::mc "Left-Up"] \
		center [::msgcat::mc "Center"] \
	]
	
	custom::defvar config(-animate) up \
	[::msgcat::mc "Animation of window."] \
	-type options -group Winup -values [list \
		left [::msgcat::mc "Left"] \
		right [::msgcat::mc "Right"] \
		up [::msgcat::mc "Up"] \
		down [::msgcat::mc "Down"] \
	]
	
	custom::defvar config(-offsetx) 0 \
	[::msgcat::mc "Shift of window (horisontal)."] \
	-group Winup -type integer
		
	custom::defvar config(-offsety) 0 \
	[::msgcat::mc "Shift of window (vertical)."] \
	-group Winup -type integer
		
	custom::defvar config(-keyframes) [list 1000 5000 500] \
	[::msgcat::mc "Keys for window."] \
	-group Winup -type string
	
	custom::defvar config(max_string_length) 250 \
	[::msgcat::mc "Maximum number of characters in incoming message."] \
	-group Winup -type integer
	
	custom::defvar config(auto_hide) 1 \
	[::msgcat::mc "Auto hide notify window."] \
	-group Winup -type boolean
	
	custom::defvar config(show_avatars) 1 \
	[::msgcat::mc "Show avatars on notifycation window."] \
	-group Winup -type boolean
	
	custom::defvar config(save_avatars) 1 \
	[::msgcat::mc "Save and load avatars."] \
	-group Winup -type boolean
	
	custom::defvar config(photo_width) 50 \
	[::msgcat::mc "Width of avatars in notify window."] \
	-group Winup -type integer
		
	custom::defvar config(photo_height) 50 \
	[::msgcat::mc "Height of avatars in notify window."] \
	-group Winup -type integer
		
	custom::defvar config(alpha) 90 \
	[::msgcat::mc "Window opacity (in percent)."] \
	-type integer -group Winup
	
	custom::defvar config(ignore_status) dnd \
	[::msgcat::mc "Ignore message if your status..."] \
	-type options -group Winup -values [list \
		available [::msgcat::mc "Available"] \
		chat [::msgcat::mc "Chat"] \
		away [::msgcat::mc "Away"] \
		xa [::msgcat::mc "Xa"] \
		dnd [::msgcat::mc "Dnd"] \
	]
	
	custom::defvar config(unique_jids) {} \
	[::msgcat::mc "JIDs for always notyfy (with regexp)."] \
	-type string -group Winup
	
	custom::defvar config(ignore_jids) {} \
	[::msgcat::mc "You'r black list of jids (with regexp)."] \
	-type string -group Winup
	
	custom::defvar config(bgcolor) #555555 \
	[::msgcat::mc "Background color."] \
	-type string -group Winup
		
	custom::defvar config(fgcolor) #cccccc \
	[::msgcat::mc "Foreground color."] \
	-type string -group Winup
		
	custom::defvar config(nick_font) {Tahoma 8} \
	[::msgcat::mc "Nick font."] \
	-type font -group Winup
		
	custom::defvar config(status_font) {Tahoma 7} \
	[::msgcat::mc "Status font."] \
	-type font -group Winup
		
	custom::defvar config(message_font) {Tahoma 8} \
	[::msgcat::mc "Message font."] \
	-type font -group Winup
		
	custom::defvar config(window_minimal_size) {285 120} \
	[::msgcat::mc "Minimal size of notifycation window."] \
	-type string -group Winup
		
	custom::defvar config(auto_update_tabs) 1 \
	[::msgcat::mc "Automatically mark \"as read\"."] \
	-type boolean -group Winup
		
	# Hidden Group
	custom::defvar toolbar_button(index) {-1} \
	[::msgcat::mc "Last button index"] \
	-type string -group Hidden
	custom::defvar toolbar_button(state) {1} \
	[::msgcat::mc "Button (and plugin) state"] \
	-type string -group Hidden
	
	image create photo {toolbar/winup-enabled} \
	-file [file join $config(script_dir) pixmaps toolbar-enabled.gif]
	image create photo {toolbar/winup-disabled} \
	-file [file join $config(script_dir) pixmaps toolbar-disabled.gif]	
	
	if {![::plugins::is_registered winup]} {
		::plugins::register winup \
			-namespace [namespace current] \
			-source [info script] \
			-description [::msgcat::mc "Whether the Winup plugin is loaded."] \
			-loadcommand [namespace code load_plugin] \
			-unloadcommand [namespace code unload_plugin]
		return
	}
	
	proc load_plugin {} {
		hook::add finload_hook \
		[namespace current]::add_toolbar_button
		hook::add process_message_hook \
		[namespace current]::parse_message 100
		add_toolbar_button
		load_photos
	}
	
	proc unload_plugin {} {
		hook::remove finload_hook \
		[namespace current]::add_toolbar_button
		hook::remove process_message_hook \
		[namespace current]::parse_message 100
		delete_toolbar_button
		save_photos
	}
}

proc winup::parse_message {xlib from id type is_subject \
			subject body err thread priority x} {
variable config
variable photos
variable threaded
	
	set mjid 0
	foreach majid $config(unique_jids) {
		if {[string match $majid $from]} {
		set mjid 1
		}
	}
		
	foreach ignored $config(ignore_jids) {
	if {[string match $ignored $from]} return
	}
	
	# Deactive state
	if {!$mjid && ![toolbar_button_state]} return
	
	# For empty messages
	if {[string length $body] == 0} {
	return
	}
	
	# If unsupported type - break
	if {![string equal $type "chat"] && \
		![string equal $type "groupchat"]} {
			return
	}
	
	# Get chatid
	if {[string equal $type "groupchat"]} {
	set vjid [::xmpp::jid::stripResource $from]
	} else {set vjid $from}
	if {[catch {set chatid [chat::chatid $xlib $vjid]}]} {
	return
	}
	
	# If window opened and see - break
	set input_win {}
	catch {set input_win [chat::input_win $chatid]}
	if {[string equal $input_win [focus]] || \
		[string equal [chat::chat_win $chatid] [focus]]} {
			return
	}
	
	if {[string equal $type "groupchat"]} {
		# If message from history - break
		if {[::xmpp::delay::exists $x]} {return}
		
		# For supported ignore plugin. Break is from ignored
		if {![catch {::plugins::mucignore::is_ignored $xlib $from $type} ignore] && \
			[string length $ignore] > 0} {
				return
		}

		set my_jid [chat::our_jid $chatid]
		set my_nick [chat::get_nick $xlib $my_jid $type]
		
		if {![check_message $my_nick $body]} {return}
	}
	
	set nick [::chat::get_nick $xlib $from $type]

	# Get resized photo
	set photo ""
	if {$config(show_avatars)} {
		if {![info exist photos($from)]} {
			if {[lsearch -exact [image names] photo_$from] >= 0} {
				if {$threaded} {
					set photos($from) [ThreadedResize::resize photo_$from \
					$config(photo_width) $config(photo_height)]
				} else {
					set photos($from) [resize::resize photo_$from \
					$config(photo_width) $config(photo_height)]
				}
			}
		}
		catch {set photo $photos($from)}
	}
		
	# If user status not ignored then show window
	if {![string equal $::curuserstatus $config(ignore_status)]} {
		create_window $xlib $from $type [get_user_status_desc $xlib $from] \
		$subject $photo $body $nick
	} else {
		foreach majid $config(unique_jids) {
			if {[string match $majid $from]} {
				create_window $xlib $from $type [get_user_status_desc $xlib $from] \
				$subject $photo $body $nick
				return
			}
		}
	}
}

proc winup::create_window {xlib from type status subject photo text nick} {
variable config
	
	if {[string equal $type "groupchat"]} {
		set titletext [::msgcat::mc "Tkabber: message from groupchat"]
		set chatid [chat::chatid $xlib [::xmpp::jid::stripResource $from]]
	} else {
		set titletext [::msgcat::mc "Tkabber: message from chat"]
		set chatid [chat::chatid $xlib $from]
	}
	
	if {$config(auto_update_tabs)} {
	set_updated $chatid
	}
	
	if {[info exist config(topwin)] && [winfo exist $config(topwin)]} { 
	if {[fly::state $config(topwin)] != 0} return
	set top $config(topwin)
	} else {
	set config(topwin) [new_path_id]
	toplevel $config(topwin)
	set top $config(topwin)
	}
	
	set config(id,$top) $chatid	
	
	catch {wm overrideredirect $top 1}
	
	foreach {option value} [wm attributes $top] {
		if {[string equal $option -topmost]} {
			wm attributes $top -topmost 1
		}
	}
	
	$top configure -background $config(bgcolor)
	
	set alpha [expr $config(alpha) / 100.0]
	if {$alpha <= 1.0} {
		foreach {option value} [wm attributes $top] {
			if {[string equal $option -alpha]} {
				wm attributes $top -alpha $alpha
			}
		}
	}

	lassign $config(window_minimal_size) minx miny
	if {[string is integer -strict $minx] && \
		[string is integer -strict $miny]} {
			wm minsize $top $minx $miny
	} else {wm minsize $top 285 150}
	
	children_configure $top $xlib $from $status \
		$subject $text $titletext $photo $nick
	
	::fly::init $top -anchor $config(-anchor) \
		-animate $config(-animate) \
		-offsetx $config(-offsetx) \
		-offsety $config(-offsety) \
		-keyframes $config(-keyframes) \
		-withdraw 1
	
	if {$config(auto_hide)} {
		fly::animate $top
		fly::animate $top 1
	} else {
	fly::animate $top 1
	}
}

proc winup::new_path_id { } {
	variable winid
	
	return .winup[incr winid]
}

proc winup::range_text {text mlength} {
	regsub -all "\n" $text [string repeat " " 20] tmp
	
	if {[string length $tmp] > $mlength} {
	set text "[string range $tmp 0 $mlength]..."
	}
	
	regsub -all [string repeat " " 20] $text \n text
	return $text
}

proc winup::on_click {key top} {
variable config
	
	set chatid $config(id,$top)
	
	set type [expr {[chat::is_groupchat $chatid] ? \
		"groupchat" : "chat"}]
	
	switch -- $key {
		Button-1 {
			::ifacetk::systray::restore
			::chat::activate $chatid
		}
		Button-3 {
			fly::hide $top 1
		} 
	}
}

proc winup::set_updated {chatid} {
    global usetabbar
    global tabcolors
    global alert_colors

    if {!$usetabbar} return

    set winid [::chat::winid $chatid]
	set page [ifacetk::nbpage $winid]

	set color [lindex $alert_colors 0]
	.nb itemconfigure $page -foreground $color -activeforeground $color
	set tabcolors($page) 0
 
	set ::ifacetk::number_msg($chatid) 0
	set ::ifacetk::personal_msg($chatid) 0
	
	ifacetk::update_chat_title $chatid
	ifacetk::update_main_window_title
}

proc winup::children_configure {top xlib from status subject text titletext photo nick} {
variable config

	if {![winfo exist $top]} {
	return -code error "bad window path name \"$top\""
	}

	catch {eval destroy [winfo children $top]}
	
	set status [range_text $status 100]
	set subject [range_text $subject 100]
	set text [range_text $text $config(max_string_length)]
	
	set wnd [frame $top.frame -relief flat -background $config(bgcolor) -class Winup]
	pack $wnd -expand 1 -fill both -anchor center -side top
	
	set ftop $wnd.top
	frame $ftop -relief flat -background $config(bgcolor)
	pack $ftop -expand 0 -fill x -anchor n -side top
	
	label $ftop.icon -background $config(bgcolor) \
		-image [::ifacetk::roster::get_jid_icon $xlib $from] 
	pack $ftop.icon -anchor nw	

	label $ftop.title -text $titletext -font {tahoma 8 bold} \
		-background $config(bgcolor) -foreground $config(fgcolor)
	pack $ftop.title -anchor nw

	grid $ftop.icon -row 0 -column 0
	grid $ftop.title -row 0 -column 1

	set fcenter $wnd.center
	frame $fcenter -relief flat
	pack $fcenter -expand 1 -fill both -anchor center -padx 5
	
	label $fcenter.icon -image $photo
	pack $fcenter.icon -anchor nw

	label $fcenter.user -text "$nick <$from>" -justify left -font $config(nick_font)
	pack $fcenter.user -anchor nw -side left
	
	label $fcenter.status -text $status -justify left -font $config(status_font)
	pack $fcenter.status -anchor nw
	
	if {[string length $subject] != 0} {
		label $fcenter.subject -text $subject -justify left \
			-font {tahoma 8 bold} -foreground red
		pack $fcenter.subject -anchor nw
	}
	
	message $fcenter.message -text $text -justify left \
	-relief solid -bd 0 -width 350 -font $config(message_font)
	pack $fcenter.message -anchor nw -side bottom -fill x

	grid $fcenter.icon -row 0 -column 0 -rowspan 4
	grid $fcenter.user -row 0 -column 1 -sticky w
	grid $fcenter.status -row 1 -column 1 -sticky w
	
	if {[string length $subject] == 0} {
		grid $fcenter.message -row 2 -column 1 -sticky w
	} else {
		grid $fcenter.subject -row 2 -column 1 -sticky w
		grid $fcenter.message -row 3 -column 1 -sticky w
	}
		
	frame $wnd.bottom -relief flat -background $config(bgcolor)
	pack $wnd.bottom -expand 0 -fill x -anchor center -pady 1 -side bottom	
	
	bind $top <Button-1> [list [namespace current]::on_click Button-1 $top]
	bind $top <Button-3> [list [namespace current]::on_click Button-3 $top]
	
	return $top
}

proc winup::new_path_id { } {
variable winid
	
	return .winup[incr winid]
}

# TOOLBAR BUTTON FUNCTIONS
proc winup::add_toolbar_button {} {
variable config
variable toolbar_button
	
	if {$config(add_toolbar_button)} {
		catch {.mainframe gettoolbar 0} toolbar
		set bbox $toolbar.bbox
		
		if {[winfo exist $toolbar.bbox] && \
			![ButtonBox::exist $toolbar.bbox $toolbar_button(index)]} {
				set toolbar_button(index) [ifacetk::add_toolbar_button \
					[state_toolbar_icon] \
						[namespace current]::change_button_state \
							[text_toolbar_button]]
		}
	}
}

proc winup::delete_toolbar_button {} {
variable toolbar_button
	
	catch {.mainframe gettoolbar 0} toolbar
	set bbox $toolbar.bbox
	
	if {[winfo exist $toolbar.bbox] && \
		[ButtonBox::exist $toolbar.bbox $toolbar_button(index)]} {
			ButtonBox::delete $toolbar.bbox $toolbar_button(index)
			set toolbar_button(index) -1
	}
}

proc winup::add_or_delete_toolbar_button {args} {
variable config 
	
	switch -- $config(add_toolbar_button) {
		1 add_toolbar_button 
		0 delete_toolbar_button
	}
}

proc winup::change_button_state { } {
variable toolbar_button
	
	set toolbar_button(state) [expr {$toolbar_button(state) ? {0} : {1}}]
	
	set bbox [.mainframe gettoolbar 0].bbox
	ButtonBox::itemconfigure $bbox $toolbar_button(index) \
		-helptext [text_toolbar_button] \
		-image [state_toolbar_icon]
}

proc winup::text_toolbar_button {} {
variable toolbar_button
	
	return [expr {$toolbar_button(state) ? \
		[::msgcat::mc "Deactivate notifycation."] : \
			[::msgcat::mc "Activate notifycation."]}]
}

proc winup::state_toolbar_icon {} {
variable toolbar_button
	
	return [expr {$toolbar_button(state) ? \
		"toolbar/winup-enabled" : \
			"toolbar/winup-disabled"}]
}

proc winup::toolbar_button_state {} {
variable toolbar_button
	return $toolbar_button(state)
}

proc winup::save_photos {} {
variable photos
variable config

	if {!$config(save_avatars)} return
	
	set av_dir [file join $::configdir winup-avatars]
	catch {file mkdir $av_dir}
	
	foreach photo [array names photos] {
		set file [::base64::encode $photo]
		$photos($photo) write [file join $av_dir $file]
	}
}

proc winup::load_photos {} {
variable photos
variable config

	if {!$config(save_avatars)} return
	
	set av_dir [file join $::configdir winup-avatars]
	catch {file mkdir $av_dir}
	
	foreach file_id [glob -nocomplain -dir $av_dir *] {
		set image_id [image create photo -file $file_id]
		set from [::base64::decode [file tail $file_id]]
		set photos($from) $image_id
	}
}

# ButtonBox patch for 
# adding and deleting toolbar button
proc ::ButtonBox::exist {path index} {
    variable $path
    upvar 0 $path data
	
	return [expr {[lsearch -exact $data(buttons) $index] > 0}]	
}

proc ::ButtonBox::delete { path idx } {
    variable $path
    upvar 0  $path data

    set i [lsearch -exact $data(buttons) $idx]
    set data(buttons) [lreplace $data(buttons) $i $i]
    destroy $path.b$idx
}
