
#-------------------------------------------------------------------------------
# Module:     menuEdit.tk
# Activation: Loaded during GOBLET startup
# Scope:      Edit menu item, bindings, some small dialogs
#             Handling of transitions between different run modes
#             Handling of modified objects
#             Checking node and arc indices
#-------------------------------------------------------------------------------



proc MakeEditMenu {} {
    global ThisGraph
    
    destroy .mbar.edit.menu

    menu .mbar.edit.menu -tearoff false

    if {[$ThisGraph is mip]} {
        .mbar.edit.menu add command -label "Reset Basis" \
            -accelerator Ctrl+X -command ResetLPBasis
        .mbar.edit.menu add command -label "Pivoting..."\
            -accelerator Shift+Ctrl+P -command LPPivoting
        .mbar.edit.menu add command -label "Edit Rows..."\
            -accelerator Shift+Ctrl+R -command EditLPRow
        .mbar.edit.menu add command -label "Edit Columns..."\
            -accelerator Shift+Ctrl+C -command EditLPColumn

        menu .mbar.edit.menu.objsense -tearoff false
        .mbar.edit.menu add cascade -label "Object Sense"\
            -menu .mbar.edit.menu.objsense
        .mbar.edit.menu.objsense add command -label "Minimize"\
            -command {
                $ThisGraph minimize
                PropagateModifications
            }
        .mbar.edit.menu.objsense add command -label "Maximize"\
            -command {
                $ThisGraph maximize
                PropagateModifications
            }
        .mbar.edit.menu.objsense add command -label "Revert"\
            -command {
                $ThisGraph revert
                PropagateModifications
            }
        .mbar.edit.menu.objsense add command -label "Nullify"\
            -command {
                $ThisGraph nullify
                PropagateModifications
            }

        bind . <Control-a> {}
        bind . <Control-v> {}
        bind . <Control-r> {}
        bind . <Control-i> {}
        bind . <Control-x> ResetLPBasis
        bind . <Control-m> {}
        bind . <Control-e> {}
        bind . <Control-C> EditLPColumn
        bind . <Control-X> {}
        bind . <Control-E> {}
        bind . <Control-P> LPPivoting
        bind . <Control-R> EditLPRow
    } else {
        if {[$ThisGraph is sparse]} {
            .mbar.edit.menu add command -label "Insert Arcs" \
                -accelerator Ctrl+A -command InsertArcs
            .mbar.edit.menu add command -label "Insert Nodes" \
                -accelerator Ctrl+V -command AddNodes
            .mbar.edit.menu add command -label "Redirect Arcs" \
                -accelerator Ctrl+R -command RedirectArcs
            .mbar.edit.menu add command -label "Reorder Incidences" \
                -accelerator Ctrl+I -command EditIncidences
            .mbar.edit.menu add command -label "Delete Objects" \
                -accelerator Ctrl+X -command DeleteObjects
            .mbar.edit.menu add command -label "Explicit Parallels" \
                -command ExplicitParallels
            .mbar.edit.menu add separator

            bind . <Control-a> {InsertArcs}
            bind . <Control-v> {AddNodes}
            bind . <Control-r> {RedirectArcs}
            bind . <Control-x> {DeleteObjects}
            bind . <Control-i> {EditIncidences}
        } else {
            bind . <Control-a> {}
            bind . <Control-v> {}
            bind . <Control-r> {}
            bind . <Control-x> {}
            bind . <Control-i> {}
        }
        
        .mbar.edit.menu add command -label "Move Nodes" \
            -accelerator Ctrl+M -command MoveNodes
        .mbar.edit.menu add command -label "Edit Labels" \
            -accelerator Ctrl+E -command EditLabels
        .mbar.edit.menu add command -label "Set Colours" \
            -command SetColours -underline 4
        .mbar.edit.menu add command -label "Set Predecessors" \
            -command SetPredecessors -underline 4
        .mbar.edit.menu add command -label "Constant Labels..." \
            -accelerator Shift+Ctrl+C \
            -command {source [file join $GoblinTclPath editConstants.tk]}

        if {![$ThisGraph is sparse]} {
            menu .mbar.edit.menu.metrics -tearoff false
            .mbar.edit.menu add cascade -label "Metrics" \
                -underline 0 -menu .mbar.edit.menu.metrics
            .mbar.edit.menu.metrics add command -label "Explicit Length Labels" \
                -underline 9 -command {
                    set goblinMethGeometry 0
                    PropagateModifications
                }
            .mbar.edit.menu.metrics add command -label "Manhattan Metrics" \
                -underline 0 -command {
                    set goblinMethGeometry 1
                    PropagateModifications
                }
            .mbar.edit.menu.metrics add command -label "Euclidian Distances" \
                -underline 0 -command {
                    set goblinMethGeometry 2
                    PropagateModifications
                }
            .mbar.edit.menu.metrics add command -label "Maximum Metrics" \
                -underline 1 -command {
                    set goblinMethGeometry 3
                    PropagateModifications
                }
            .mbar.edit.menu.metrics add command -label "Spheric Distances" \
                -underline 0 -command {
                    set goblinMethGeometry 4
                    PropagateModifications
                }
        }

        .mbar.edit.menu add separator
        .mbar.edit.menu add command -label "Delete Solutions..." \
            -accelerator Shift+Ctrl+X \
            -command {source [file join $GoblinTclPath editDelete.tk]}
        .mbar.edit.menu add command -label "Extract Solutions..." \
            -accelerator Shift+Ctrl+E \
            -command {source [file join $GoblinTclPath editExtract.tk]}

        bind . <Control-m> {MoveNodes}
        bind . <Control-e> {EditLabels}
        bind . <Control-C> {source [file join $GoblinTclPath editConstants.tk]}
        bind . <Control-X> {source [file join $GoblinTclPath editDelete.tk]}
        bind . <Control-E> {source [file join $GoblinTclPath editExtract.tk]}
        bind . <Control-P> {}
        bind . <Control-R> {}
    }
    
    .mbar.edit.menu add separator
    .mbar.edit.menu add command -label "Undo Changes" \
        -accelerator Ctrl+Z -command UndoChanges
    .mbar.edit.menu add command -label "Redo Changes" \
        -accelerator Ctrl+Y -command RedoChanges

    bind . <Control-y> {RedoChanges}
    bind . <Control-z> {UndoChanges}
}

proc ResetMode {nextMode} {
    global Mode StartNode ThisArc ThisNode ActiveDialog

    DestroyBalloon

    if {[string equal $Mode "insert arcs"]} {
        if {$ThisArc != ""} {
            $c delete $ThisArc
            set ThisArc ""
        }
        set StartNode ""
    }

    if {[string equal $Mode "align label"]} {
        set ThisNode ""
        set ThisArc ""
    }

    if {[string equal $Mode "move nodes"]} {
        set ThisNode ""
    }

    if {[string equal $Mode "change labels"]} {
        UploadArcLabels
        UploadNodeLabels
    }

    set Mode $nextMode
}

proc InsertArcs {} {
    if {![goblin solver idle]} {return}

    ResetMode "insert arcs"
    InitEditor
    MakeHelp "(L) select start node  (R) add bend nodes"
}

proc ResetLPBasis {} {
    if {![goblin solver idle]} {return}

    global ThisGraph StatusInfo

    $ThisGraph reset
    PropagateModifications

    set StatusInfo "Basis set to lower variable bounds"
}

proc CheckLpVar {} {
    global ThisGraph DialogPar StatusInfo
    
    set k [$ThisGraph #variables]
    for {set i 0} {$i<$k &&
        $DialogPar("LpVarLabel")!=[$ThisGraph variable $i label]} {incr i} {}
    if {$i==$k} {
        set StatusInfo "variable label \"$DialogPar("LpVarLabel")\" not found"
        return -1
    } else {
        return $i
    }
}

proc CheckLpRow {} {
    global ThisGraph DialogPar StatusInfo
    
    set k [$ThisGraph #rows]
    if {$DialogPar("LpRowLabel")==""} {return [expr [CheckLpVar]+$k]}

    for {set i 0} {$i<$k &&
        $DialogPar("LpRowLabel")!=[$ThisGraph row $i label]} {incr i} {}
    if {$i==$k} {
        set StatusInfo "row label \"$DialogPar("LpRowLabel")\" not found"
        return -1
    } else {
        return $i
    }
}

proc LPPivoting {} {
    global GoblinTclPath

    source [file join $GoblinTclPath editPivot.tk]
}

proc EditLPRow {} {
    global GoblinTclPath

    source [file join $GoblinTclPath editRow.tk]
}

proc EditLPColumn {} {
    global GoblinTclPath

    source [file join $GoblinTclPath editVar.tk]
}

proc AddNodes {} {
    if {![goblin solver idle]} {return}

    ResetMode "insert nodes"
    InitEditor

    global ThisGraph
    if {![$ThisGraph is bipartite]} {
        MakeHelp "(L) place new node"
    } else {
        MakeHelp "(L) place new node  (R) Move to other component"
    }
    
}

proc QualifiedNode {x} {
    global ThisGraph StatusInfo
    if {$x=="*"} {return 1}
    if {$x=="" || [catch {expr int($x)}] || $x!=[expr int($x)]} {
        set StatusInfo "node index \"$x\" not accepted"
        return 0
    }
    if {[expr $x<[$ThisGraph #nodes] && $x>=0]} {
        return 1
    } else {
        set StatusInfo "node index \"$x\" not accepted"
        return 0
    }
}

proc QualifiedArc {x} {
    global ThisGraph StatusInfo
    if {$x=="*"} {return 1}
    if {$x=="" || [catch {expr int($x)}] || $x!=[expr int($x)]} {
        set StatusInfo "arc index \"$x\" not accepted"
        return 0
    }
    if {[expr $x<2*[$ThisGraph #arcs] && $x>=0]} {
        return 1
    } else {
        set StatusInfo "arc index \"$x\" not accepted"
        return 0
    }
}

proc QualifiedInt {x lower upper} {
    global StatusInfo
    if {$x=="" || [catch {expr int($x)}] || $x!=[expr int($x)] \
            || ($upper!="*" && $x>$upper) || ($lower!="*" && $x<$lower)} {
        set StatusInfo "value \"$x\" not accepted"
        return 0
    } else {
        return 1
    }
}

set ThisArc ""
proc MoveNodes {} {
    ResetMode "move nodes"
    InitEditor
    MakeHelp "(L) drag and drop object"
}

proc EditLabels {} {
    ResetMode "change labels"
    InitEditor
    MakeHelp "(L) select object"
}

proc DownloadNodeLabels {v} {
    global ThisGraph ThisNode ThisDemand ThisDistance ThisColour \
        ThisPotential ThisPredecessor
    
    set ThisNode $v
    set ThisDemand [$ThisGraph node $v demand]
    set ThisColour [$ThisGraph node $v colour]
    set ThisDistance [$ThisGraph node $v distance]
    set ThisPotential [$ThisGraph node $v potential]
    set ThisPredecessor [$ThisGraph node $v predecessor]
}

proc DownloadArcLabels {a} {
    global ThisGraph ThisArc ThisUpper ThisLower ThisLength ThisSubgraph
    
    set ThisArc $a
    set ThisUpper [$ThisGraph arc [expr {2*$a}] ucap]
    set ThisLower [$ThisGraph arc [expr {2*$a}] lcap]
    set ThisLength [$ThisGraph arc [expr {2*$a}] length]
    set ThisOrientation [$ThisGraph arc [expr {2*$a}] orientation]
    set ThisSubgraph [$ThisGraph arc [expr {2*$a}] subgraph]
}

proc UploadNodeLabels {} {
    global ThisGraph ThisNode ThisDemand ThisDistance ThisColour \
        ThisPotential ThisPredecessor
    
    if {[info exists ThisNode] && $ThisNode != ""} {
        set changed 0
        
        if {[$ThisGraph node $ThisNode demand] != $ThisDemand} {
            $ThisGraph node $ThisNode set demand $ThisDemand
            set changed 1
        }

        if {[$ThisGraph node $ThisNode distance] != $ThisDistance} {
            $ThisGraph node $ThisNode set distance $ThisDistance
            set changed 1
        }

        if {[$ThisGraph node $ThisNode potential] != $ThisPotential} {
            $ThisGraph node $ThisNode set potential $ThisPotential
            set changed 1
        }

        if {[$ThisGraph node $ThisNode colour] != $ThisColour} {
            $ThisGraph node $ThisNode set colour $ThisColour
            set changed 1
        }

        if {[$ThisGraph node $ThisNode predecessor] != $ThisPredecessor} {
            $ThisGraph node $ThisNode set predecessor $ThisPredecessor
            set changed 1
        }

        if {$changed} {PropagateModifications}
    }
}

proc UploadArcLabels {} {
    global ThisGraph ThisArc ThisUpper ThisLower ThisLength ThisSubgraph
    
    if {[info exists ThisArc] && $ThisArc != "" && \
        $ThisSubgraph<= $ThisUpper && $ThisSubgraph>= $ThisLower
    } {
        set changed 0
        
        if {[$ThisGraph arc [expr {2*$ThisArc}] ucap] != $ThisUpper} {
            $ThisGraph arc [expr {2*$ThisArc}] set ucap $ThisUpper
            set changed 1
        }

        if {[$ThisGraph arc [expr {2*$ThisArc}] lcap] != $ThisLower} {
            $ThisGraph arc [expr {2*$ThisArc}] set lcap $ThisLower
            set changed 1
        }

        if {[$ThisGraph arc [expr {2*$ThisArc}] length] != $ThisLength} {
            $ThisGraph arc [expr {2*$ThisArc}] set length $ThisLength
            set changed 1
        }

        if {[$ThisGraph arc [expr {2*$ThisArc}] subgraph] != $ThisSubgraph} {
            $ThisGraph arc [expr {2*$ThisArc}] set subgraph $ThisSubgraph
            set changed 1
        }

        if {$changed} {PropagateModifications}
    }
}

proc SetColours {} {
    ResetMode "set colours"
    InitEditor
    MakeHelp "(L) decrease  (R) increase colour index"
}

proc SetPredecessors {} {
    ResetMode "set predecessors"
    InitEditor
    MakeHelp "(L) select node"
}

proc RedirectArcs {} {
    if {![goblin solver idle]} {return}

    global ThisGraph

    ResetMode "redirect arcs"
    InitEditor
    if {![$ThisGraph is directed]} {
        MakeHelp "(L) flip direction  (R) enable/disable direction"
    } else {
        MakeHelp "(L) flip direction"
    }
}

proc ExplicitParallels {} {
    if {![goblin solver idle]} {return}

    global Mode ThisGraph
    $ThisGraph generate parallels
    set Mode "display object"
    PropagateModifications
}

proc DeleteObjects {} {
    if {![goblin solver idle]} {return}

    ResetMode "delete objects"
    InitEditor
    MakeHelp "(L) delete object"
}

proc EditIncidences {} {
    if {![goblin solver idle]} {return}

    ResetMode "sort incidences"
    InitEditor
    MakeHelp "(L) select node"
}

proc PropagateModifications {} {
    global Unchanged FileName Log FirstBackup ThisBackup LastBackup \
        CanvasRegion Mode ThisGraph BackupLevel

    if {$ThisBackup==$LastBackup} {
        incr ThisBackup
        incr LastBackup
        if {[expr $LastBackup-$FirstBackup-1]==$BackupLevel} {
            file delete -force $FileName.backup$FirstBackup.gob
            incr FirstBackup
        }
    } else {
        incr ThisBackup
        for {set i $ThisBackup} {$i<$LastBackup} {incr i} {
            file delete -force $FileName.backup$i.gob
        }
    }

    set Unchanged 0
    wm title . "GOBLET: [file tail $FileName] (modified)"
    if {[winfo exists $Log]} {LoadLogFile}
    UpdateSystemInfo

    if {[$ThisGraph is mip] || [$ThisGraph #nodes]>0} {
        $ThisGraph write $FileName.backup$ThisBackup.gob
    }

    if {[winfo exists $CanvasRegion] && \
        ($Mode=="display object" || $Mode=="navigate")} {InitDisplay}
    
    if {[winfo exists $Log]} {LoadLogFile}

    UpdateActiveDialogs
}

proc UndoChanges {} {
    global FirstBackup ThisBackup FileName ThisGraph

    if {$ThisBackup>$FirstBackup} {
        incr ThisBackup -1
        $ThisGraph delete
        goblin read $ThisGraph $FileName.backup$ThisBackup.gob
        InitDisplay
    }
}

proc RedoChanges {} {
    global ThisBackup LastBackup FileName ThisGraph

    if {$ThisBackup<$LastBackup} {
        incr ThisBackup
        $ThisGraph delete
        goblin read $ThisGraph $FileName.backup$ThisBackup.gob
        InitDisplay
    }
}
