# $Id: filters.tcl 1549 2008-10-18 10:13:07Z sergei $
#
# Obsolete jabberd 1.4 mod_filter (which has been never documented in XEP) support.
#


namespace eval filters {
    set condtags {unavailable from resource subject body show type}
    set acttags {settype forward reply offline continue}


    set fromtag(unavailable)	[::msgcat::mc "I'm not online"]
    set fromtag(from)		[::msgcat::mc "the message is from"]
    set fromtag(resource)	[::msgcat::mc "the message is sent to"]
    set fromtag(subject)	[::msgcat::mc "the subject is"]
    set fromtag(body) 		[::msgcat::mc "the body is"]
    set fromtag(show) 		[::msgcat::mc "my status is"]
    set fromtag(type) 		[::msgcat::mc "the message type is"]
    set fromtag(settype)	[::msgcat::mc "change message type to"]
    set fromtag(forward)	[::msgcat::mc "forward message to"]
    set fromtag(reply) 		[::msgcat::mc "reply with"]
    set fromtag(offline)	[::msgcat::mc "store this message offline"]
    set fromtag(continue)	[::msgcat::mc "continue processing rules"]

    set totag($fromtag(unavailable))	unavailable
    set totag($fromtag(from))		from
    set totag($fromtag(resource))	resource
    set totag($fromtag(subject))	subject
    set totag($fromtag(body)) 		body
    set totag($fromtag(show)) 		show
    set totag($fromtag(type)) 		type
    set totag($fromtag(settype))	settype
    set totag($fromtag(forward))	forward
    set totag($fromtag(reply)) 		reply
    set totag($fromtag(offline))	offline
    set totag($fromtag(continue))	continue

    set rulecondmenu [list $fromtag(unavailable) $fromtag(from) \
			  $fromtag(resource) $fromtag(subject) $fromtag(body) \
			  $fromtag(show) $fromtag(type)]

    set ruleactmenu [list $fromtag(settype) $fromtag(forward) $fromtag(reply) \
			 $fromtag(offline) $fromtag(continue)]

    set m [menu .rulecondmenu -tearoff 0]
    $m add command -label $fromtag(unavailable)
    $m add command -label $fromtag(from)
    $m add command -label $fromtag(resource)
    $m add command -label $fromtag(subject)
    $m add command -label $fromtag(body)
    $m add command -label $fromtag(show)
    $m add command -label $fromtag(type)

    set m [menu .ruleactmenu -tearoff 0]
    $m add command -label $fromtag(settype)
    $m add command -label $fromtag(forward)
    $m add command -label $fromtag(reply)
    $m add command -label $fromtag(offline)
    $m add command -label $fromtag(continue)

    custom::defgroup Privacy [::msgcat::mc "Blocking communication options."] -group Tkabber

    custom::defvar options(enable) 0 \
	[::msgcat::mc "Enable jabberd 1.4 mod_filter support (obsolete)."] \
	-type boolean -group Privacy \
	-command [namespace code setup_menu]
}

proc filters::setup_menu {args} {
    variable options

    set mlabel [::msgcat::mc "Edit message filters"]

    set m [.mainframe getmenu privacy]
    catch { set idx [$m index $mlabel] }

    if {$options(enable) && ![info exists idx]} {
	$m add separator
	$m add command -label $mlabel -command [namespace code open]
	return
    }
	
    if {!$options(enable) && [info exists idx]} {
	$m delete [expr {$idx - 1}] $idx
	return
    }
}

hook::add finload_hook [namespace current]::filters::setup_menu

proc filters::open {} {
    variable rf

    if {[winfo exists .filters]} {
	.filters draw
	return
    }

    set xlib [lindex [connections] 0]

    ::xmpp::sendIQ $xlib get \
	-query [::xmpp::xml::create item -xmlns jabber:iq:filter] \
	-command [list filters::recv]
}


proc filters::recv {res child} {
    variable rf
    variable rule
    variable rulelist

    debugmsg filters "$res $child"

    if {![string equal $res ok]} {
	MessageDlg .filters_err -aspect 50000 -icon error \
	    -message [::msgcat::mc "Requesting filter rules: %s" \
			  [error_to_string $child]] \
	    -type user -buttons ok -default 0 -cancel 0
	return
    }


    Dialog .filters -title [::msgcat::mc "Filters"] -separator 1 -anchor e \
	-modal none \
	-default 0 -cancel 1

    set f [.filters getframe]

    set bf [frame $f.bf]
    pack $bf -side right -anchor n

    set bb [ButtonBox $bf.bb -orient vertical -spacing 0]
    $bb add -text [::msgcat::mc "Add"] -command {filters::add}
    $bb add -text [::msgcat::mc "Edit"] -command {filters::edit}
    $bb add -text [::msgcat::mc "Remove"] -command {filters::remove}
    $bb add -text [::msgcat::mc "Move up"] -command {filters::move -1}
    $bb add -text [::msgcat::mc "Move down"] -command {filters::move 1}
    pack $bb -side top

    set sw [ScrolledWindow $f.sw]
    set rf [listbox $sw.rules]
    pack $sw -expand yes -fill both
    $sw setwidget $rf

    set ok [.filters add -text [::msgcat::mc "OK"] \
		-command {filters::commit}]
    .filters add -text [::msgcat::mc "Cancel"] -command {destroy .filters}

    $rf delete 0 end
    array unset rule
    set rulelist {}

    ::xmpp::xml::split $child tag xmlns attrs cdata subels

    if {[string equal $xmlns jabber:iq:filter]} {
	foreach child $subels {
	    process_rule $child
	}
    }
    $rf activate 0

    .filters draw
}

proc filters::process_rule {child} {
    variable rf
    variable rulelist

    ::xmpp::xml::split $child tag xmlns attrs cdata subels

    set rname [::xmpp::xml::getAttr $attrs name]
    $rf insert end $rname
    lappend rulelist $rname

    foreach data $subels {
	process_rule_data $rname $data
    }
}

proc filters::process_rule_data {name child} {
    variable rule

    ::xmpp::xml::split $child tag xmlns attrs cdata subels

    lappend rule($name) $tag $cdata
    debugmsg filters [array get rule]
}

proc filters::edit {} {
    variable rf

    set name [$rf get active]
    debugmsg filters $name
    if {$name != ""} {
	open_edit $name
    }
}


proc filters::open_edit {rname} {
    variable rule
    variable tmp

    set w [win_id rule $rname]

    if {[winfo exists $w]} {
	focus -force $w
	return
    }

    Dialog $w -title [::msgcat::mc "Edit rule"] -separator 1 -anchor e -modal none \
	    -default 0 -cancel 1

    set f [$w getframe]

    label $f.lrname -text [::msgcat::mc "Rule Name:"]
    entry $f.rname -textvariable filters::tmp($rname,name)
    set tmp($rname,name) $rname

    grid $f.lrname -row 0 -column 0 -sticky e
    grid $f.rname  -row 0 -column 1 -sticky ew

    set cond [TitleFrame $f.cond -text [::msgcat::mc "Condition"] -borderwidth 2 -relief groove]
    set fc [$cond getframe]

    button $fc.add -text [::msgcat::mc "Add"]
    pack $fc.add -side right -anchor n

    set swc [ScrolledWindow $fc.sw -relief sunken -borderwidth $::tk_borderwidth]
    pack $swc -expand yes -fill both
    set sfc [ScrollableFrame $swc.f -height 100]
    $swc setwidget $sfc

    grid $cond -row 1 -column 0 -sticky news -columnspan 2

    set act [TitleFrame $f.act -text [::msgcat::mc "Action"] -borderwidth 2 -relief groove]
    set fa [$act getframe]

    button $fa.add -text [::msgcat::mc "Add"]
    pack $fa.add -side right -anchor n

    set swa [ScrolledWindow $fa.sw -relief sunken -borderwidth $::tk_borderwidth]
    pack $swa -expand yes -fill both
    set sfa [ScrollableFrame $swa.f -height 100]
    $swa setwidget $sfa

    grid $act -row 2 -column 0 -sticky news -columnspan 2


    grid columnconfig $f 1 -weight 1 -minsize 0
    grid rowconfig $f 1 -weight 1
    grid rowconfig $f 2 -weight 1

    set fcond [$sfc getframe]
    set fact [$sfa getframe]

    $w add -text [::msgcat::mc "OK"] -command [list filters::accept_rule $w $rname $fcond $fact]
    $w add -text [::msgcat::mc "Cancel"] -command [list destroy $w]

    variable ruleactmenu
    variable rulecondmenu
    $fc.add configure \
	-command [list filters::insert_item \
		      $fcond unavailable "" $rulecondmenu]
    $fa.add configure \
	-command [list filters::insert_item $fact settype "" $ruleactmenu]

    fill_rule $rname $fcond $fact

    $w draw
}


proc filters::fill_rule {rname fcond fact} {
    variable rule
    variable condtags
    variable acttags
    variable ruleactmenu
    variable rulecondmenu
    variable items

    set items($fcond) {}
    set items($fact) {}
    foreach {tag value} $rule($rname) {
	if {[lcontain $condtags $tag]} {
	    debugmsg filters "C $tag $value"
	    insert_item $fcond $tag $value $rulecondmenu
	} elseif {[lcontain $acttags $tag]} {
	    debugmsg filters "A $tag $value"
	    insert_item $fact $tag $value $ruleactmenu
	}
    }
}



proc filters::insert_item {f tag val menu} {
    variable items
    variable fromtag

    if {[llength $items($f)]} {
	set n [expr {[lindex $items($f) [expr {[llength $items($f)] - 1}]] + 1}]
    } else { 
	set n 0
    }

    # TODO: hiding entry for some tags
    eval [list OptionMenu $f.mb$n $f.mb$n.var] $menu
    global $f.mb$n.var
    set $f.mb$n.var $fromtag($tag)
    entry $f.e$n
    $f.e$n insert 0 $val
    Separator $f.sep$n -orient vertical
    button $f.remove$n -text [::msgcat::mc "Remove"] -command [list filters::remove_item $f $n]

    grid $f.mb$n      -row $n -column 0 -sticky ew
    grid $f.e$n       -row $n -column 1 -sticky ew
    grid $f.sep$n     -row $n -column 2 -sticky ew
    grid $f.remove$n  -row $n -column 3 -sticky ew


    lappend items($f) $n
    debugmsg filters $items($f)
}

proc filters::remove_item {f n} {
    variable items

    set idx [lsearch -exact $items($f) $n]
    set items($f) [lreplace $items($f) $idx $idx]

    eval destroy [grid slaves $f -row $n]

    debugmsg filters $items($f)
}

proc filters::accept_rule {w rname fcond fact} {
    variable items
    variable totag
    variable rule
    variable tmp
    variable rf
    variable rulelist

    set newname $tmp($rname,name)
    if {$newname == ""} {
	MessageDlg .rname_err -aspect 50000 -icon error \
	    -message [::msgcat::mc "Empty rule name"] -type user \
	    -buttons ok -default 0 -cancel 0
	return
    }
    if {$rname != $newname && [lcontain $rulelist $newname]} {
	MessageDlg .rname_err -aspect 50000 -icon error \
	    -message [::msgcat::mc "Rule name already exists"] -type user \
	    -buttons ok -default 0 -cancel 0
	return
    }


    set rule($newname) {}
    foreach n $items($fcond) {
	set tag $totag([set ::$fcond.mb$n.var])
	set val [$fcond.e$n get]
	debugmsg filters "$tag $val"
	lappend rule($newname) $tag $val
    }

    foreach n $items($fact) {
	set tag $totag([set ::$fact.mb$n.var])
	set val [$fact.e$n get]
	debugmsg filters "$tag $val"
	lappend rule($newname) $tag $val
    }

    debugmsg filters [array get rule]

    set idx [lsearch -exact $rulelist $rname]
    set rulelist [lreplace $rulelist $idx $idx $newname]

    $rf delete 0 end
    foreach r $rulelist {
	$rf insert end $r
    }


    set items($fcond) {}
    set items($fact) {}
    destroy $w
}

proc filters::add {} {
    variable rule
    set rule() {}
    open_edit ""
}

proc filters::remove {} {
    variable rf
    variable rulelist

    set name [$rf get active]
    debugmsg filters $name
    if {$name != ""} {
	set idx [lsearch -exact $rulelist $name]
	set rulelist [lreplace $rulelist $idx $idx]
	$rf delete active
	debugmsg filters $rulelist
    }
}

proc filters::commit {} {
    variable rulelist
    variable rule

    set result {}
    foreach rname $rulelist {
	set rtags {}
	foreach {tag val} $rule($rname) {
	    lappend rtags [::xmpp::xml::create $tag -cdata $val]
	}

	lappend result [::xmpp::xml::create rule \
			    -attrs [list name $rname] \
			    -subelements $rtags]
    }

    debugmsg filters $result

    set xlib [lindex [connections] 0]

    ::xmpp::sendIQ $xlib set \
	    -query [::xmpp::xml::create item \
			    -xmlns jabber:iq:filter \
			    -subelements $result]

    destroy .filters
}

proc filters::move {shift} {
    variable rulelist
    variable rf

    set name [$rf get active]
    set idx [lsearch -exact $rulelist $name]
    set rulelist [lreplace $rulelist $idx $idx]
    set newidx [expr {$idx + $shift}]
    set rulelist [linsert $rulelist $newidx $name]

    debugmsg filters $rulelist

    $rf delete 0 end
    foreach r $rulelist {
	$rf insert end $r
    }

    $rf activate $newidx
    $rf selection set $newidx

    #set newidx [expr [$rf index active] - 1]
    #$rf move active $newidx
}
