namespace eval jigsaw-wizard {
    variable Data

    set Data(title) "Jigsaw Puzzler Creation Wizard"
    set Data(initialDir) ""
    set Data(filePath) ""
    set Data(fileTail) ""
    set Data(num,x) 5
    set Data(num,y) 4
    set Data(num,total) 20
    set Data(shape,1) 40
    set Data(shape,2) 10
    set Data(shape,3) 80
    set Data(polyColor) black
    set Data(showImage) 0
    set Data(progWin) .prog
}

###################################################
# jigsaw-wizard::updatePieceTotal
#
# Description: Count the pieces
#
# Parameters:  
###################################################
proc jigsaw-wizard::updatePieceTotal {} {
    variable Data

    if { "$Data(num,x)" eq "" || "$Data(num,y)" eq "" } {
        set Data(num,total) 0
    } else {
        set Data(num,total) [expr {$Data(num,x) * $Data(num,y)}]
    }
}
 
proc jigsaw-wizard::canvasToImage { c fileName } {
    set region [$c cget -scrollregion]
    set xsize [lindex $region 2]
    set ysize [lindex $region 3]
    set img [image create photo -width $xsize -height $ysize]
    $c xview moveto 0
    $c yview moveto 0
    update idletasks
    set xr 0.0
    set yr 0.0
    set px 0
    set py 0
    while { $xr < 1.0 } {
        while { $yr < 1.0 } {
            set tmpImg [image create photo -format window -data $c]
            $img copy $tmpImg -to $px $py
            image delete $tmpImg
            set yr [lindex [$c yview] 1]
            $c yview moveto $yr
            set py [expr round ($ysize * [lindex [$c yview] 0])]
            update idletasks
        }
        $c yview moveto 0
        set yr 0.0
        set py 0

        set xr [lindex [$c xview] 1]
        $c xview moveto $xr
        set px [expr round ($xsize * [lindex [$c xview] 0])]
        update idletasks
    }
    $img write $fileName -format PNG
    image delete $img
}

proc jigsaw-wizard::saveAsImage {} {
    variable Data

    set fileTypes {
        { {PNG files} {.png} }
    }

    set filePath [tk_getSaveFile \
        -filetypes $fileTypes \
        -title "Select image file" \
        -confirmoverwrite true \
        -initialfile "PuzzleCut.png"]

    if { $filePath ne "" } {
        canvasToImage $Data(w:c) $filePath
    }
}


proc jigsaw-wizard::selectPolyColor {} {
    variable Data

    set newCol [tk_chooseColor -title "Select shape outline color" \
               -initialcolor $Data(polyColor)]
   if { $newCol ne "" } {
        set Data(polyColor) $newCol
        drawNet
    }
}

proc jigsaw-wizard::toggleImage {} {
    variable Data

    if { $Data(showImage) } {
        $Data(w:c) create image 0 0 -anchor nw -image $Data(image) -tags [list preview]
    } else {
        $Data(w:c) delete preview
    }
    $Data(w:c) raise poly
}

###################################################
# jigsaw-wizard::updateImage
#
# Description: Update the image
#
# Parameters:  path - path to create
###################################################
proc jigsaw-wizard::updateImage {} {
    variable Data

    set w [image width $Data(image)]
    set h [image height $Data(image)]

    toggleImage

    jigsaw-wizard::resizeCanvas $Data(w:c) $w $h
}

###################################################
# jigsaw-wizard::pickJigsawImage
#
# Description: Pick an image to use for the jigsaw
#
# Parameters:
###################################################
proc jigsaw-wizard::pickJigsawImage {} {
    variable Data

    if {[set Data(image) [pickImage $Data(filePath)]] ne ""} {
        set Data(filePath) [$Data(image) cget -file]
        set Data(fileTail) [file tail $Data(filePath)]
        set Data(transparency) 0.5

        updateImage

        drawNet
        $Data(w:createButton)      configure -state normal
        $Data(w:showImageButton)   configure -state normal
        $Data(w:polyColorButton)   configure -state normal
        catch { $Data(w:saveAsImageButton) configure -state normal }

        jigsaw-play::createBackgroundImage $Data(filePath) $Data(transparency)
        jigsaw-play::showBackgroundImage
    }
}
    
###################################################
# jigsaw-wizard::pickImage
#
# Description: Pick an image
#
# Parameters:
###################################################
proc jigsaw-wizard::pickImage {initialFile} {
    variable Data

    set types {
        {{PNG files} {.png} }
    }

    set cont true

    while { $cont == true } {
        set filePath [tk_getOpenFile -filetypes $types \
                -parent $Data(w:top) \
                -title "Select image file" \
                -initialdir $Data(initialDir) \
                -initialfile $initialFile]

        if { "$filePath" ne "" } {
            # Record directory for next time
            set Data(initialDir) [file dirname $filePath]

            if {[catch {image create photo -file $filePath} result]} {
                # Could not read image file
                set response [tk_messageBox -type ok \
                        -icon error \
                        -title "Image File Error" \
                        -parent $Data(w:top) \
                        -message "$result"]
            } else {
                # Image file OK
                set cont false
                return $result
            }
        } else {
            # User cancelled dialog
            set cont false
        }
    }
    return ""
}

###################################################
# jigsaw-wizard::drawNet
#
# Description: Build a window of piece controls
#
# Parameters:
#
###################################################
proc jigsaw-wizard::drawNet {} {
    variable Data

    if { $Data(image) ne "" } {
        jigsaw-create::createNet $Data(w:c) $Data(num,x) $Data(num,y) \
            $Data(image) $Data(shape,1) $Data(shape,2) $Data(shape,3) $Data(polyColor)

        if { $Data(num,total) > 1 } { 
            $Data(w:createButton) configure -state normal
        } else {
            $Data(w:createButton) configure -state disabled
        }
    }
}

###################################################
# jigsaw-wizard::cutProgress
#
# Description: Show the progress of the jigsaw cutting
#
# Parameters:
#
###################################################
proc jigsaw-wizard::cutProgress {w args} {
    variable Data
    
    set win $Data(progWin)
    set text $win.text
    set prog $win.prog
    set total $Data(num,total)

    if { ! [winfo exists $win] } {
        toplevel $win
        wm title $win "Creating Jigsaw ..."
        wm transient $win $w
        wm resizable $win 0 0

        wm withdraw $win

        wm protocol $win WM_DELETE_WINDOW [list]

        ttk::label $text -width 30 -anchor w
        ttk::progressbar $prog -maximum 100
        grid $text -sticky w
        grid $prog -sticky ew

        # This is needed before the geometry can be set up
        update idletasks       
        jigsaw-main::setGeometry $win
        wm deiconify $win

        $text configure -text "Cutting Jigsaw: $::cutProgress/$total"
    }

    set percentCut [expr {int((100*$::cutProgress) / $total)}]
    $prog configure -value $percentCut
    $text configure -text "Cutting Jigsaw: $::cutProgress/$total"
}

###################################################
# jigsaw-wizard::cutJigsaw
#
# Description: Cut the jigsaw
#
# Parameters:  c - canvas path
#
###################################################
proc jigsaw-wizard::cutJigsaw {c} {
    variable Data
    global playdata

    # Must first empty playdata
    array unset playdata

    set top [winfo toplevel $c]

    set ::cutProgress 0
    trace add variable ::cutProgress write [list jigsaw-wizard::cutProgress $top]
 
    # Disable all widgets in wizard
    jigsaw-main::stateChange $Data(w:top) record
    jigsaw-main::stateChange $Data(w:top) disabled
 
    set ::cutProgress 0
    update

    jigsaw-create::makePuzzle $c ::cutProgress playdata

    set playdata(image) $Data(image)
    set playdata(filePath) $Data(filePath)
    set playdata(transparency) $Data(transparency)

    jigsaw-main::unsetFilePath [jigsaw-main::getToplevel]
    jigsaw-play::startJigsaw playdata [jigsaw-main::getCanvas]
 
    jigsaw-main::updateToolbar 1
 
    catch {destroy $Data(progWin)}
    jigsaw-wizard::closeWizard
}

###################################################
# jigsaw-wizard::scheduleDrawNet
#
# Description: Used to schedule a redraw of the net.
#              It waits a little before redrawing the
#              net so as to prevent redraws occurring
#              too often
#
# Parameters:  
#
###################################################
proc jigsaw-wizard::scheduleDrawNet {args} {
    variable Data

    catch {after cancel {jigsaw-wizard::drawNet}}

    if { $Data(num,x) > 0 && $Data(num,x) < 60 && \
         $Data(num,y) > 0 && $Data(num,y) < 60} {
        after 250 {jigsaw-wizard::drawNet}
    }
}

###################################################
# jigsaw-wizard::selectPieces
#
# Description: Piece selection controls
#
# Parameters:  c - canvas path
#              w - window in which to build controls
#
###################################################
proc jigsaw-wizard::selectPieces {c w} {
    variable Data
 
    $c configure -bg white
    
    # Select buttons frame.
    set buf $w.buf
    ttk::frame $buf

    ttk::button $buf.change -text "Select puzzle image ..." \
            -command "jigsaw-wizard::pickJigsawImage"
    pack $buf.change -expand true -fill x

    ttk::button $buf.create -text "Create puzzle" \
        -command "jigsaw-wizard::cutJigsaw $c" -state disabled
    pack $buf.create -expand true -fill x
    set Data(w:createButton) $buf.create

    ttk::button $buf.cancel -text "Cancel wizard" \
        -command "jigsaw-wizard::cancelWizard"
    pack $buf.cancel -expand true -fill x

    # Display options frame
    set dof $w.dof
    ttk::labelframe $dof -text " Display options"

    ttk::checkbutton $dof.showImage -text "Show puzzle image" \
        -variable jigsaw-wizard::Data(showImage) -command jigsaw-wizard::toggleImage \
        -state disabled
    pack $dof.showImage -expand true -fill x
    set Data(w:showImageButton) $dof.showImage

    ttk::button $dof.polyColor -text "Select shape outline color ..." \
            -command "jigsaw-wizard::selectPolyColor" -state disabled
    pack $dof.polyColor -expand true -fill x
    set Data(w:polyColorButton) $dof.polyColor


    set retVal [catch {package require Img} version]
    if { $retVal == 0 } {
        ttk::button $dof.saveAsImg -text "Save as image file ..." \
                -command "jigsaw-wizard::saveAsImage" -state disabled
        pack $dof.saveAsImg -expand true -fill x
        set Data(w:saveAsImageButton) $dof.saveAsImg
    }

    # Number of pieces frame
    set pnf $w.pnf
    ttk::labelframe $pnf -text " Number of pieces"
 
    ttk::label $pnf.hl -text "Horizontal : " \
            -width 16 \
            -anchor e
    spinbox $pnf.hs -from 2 -to 60 \
            -width 2 \
            -textvariable ::jigsaw-wizard::Data(num,x) \
            -command "jigsaw-wizard::updatePieceTotal;jigsaw-wizard::scheduleDrawNet" \
            -validate key \
            -vcmd {string is integer %P} \
            -wrap 0
    bind $pnf.hs <KeyRelease> "jigsaw-wizard::updatePieceTotal;jigsaw-wizard::scheduleDrawNet"
 
    ttk::label $pnf.vl -text "Vertical : " \
            -width 16 \
            -anchor e

    spinbox $pnf.vs -from 2 -to 60 \
            -width 2 \
            -textvariable ::jigsaw-wizard::Data(num,y) \
            -command "jigsaw-wizard::updatePieceTotal;jigsaw-wizard::scheduleDrawNet" \
            -validate key \
            -vcmd {string is integer %P} \
            -wrap 0
    bind $pnf.vs <KeyRelease> "jigsaw-wizard::updatePieceTotal;jigsaw-wizard::scheduleDrawNet"

    ttk::label $pnf.tl -text "Total Pieces : " \
            -width 16 \
            -anchor e

    ttk::label $pnf.tv -textvariable ::jigsaw-wizard::Data(num,total) \
            -relief sunken -width 4 \
            -anchor w

    grid $pnf.hl $pnf.hs -sticky ew -padx 5 -pady {2 0}
    grid $pnf.vl $pnf.vs -sticky ew -padx 5 -pady 2
    grid $pnf.tl $pnf.tv -sticky ew -padx 5 -pady {0 2}
    
    # Puzzle Piece Shape
    set psf $w.psf
    ttk::labelframe $psf -text " Puzzle piece shape"
 
    ttk::label $psf.t1 -text "Hole positions:" -anchor w
    ttk::scale $psf.s1 -orient horizontal \
            -from 0 -to 100 \
            -variable ::jigsaw-wizard::Data(shape,1) \
            -command "jigsaw-wizard::scheduleDrawNet"
    $psf.s1 set $Data(shape,1)
    ttk::label $psf.l1 -text "regular" -anchor w
    ttk::label $psf.r1 -text "random" -anchor e

    ttk::label $psf.t2 -text "Edges:" -anchor w
    ttk::scale $psf.s2 -orient horizontal \
            -from 0 -to 100 \
            -variable ::jigsaw-wizard::Data(shape,2) \
            -command "jigsaw-wizard::scheduleDrawNet"
    $psf.s2 set $Data(shape,2)
    ttk::label $psf.l2 -text "regular" -anchor s
    ttk::label $psf.r2 -text "random" -anchor s

    ttk::label $psf.t3 -text "Hole size:" -anchor w
    ttk::scale $psf.s3 -orient horizontal \
            -from 0 -to 100 \
            -variable ::jigsaw-wizard::Data(shape,3) \
            -command "jigsaw-wizard::scheduleDrawNet"
    $psf.s3 set $Data(shape,3)
    ttk::label $psf.l3 -text "small" -anchor s
    ttk::label $psf.r3 -text "large" -anchor s
 
    grid $psf.t1 - -sticky ew -padx 5 -pady {2 0}
    grid $psf.l1 -row 1 -column 0 -sticky w -padx 5
    grid $psf.r1 -row 1 -column 1 -sticky e -padx 5
    grid $psf.s1 - -sticky ew -padx 5 -pady 0
 
    grid $psf.t2 - -sticky ew -padx 5 -pady {2 0}
    grid $psf.l2 -row 4 -column 0 -sticky w -padx 5
    grid $psf.r2 -row 4 -column 1 -sticky e -padx 5
    grid $psf.s2 - -sticky ew -padx 5 -pady 0
 
    grid $psf.t3 - -sticky ew -padx 5 -pady {2 0}
    grid $psf.l3 -row 7 -column 0 -sticky w -padx 5
    grid $psf.r3 -row 7 -column 1 -sticky e -padx 5
    grid $psf.s3 - -sticky ew -padx 5 -pady 0

    grid columnconfig $psf 0 -weight 1
    grid columnconfig $psf 1 -weight 1
 
    # Buttons
    
    # Pack the controls
    grid $buf  - -sticky news -padx 2 -pady {10 0}
    grid $dof  - -sticky news -padx 2 -pady {10 0}
    grid $pnf  - -sticky news -padx 2 -pady {10 0}
    grid $psf  - -sticky news -padx 2 -pady {10 0}

    grid columnconfigure $w 0 -weight 1
}

###################################################
# jigsaw-wizard::resizeCanvas
#
# Description: Resize the canvas in the wizard
#
# Parameters: c width height
###################################################
proc jigsaw-wizard::resizeCanvas {c width height} {
    
    $c configure -width $width -height $height \
            -scrollregion "0 0 $width $height"
}

###################################################
# jigsaw-wizard::createWizard
#
# Description: Build a jigsaw creation wizard
#
# Parameters:  parent - Wizard will be transient of <parent>
###################################################
proc jigsaw-wizard::createWizard {parent} {
    variable Data

    set Data(fileTail) ""
    set Data(image)    ""

    set win .wizard
    set Data(w:top) $win

    if {[winfo exists $win]} {
        destroy $win
    }
    
    toplevel $win
    wm transient $win $parent

    wm withdraw $win

    wm title $win "$Data(title)"
    wm minsize $win 800 600
    
    wm protocol $win WM_DELETE_WINDOW [list jigsaw-wizard::cancelWizard]

    set winfr [ttk::frame $win.fr]
    pack $winfr -expand true -fill both

    set vsep [ttk::separator $winfr.vsep -orient vertical]
    set ctrl [ttk::frame $winfr.ctrl -width 120]
    set f [ttk::frame $winfr.f]
 
    grid $f -row 0 -column 0 -sticky news -padx 5 -pady 5
    grid $vsep -row 0 -column 1 -sticky ns -padx 0 -pady 5
    grid $ctrl -row 0 -column 2 -sticky news -padx 5 -pady 5
    grid columnconfigure $winfr 0 -weight 1
    grid columnconfigure $winfr 2 -minsize 170
    grid rowconfigure $winfr 0 -weight 1

    set hs $f.hs
    set vs $f.vs
    set c $f.c

    scrollbar $hs -orient horiz -command "$c xview"
    scrollbar $vs -command "$c yview"
 
    canvas $c -bg white \
            -highlightthickness 0 \
            -xscrollcommand "$hs set" \
            -yscrollcommand "$vs set"
 
    grid $c  -row 0 -column 0 -sticky {}
    grid $vs -row 0 -column 1 -sticky ns
    grid $hs -row 1 -column 0 -sticky ew
    grid rowconfigure    $f 0 -weight 1
    grid columnconfigure $f 0 -weight 1

    set Data(w:c) $c

    # Scrollbars only appear when needed
    ::autoscroll::autoscroll $hs
    ::autoscroll::autoscroll $vs
    resizeCanvas $c 400 300
 
    selectPieces $c $ctrl

    update idletasks
    jigsaw-main::setGeometry $win
    wm deiconify $win

    update
}

###################################################
# jigsaw-wizard::closeWizard
#
# Close down gracefully
###################################################
proc jigsaw-wizard::closeWizard {} {
    variable Data

    destroy $Data(w:top)
}

###################################################
# jigsaw-wizard::cancelWizard
#
# Close down gracefully
###################################################
proc jigsaw-wizard::cancelWizard {} {
    variable Data
 
    catch {image delete $Data(image)}

    closeWizard
}
