# $Id: chatstate.tcl 1416 2008-05-12 08:24:02Z sergei $
#
# Chat State Notifications (XEP-0085) (only <active/> and <composing/>) support.
#

namespace eval chatstate {
    custom::defgroup Chatstate \
	[::msgcat::mc "Chat message window state plugin options."] \
	-group Chat
    custom::defvar options(enable) 0 \
	[::msgcat::mc "Enable sending chat state notifications."] \
	-type boolean -group Chatstate

    disco::register_feature $::NS(chatstate)
}

# Workaround a bug in JIT, which responds with error to chatstate
# events without a body
proc chatstate::ignore_error \
     {connid from id type is_subject subject body err thread priority x} {
    switch -- $type/$id {
	error/chatstate {
	    return stop
	}
    }
}

hook::add process_message_hook [namespace current]::chatstate::ignore_error 20

proc chatstate::flush_composing {chatid user body type} {
    variable chatstate

    set chatstate(composing,$chatid) 1
}

hook::add chat_send_message_hook \
    [list [namespace current]::chatstate::flush_composing] 91

proc chatstate::process_x {chatid from type body xs} {
    if {$type == "chat"} {
	foreach x $xs {
	    jlib::wrapper:splitxml $x tag vars isempty chdata children
	    set xmlns [jlib::wrapper:getattr $vars xmlns]
	    switch -- $xmlns \
		$::NS(chatstate) {
		    return [process_x_chatstate \
				$chatid $from $type $body $x]
		}
	}
    }
}

hook::add draw_message_hook \
    [list [namespace current]::chatstate::process_x] 1

proc chatstate::process_x_chatstate {chatid from type body x} {
    variable options
    variable chatstate

    jlib::wrapper:splitxml $x tag vars isempty chdata children

    switch -- $tag {
	active {
	    if {![info exists chatstate(active,$chatid)] || \
		    !$chatstate(active,$chatid)} {
		set chatstate(active,$chatid) 1
		change_status $chatid active
	    }
	}
	inactive {
	    if {[info exists chatstate(active,$chatid)] && \
		    $chatstate(active,$chatid)} {
		set chatstate(active,$chatid) 0
		change_status $chatid inactive
	    }
	}
	gone {
	    set chatstate(active,$chatid) 0
	    change_status $chatid gone
	}
	composing -
	paused {
	    change_status $chatid $tag
	}
    }
    return
}

proc chatstate::change_status {chatid status} {
    variable event_afterid

    if {[info exists event_afterid($chatid)]} {
	after cancel $event_afterid($chatid)
    }
    set cw [chat::winid $chatid]
    set jid [chat::get_jid $chatid]
    set text ""
    set stext ""
    switch -- $status {
	active {
	    set text [::msgcat::mc "Chat window is active"]
	    set stext [format [::msgcat::mc "%s has activated chat window"] $jid]
	}
	composing {
	    set text [::msgcat::mc "Composing a reply"]
	    set stext [format [::msgcat::mc "%s is composing a reply"] $jid]
	}
	paused {
	    set text [::msgcat::mc "Paused a reply"]
	    set stext [format [::msgcat::mc "%s is paused a reply"] $jid]
	}
	inactive {
	    set text [::msgcat::mc "Chat window is inactive"]
	    set stext [format [::msgcat::mc "%s has inactivated chat window"] $jid]
	}
	gone {
	    set text [::msgcat::mc "Chat window is gone"]
	    set stext [format [::msgcat::mc "%s has gone chat window"] $jid]
	}
    }

    if {$stext != "" && $::usetabbar} {set_status $stext}
    if {![winfo exists $cw]} return
    $cw.status.event configure -text $text
    set event_afterid($chatid) \
	[after 10000 [list [namespace current]::clear_status $chatid]]
}

proc chatstate::clear_status {chatid} {
    set cw [chat::winid $chatid]
    if {![winfo exists $cw]} return
    $cw.status.event configure -text ""
}

proc chatstate::event_composing {chatid sym} {
    variable options
    variable chatstate

    if {![chat::is_chat $chatid]} return
    if {$sym == ""} return
    if {[info exists chatstate(composing,$chatid)] && \
	    !$chatstate(composing,$chatid)} return
    if {!$options(enable)} return

    set connid [chat::get_connid $chatid]
    set jid [chat::get_jid $chatid]

    set chatstate(composing,$chatid) 0

    if {[get_jid_status $connid $jid] == "unavailable"} return

    lappend xlist [jlib::wrapper:createtag composing \
		       -vars [list xmlns $::NS(chatstate)]]

    jlib::send_msg $jid -id chatstate -xlist $xlist -connection $connid
}

proc chatstate::setup_ui {chatid type} {
    variable chatstate

    if {![chat::is_chat $chatid]} return

    set cw [chat::winid $chatid]

    set l $cw.status.event
    if {![winfo exists $l]} {
	label $l
	pack $l -side left
    }

    set iw [chat::input_win $chatid]
    bind $iw <Key> +[list [namespace current]::event_composing \
			 [double% $chatid] %A]
}

hook::add open_chat_post_hook [namespace current]::chatstate::setup_ui

proc chatstate::clear_status_on_send {chatid user body type} {
    if {![chat::is_chat $chatid]} return
    clear_status $chatid
}

hook::add chat_send_message_hook \
    [namespace current]::chatstate::clear_status_on_send

proc chatstate::make_xlist {varname chatid user body type} {
    variable options
    variable chatstate
    upvar 2 $varname var

    if {!$options(enable) || $type != "chat"} {
	return
    }

    set chatstate(windowactive,$chatid) 1

    lappend var [jlib::wrapper:createtag active \
		     -vars [list xmlns $::NS(chatstate)]]
    return
}

hook::add chat_send_message_xlist_hook \
    [namespace current]::chatstate::make_xlist

proc chatstate::send_gone {chatid} {
    variable options
    variable chatstate

    if {![info exists chatstate(windowactive,$chatid)] || \
	    ($chatstate(windowactive,$chatid) == 0)} return

    if {![chat::is_chat $chatid]} return
    if {!$options(enable)} return

    set connid [chat::get_connid $chatid]
    set jid [chat::get_jid $chatid]

    unset chatstate(windowactive,$chatid)

    if {[get_jid_status $connid $jid] == "unavailable"} return

    lappend xlist [jlib::wrapper:createtag gone \
		       -vars [list xmlns $::NS(chatstate)]]

    jlib::send_msg $jid -id chatstate -xlist $xlist -connection $connid
}

hook::add close_chat_post_hook \
    [namespace current]::chatstate::send_gone 10

