#!/usr/bin/env wish

# Conway's Game of Life
# Riivo Talviste
# Tööjaamade tarkvara, 2007

proc makeGrid {} {
	global cellsx
	global cellsy
	global currentState
	global nextState
	
	if {[array exists currentState]} {unset currentState}
	if {[array exists nextState]} {unset nextState}
	
	for {set i 0} {$i < $cellsy} {incr i} {
		for {set j 0} {$j < $cellsx} {incr j} {
			set currentState($i,$j) 0
			set nextState($i,$j) 0
		}
	}
}

proc drawGrid {} {
	global cellsx
	global cellsy
	global cellSize
	global currentState
	
	# kustutame kõik eelmised ruudud:
	foreach w [winfo children .playarea] {
		if {[winfo class $w] eq "Cell"} {
			destroy $w
		}
	}

	for {set i 0} {$i < $cellsy} {incr i} {
		for {set j 0} {$j < $cellsx} {incr j} {
			if {$currentState($i,$j)} {
				set ccol black
			} else {
				set ccol white
			}
			frame .playarea.cell-$i-$j -bg $ccol -borderwidth 1 -relief raised -width $cellSize -height $cellSize -class Cell
			grid .playarea.cell-$i-$j -row $i -column $j
		}
	}
}

proc toggleCellEditing {} {
	global cellsEditable
	#puts "Cell editing: $cellsEditable"
	if {$cellsEditable} {
		bind Cell <ButtonPress> { toggleCell [lindex [split %W "-"] 1] [lindex [split %W "-"] 2] }
	} else {
		bind Cell <ButtonPress> {}
	}
}

proc toggleCell {x y} {
	#puts "Toggle cell: $x $y"
	global currentState
	if {$currentState($x,$y) == 0} {
		set currentState($x,$y) 1
		.playarea.cell-$x-$y configure -bg black
	} else {
		set currentState($x,$y) 0
		.playarea.cell-$x-$y configure -bg white
	}
}

proc getLiveNeighbours {x y} {
	global cellsx
	global cellsy
	global folded
	global currentState
	
	set n 0
	
	if {[set cx [expr {$x-1}]] >= 0 || ($folded && [set cx [expr {$cellsy-1}]] >= 0)} {
		if {[set cy [expr {$y-1}]] >= 0 || ($folded && [set cy [expr {$cellsx-1}]] >= 0)} {
			if {$currentState($cx,$cy) == 1} {incr n}
		}
		if {[set cy $y] >= 0} {
			if {$currentState($cx,$cy) == 1} {incr n}
		}
		if {[set cy [expr {$y+1}]] < $cellsx || ($folded && [set cy 0] >= 0)} {
			if {$currentState($cx,$cy) == 1} {incr n}
		}
	}
	if {[set cx $x] >= 0} {
		if {[set cy [expr {$y-1}]] >= 0 || ($folded && [set cy [expr {$cellsx-1}]] >= 0)} {
			if {$currentState($cx,$cy) == 1} {incr n}
		}
		#if {[set cy $y] >= 0} {
		#	if {$currentState($cx,$cy) == 1} {incr n}
		#}
		if {[set cy [expr {$y+1}]] < $cellsx || ($folded && [set cy 0] >= 0)} {
			if {$currentState($cx,$cy) == 1} {incr n}
		}
	}
	if {[set cx [expr {$x+1}]] < $cellsy || ($folded && [set cx 0] >= 0)} {
		if {[set cy [expr {$y-1}]] >= 0 || ($folded && [set cy [expr {$cellsx-1}]] >= 0)} {
			if {$currentState($cx,$cy) == 1} {incr n}
		}
		if {[set cy $y] >= 0} {
			if {$currentState($cx,$cy) == 1} {incr n}
		}
		if {[set cy [expr {$y+1}]] < $cellsx || ($folded && [set cy 0] >= 0)} {
			if {$currentState($cx,$cy) == 1} {incr n}
		}
	}
	
	#set cx [expr {$x-1}]
	#set cy [expr {$y-1}]
	#if {$cx >= 0 && $cy >= 0 && $currentState($cx,$cy) == 1} {incr n}
	#set cy [expr {$y-0}]
	#if {$cx >= 0 && $cy >= 0 && $currentState($cx,$cy) == 1} {incr n}
	#set cy [expr {$y+1}]
	#if {$cx >= 0 && $cy < $cellsy && $currentState($cx,$cy) == 1} {incr n}
	
	#set cx [expr {$x-0}]
	#set cy [expr {$y-1}]
	#if {$cx >= 0 && $cy >= 0 && $currentState($cx,$cy) == 1} {incr n}
	##set cy [expr {$y-0}] # cell ise
	##if {$cx >= 0 && $cy >= 0 && $currentState($cx,$cy) == 1} {incr n}
	#set cy [expr {$y+1}]
	#if {$cx >= 0 && $cy < $cellsy && $currentState($cx,$cy) == 1} {incr n}

	#set cx [expr {$x+1}]
	#set cy [expr {$y-1}]
	#if {$cx < $cellsx && $cy >= 0 && $currentState($cx,$cy) == 1} {incr n}
	#set cy [expr {$y-0}]
	#if {$cx < $cellsx && $cy >= 0 && $currentState($cx,$cy) == 1} {incr n}
	#set cy [expr {$y+1}]
	#if {$cx < $cellsx && $cy < $cellsy && $currentState($cx,$cy) == 1} {incr n}
	
	return $n
}

proc play {} {
	global autoPlay
	global cellsx
	global cellsy
	
	if {[expr {100000/($cellsx*$cellsy)}] < 500} {
		set sleep [expr {100000/($cellsx*$cellsy)}]
	} else {
		set sleep 500
	}
	
	while {$autoPlay} {
		update
		tick
		after $sleep
	}
}

proc tick {} {
	global cellsx
	global cellsy
	global step
	global currentState
	global nextState

	#puts "Tick!"
	
	# Arvutame järgmise seisu:
	for {set i 0} {$i < $cellsy} {incr i} {
		for {set j 0} {$j < $cellsx} {incr j} {
			set neighbours [getLiveNeighbours $i $j]
			
			if {$neighbours < 2 || $neighbours > 3} {
				set nextState($i,$j) 0
				#puts "($i; $j) N:$neighbours 0"
			} elseif {$neighbours == 3} {
				set nextState($i,$j) 1
				#puts "($i; $j) N:$neighbours 1"
			} else {
				set nextState($i,$j) $currentState($i,$j)
				#puts "($i; $j) N:$neighbours $currentState($i,$j)"
			}
			
			# Uuendame mänguvälja:
			if {$nextState($i,$j) == 1} {
				.playarea.cell-$i-$j configure -bg black
			} else {
				.playarea.cell-$i-$j configure -bg white
			}
		}
	}
	
	array set currentState [array get nextState]
	
	# Uuendame sammu andmeid:
	incr step
	.controls.step configure -text "Aeg: $step"
}

proc loadFile {} {
	global cells
	global cellsx
	global cellsy
	global folded
	global currentState
	global nextState
	global step

	set types {
		{{Conway's Game of Life Files} {.cgol}}
		{{All Files} {*}}
	}
	set inFile [tk_getOpenFile -title "Lae konfiguratsioon" -filetypes $types]
	
	if {[string length $inFile] == 0} {return}
	
	if {[file readable $inFile]} {
		set aFile [open $inFile r]
		set data [read -nonewline $aFile]
		close $aFile
		
		# parse data:
		set lines [split $data "\n"]
		set firstline [split [lindex $lines 0] " "]
		
		set cellsx [lindex $firstline 0]
		set cellsy [lindex $firstline 1]
		set folded [lindex $firstline 2]
		
		if {$cellsx == $cellsy} {set cells $cellsx}
		
		makeGrid
		
		foreach point $lines {
			if {[llength $point] == 2} {
				set currentState([lindex $point 0],[lindex $point 1]) 1
			}
		}
		
		drawGrid
		
		set step 0
		.controls.step configure -text "Aeg: $step"
	} else {
		puts "Cannot open `$inFile' for reading!"
	}
}

proc saveFile {} {
	global cellsx
	global cellsy
	global folded
	global currentState

	set types {
		{{Conway's Game of Life Files} {.cgol}}
		{{All Files} {*}}
	}
	set outFile [tk_getSaveFile -title "Salvesta konfiguratsioon" -filetypes $types]
	
	if {[string length $outFile] == 0} {return}
	
	if {[file writable $outFile] || ![file exists $outFile]} {
		set aFile [open $outFile w]
		
		puts $aFile "$cellsx $cellsy $folded"
		
		for {set i 0} {$i < $cellsy} {incr i} {
			for {set j 0} {$j < $cellsx} {incr j} {
				if {$currentState($i,$j) == 1} {
					puts $aFile "$i $j"
				}
			}
		}
		close $aFile
		
	} else {
		puts "Cannot open `$outFile' for writing!"
	}
}

# Algväärtustamine
set cellSize 10
set cells 30
set cellsx $cells
set cellsy $cells
set folded 0
set step 0

# Peaaken
wm geometry . =600x400
wm title . "Conway's Game of Life"

# Menüü
menu .menubar
. config -menu .menubar

menu .menubar.menuFile -tearoff 0
.menubar add cascade -label Fail -menu .menubar.menuFile

.menubar.menuFile add command -label "Lae seis..." -command {loadFile}
.menubar.menuFile add command -label "Salvesta seis..." -command {saveFile}
.menubar.menuFile add sep
.menubar.menuFile add command -label Välju -command exit

menu .menubar.menuGame -tearoff 0
.menubar add cascade -label Mäng -menu .menubar.menuGame

menu .menubar.menuGame.controls -tearoff 0
.menubar.menuGame add cascade -label Kontrollid -menu .menubar.menuGame.controls

.menubar.menuGame.controls add check -label Arene -variable autoPlay -command {if {$autoPlay} {play}}
.menubar.menuGame.controls add command -label "Samm >>" -command {tick}
.menubar.menuGame.controls add sep
.menubar.menuGame.controls add command -label "Aeg nulli" -command {set step 0; .controls.step configure -text "Aeg: $step"}

menu .menubar.menuGame.grid -tearoff 0
.menubar.menuGame add cascade -label Mänguväli -menu .menubar.menuGame.grid

.menubar.menuGame.grid add check -label Redigeeri -variable cellsEditable -command {toggleCellEditing}
.menubar.menuGame.grid add check -label "Voldi kokku" -variable folded

menu .menubar.menuGame.grid.size -tearoff 0
.menubar.menuGame.grid add cascade -label Mõõtmed -menu .menubar.menuGame.grid.size

.menubar.menuGame.grid.size add radio -label 10x10 -variable cells -value 10 -command {set cellsx $cells; set cellsy $cells; makeGrid; drawGrid}
.menubar.menuGame.grid.size add radio -label 20x20 -variable cells -value 20 -command {set cellsx $cells; set cellsy $cells; makeGrid; drawGrid}
.menubar.menuGame.grid.size add radio -label 30x30 -variable cells -value 30 -command {set cellsx $cells; set cellsy $cells; makeGrid; drawGrid}
.menubar.menuGame.grid.size add radio -label 40x40 -variable cells -value 40 -command {set cellsx $cells; set cellsy $cells; makeGrid; drawGrid}
.menubar.menuGame.grid.size add radio -label 50x50 -variable cells -value 50 -command {set cellsx $cells; set cellsy $cells; makeGrid; drawGrid}
.menubar.menuGame.grid.size add radio -label 60x60 -variable cells -value 60 -command {set cellsx $cells; set cellsy $cells; makeGrid; drawGrid}

menu .menubar.menuInfo -tearoff 0
.menubar add cascade -label Info -menu .menubar.menuInfo

.menubar.menuInfo add command -label Programmist -command {tk_messageBox -message "Conway Elumäng\nConway's Game of Life\n\nRiivo Talviste, 2007" -type ok}

# Kontrollpaneel
frame .controls -width 160 -bd 2 -relief groove -bg #3b6783

label .controls.step -text "Aeg: $step"
checkbutton .controls.changeCells -text Redigeeri -relief raised -variable cellsEditable -command {toggleCellEditing}
checkbutton .controls.foldArea -text "Voldi kokku" -relief raised -variable folded
checkbutton .controls.play -text Arene! -relief raised -variable autoPlay -command {if {$autoPlay} {play}}
button .controls.tick -text "Samm >>" -command {tick}

# Mänguväli
frame .playarea -bg #678fa9

makeGrid
drawGrid

# Paigutamine
pack .playarea -side left -fill both -expand 1
pack .controls -side right -fill y
pack .controls.step -side top -fill x
pack .controls.changeCells -side top -fill x -pady {10 0}
pack .controls.foldArea -side top -fill x
pack .controls.play -side top -fill x -pady {10 0}
pack .controls.tick -side top -fill x