set fissDict {}

# defaults
set showLegend on

proc solcon {value} {
    global fissDict
    dict append fissDict solcon $value
}

proc solcov {value} {
    global fissDict
    dict append fissDict solcov $value
}

proc title {value} {
    global fissDict
    dict append fissDict title $value
}

proc recipes {value} {
    global fissDict
    set normalized {}
    set recipeNames [dict keys $value]
    foreach recipeName $recipeNames {
	set normalized_conds [list {*}[dict get $value $recipeName conds]]
	set normalized_metrics [list {*}[dict get $value $recipeName metrics]]
	dict set normalized $recipeName conds $normalized_conds
	dict set normalized $recipeName metrics $normalized_metrics
    }
    dict append fissDict recipes $normalized
}

proc stub {value} {
    global fissDict
    set myConds [lconds]
    set stubEntries $value
    foreach x $stubEntries {
	if {$x eq "."} {
	    lappend out [list $x vspace]
	} elseif {[lsearch $myConds $x] >= 0} {
	    lappend out [list $x cond]
	} else {
	    lappend out [list $x subhead]
	}
    }
    dict append fissDict stub $out
}

# given a list of words, report the length of each streak of repeated
# words. return a list of word/count pairs in the order they
# appear. 'a a a b b a a a a' returns {a 3 b 2 a 4}.
proc wordRuns l {
    set runs {}
    foreach w $l {
	set word [lindex $runs end-1]
	if {$word eq $w} {
	    incr count
	    set runs [lreplace $runs end end $count]
	} else {
	    set count 1
	    lappend runs $w $count
	}
    }
    return $runs
}

# the boxhead is implemented as a simple list, where the last N
# elements must be the conditions (columns) of the Fiss chart.  we can
# therefore use this information to figure out the shape of the
# implied matrix.
#
# I use this information to split the boxhead into a list of list,
# where the initial lists are all grouping categories and spans over
# subcategories and, ultimately, the conditions (columns).
proc boxhead {value} {
    global fissDict

    # first, get how many recipes are listed in the header.  Also keep
    # in mind here that the user might decide to not present certain
    # recipes.  so it's required to actually compare the recipes
    # listed in the boxhead to the recipes in the recipe list.
    set myRecipes [recipeNames]
    set numRecipes 0
    foreach header $value {
	if {[lsearch $myRecipes $header] >= 0} {
	    incr numRecipes
	}
    }
    
    # for now, I'm assuming the happy path and that the user has
    # correctly specified the matrix.  i'll deal with errors later,
    # when I have more time.

    # given that we now know how many columns the Fiss chart has, go
    # through and grab that many at a time into a list
    for {set i 0} {$i < [llength $value]} {incr i $numRecipes} {
	set row [lrange $value $i [expr {$i + $numRecipes - 1}]]
	lappend out [wordRuns $row]
    }
    dict append fissDict boxhead $out
}

proc vrules {value} {
    global fissDict
    foreach {style _ recipe} $value {
	lappend out [list $style $recipe]
    }
    dict append fissDict vrules $out
}

# list the conditions belonging to fissDict recipes
#
# this is not the same as the conditions of the Fiss chart b/c the
# user selects which conditions to include/exclude based upon how they
# define the stub.  In order to know which conditions are present in
# the Fiss chart, need to query the stub using the corresponding
# recipes.
proc lconds {} {
    global fissDict
    set myRecipes [dict get $fissDict recipes]
    foreach {_ recipeBody} $myRecipes {
	set x [lindex $recipeBody 1]
	set len [llength $x]
	for {set i 0} {$i < $len} {incr i 2} {
	    lappend out [lindex $x $i]
	 }
    }
    return [lsort -unique $out]
}

# list the recipes of the Fiss chart
proc recipeNames {} {
    global fissDict
    set myRecipes [dict get $fissDict recipes]
    return [dict keys $myRecipes]
}

# list recipes in boxhead, in order
proc boxheadRecipeNames {} {
    global fissDict
    set myBoxheadRecipes [lindex [dict get $fissDict boxhead] end]
    set myRecipes [recipeNames]
    foreach {recipe _} $myBoxheadRecipes {
	if {[lsearch $myRecipes $recipe] >= 0} {
	    lappend out $recipe
	}
    }
    return $out
}
    

# list the stub conditions
proc stubConds {} {
    global fissDict
    set out {}
    foreach x [dict get $fissDict stub] {
	set x0 [lindex $x 0]
	set x1 [lindex $x 1]
	if { $x1 eq "cond" } {
	    lappend out $x0
	}
    }
    return $out
}

# list the stub subheaders
proc stubSubheads {} {
    global fissDict
    set out {}
    foreach x [dict get $fissDict stub] {
	set x0 [lindex $x 0]
	set x1 [lindex $x 1]
	if { $x1 eq "subhead" } {
	    lappend out $x0
	}
    }
    return $out
}

proc showFatalError {title shortMsg longMsg} {
    while 1 {
        set btn [tk_messageBox \
            -icon error -type abortretryignore \
            -title $title \
            -message $shortMsg \
            -detail "$longMsg" \
            -default abort]

        # Map buttons: yes=copy, no=quit, cancel=stay
        if {$btn eq "abort"} {
	    exit 1
        } else {
            # cancel: do nothing, user can e.g. screenshot
            return
        }
    }
}
