# $Id: events.tcl 1416 2008-05-12 08:24:02Z sergei $
#
# Message Events (XEP-0022) support.
#

namespace eval events {
    custom::defgroup Events \
	[::msgcat::mc "Chat message events plugin options."] \
	-group Chat
    custom::defvar options(enable) 1 \
	[::msgcat::mc "Enable sending chat message events."] \
	-type boolean -group Events

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

proc events::process_x {check_request chatid from type body xs} {
    variable chats
    variable options

    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(event) {
		    return [process_x_event \
				$check_request $chatid $from $type $body $x]
		}
	}
    }
}

hook::add draw_message_hook \
    [list [namespace current]::events::process_x 0] 0

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

proc events::process_x_event {check_request chatid from type body x} {
    variable options
    variable events

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

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

    set offline 0
    set delivered 0
    set displayed 0
    set composing 0
    set id 0
    foreach child $children {
	jlib::wrapper:splitxml $child \
	    tag1 vars1 isempty1 chdata1 children1
	switch -- $tag1 {
	    offline   {set offline 1}
	    delivered {set delivered 1}
	    displayed {set displayed 1}
	    composing {set composing 1}
	    id        {set id        1}
	}
    }
    if {$id && !$check_request} {
	set status ""
	if {$offline}   {set status offline}
	if {$delivered} {set status delivered}
	if {$displayed} {set status displayed}
	if {$composing} {set status composing}
	change_status $chatid $status
	if {$body != ""} {
	    # due to some buggy clients (e.g. Yahoo-t), which send <id/>
	    # with real messages
	    return
	} else {
	    return stop
	}
    } elseif {!$id && $check_request} {
	clear_status $chatid

	set events(displayed,$chatid) $displayed
	set events(composing,$chatid) $composing

	if {!$options(enable)} return

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

	lappend eventtags [jlib::wrapper:createtag id \
			       -chdata $::chat::chats(id,$chatid)]
	if {$delivered} {
	    lappend eventtags [jlib::wrapper:createtag delivered]
	}
	if {$displayed} {
	    if {$::usetabbar} {
		set page [crange [win_id tab $cw] 1 end]
		if {[.nb raise] == $page} {
		    lappend eventtags [jlib::wrapper:createtag displayed]
		    set events(displayed,$chatid) 0
		}
	    } else {
		lappend eventtags [jlib::wrapper:createtag displayed]
		set events(displayed,$chatid) 0
	    }
	}
	if {[llength $eventtags] > 1} {
	    lappend xlist [jlib::wrapper:createtag x \
			       -vars [list xmlns $::NS(event)] \
			       -subtags $eventtags]

	    jlib::send_msg $from -xlist $xlist -connection $connid
	}
    }
    return
}

proc events::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 {
	offline   {
	    set text [::msgcat::mc "Message stored on the server"]
	    set stext [format [::msgcat::mc "Message stored on %s's server"] \
			   $jid]
	}
	delivered {
	    set text [::msgcat::mc "Message delivered"]
	    set stext [format [::msgcat::mc "Message delivered to %s"] $jid]
	}
	displayed {
	    set text [::msgcat::mc "Message displayed"]
	    set stext [format [::msgcat::mc "Message displayed to %s"] $jid]
	}
	composing {
	    set text [::msgcat::mc "Composing a reply"]
	    set stext [format [::msgcat::mc "%s is composing a reply"] $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 events::clear_status {chatid} {
    set cw [chat::winid $chatid]
    if {![winfo exists $cw]} return
    $cw.status.event configure -text ""
}

proc events::send_event_on_raise {cw chatid} {
    variable options
    variable events

    if {![chat::is_chat $chatid]} return
    if {![info exists events(displayed,$chatid)] || \
	    !$events(displayed,$chatid)} return

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

    set events(displayed,$chatid) 0

    if {!$options(enable)} return

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

    lappend eventtags [jlib::wrapper:createtag id \
			   -chdata $::chat::chats(id,$chatid)]
    lappend eventtags [jlib::wrapper:createtag displayed]

    lappend xlist [jlib::wrapper:createtag x \
		       -vars [list xmlns $::NS(event)] \
		       -subtags $eventtags]

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

hook::add raise_chat_tab_hook [namespace current]::events::send_event_on_raise

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

    if {![chat::is_chat $chatid]} return
    if {$sym == ""} return
    if {![info exists events(composing,$chatid)] || \
	    !$events(composing,$chatid)} return

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

    set events(composing,$chatid) 0

    if {!$options(enable)} return

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

    lappend eventtags [jlib::wrapper:createtag id \
			   -chdata $::chat::chats(id,$chatid)]
    lappend eventtags [jlib::wrapper:createtag composing]

    lappend xlist [jlib::wrapper:createtag x \
		       -vars [list xmlns $::NS(event)] \
		       -subtags $eventtags]

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

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

    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]::events::setup_ui

proc events::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]::events::clear_status_on_send

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

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

    lappend events [jlib::wrapper:createtag offline]
    lappend events [jlib::wrapper:createtag delivered]
    lappend events [jlib::wrapper:createtag displayed]
    lappend events [jlib::wrapper:createtag composing]
    lappend var [jlib::wrapper:createtag x \
		     -vars [list xmlns $::NS(event)] \
		     -subtags $events]
    return
}

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

