package require Tk 8

# include abctcl interface
source abctcl.tcl



# indices of commands in following two lists must correspond

# list of commands using native ABC notation - displayed in command window
set abcCmdList {
	"print_stats"
	"print_latch"
	"print_auto"
	"print_fanio"
	"print_gates"

	"print_symm"
	"print_unate"
	"read"
	"sec"
	"cec"

	"dsec"
	"dprove"
	"iprove"
	"prove"
	"sat"

	"time"

	"resyn"
}

# list of commands in abctcl notation - executable in script
set tclCmdList {
	"abc_print_stats outputData"
	"abc_print_latch outputData"
	"abc_print_auto outputData"
	"abc_print_fanio outputData"
	"abc_print_gates outputData"

	"abc_print_symm outputData"
	"abc_print_unate outputData"
	"abc_read outputData"
	"abc_sec outputData"
	"abc_cec outputData"

	"abc_dsec outputData"
	"abc_dprove outputData"
	"abc_iprove outputData"
	"abc_prove outputData"
	"abc_sat outputData"

	"abc_time outputData"

	"abcAnyCommand {resyn} outputData"
}


# load commands list
if {[catch {source commands.tcl}]} {
	puts "File 'commands.tcl not found, using internal defaults."
}



# supported file types - list in format acceptable by tk_getOpenFile
set loadfiletypes {
	{{ABC supported file types} {.aig .baf .bench .blif .eqn .pla .v} }
	{{AIGER file}   {.aig}}
	{{BAF file}     {.baf}}
	{{BENCH file}   {.bench}}
	{{BLIF file}    {.blif}}
	{{EQN file}     {.eqn}}
	{{PLA file}     {.pla}}
	{{VERILOG file} {.v}}
}


# supported file types - list in format acceptable by tk_getSaveFile
set savefiletypes {
	{{AIGER file}   {.aig}}
	{{BAF file}     {.baf}}
	{{BENCH file}   {.bench}}
	{{BLIF file}    {.blif}}
	{{CNF file}     {.cnf}}
	{{DOT file}     {.dot}}
	{{EQN file}     {.eqn}}
	{{GML file}     {.gml}}
	{{PLA file}     {.pla}}
	{{VERILOG file} {.v}}
}


# following variables are standart values, actual values are loaded from
# config.tcl configuration script

# state variables for output windows
set hideNulls(A) 0
set hideRawOutput(A) 1
set hideNulls(B) 0
set hideRawOutput(B) 1
set hideNulls(C) 0
set hideRawOutput(C) 1
set hideNulls(D) 0
set hideRawOutput(D) 1
set hideNulls(E) 0
set hideRawOutput(E) 1

# array of executed commands
set cmdexecuted(A) ""
set cmdexecuted(B) ""
set cmdexecuted(C) ""
set cmdexecuted(D) ""
set cmdexecuted(E) ""

# array of visibility of output windows
set winVisible(A) 1
set winVisible(B) 1
set winVisible(C) 1
set winVisible(D) 0
set winVisible(E) 0
# and other windows
set winVisible(command) 1
set winVisible(console) 1

# geometry of windows
set winGeometry(A) 478x350+268+0
set winGeometry(B) 518x350+754+0
set winGeometry(C) 518x312+754+384
set winGeometry(D) 478x312+268+384
set winGeometry(E) 518x312+754+384
set winGeometry(.) 163x238+0+0
set winGeometry(command) 260x426+0+270
set winGeometry(console) 57x18+267+384



# state variables for command window

# command entry variable
set cmdwincmd [lindex $tclCmdList 0]
# indicates to which output window should be the output directed - connected with
# radio buttons in command window
set cmdwinOutChoice A

# state variables for main window
# abc status changed according to ABCbusy variable
set abcstatus "initializing ABC..."



# three widget procedures from book Practical programming in TCL and Tk

#
# Example 33-1
# A text widget and two scrollbars.
proc Scrolled_Text { f args } {
	frame $f
	
	eval {text $f.text -wrap none \
		-xscrollcommand [list $f.xscroll set] \
		-yscrollcommand [list $f.yscroll set]} $args
	
	scrollbar $f.xscroll -orient horizontal	-command [list $f.text xview]
	scrollbar $f.yscroll -orient vertical	-command [list $f.text yview]

	grid $f.text $f.yscroll -sticky news
	grid $f.xscroll -sticky news
	grid rowconfigure $f 0 -weight 1
	grid columnconfigure $f 0 -weight 1
	return $f.text
}



#
# Example 33-2
# Scroll_Set manages optional scrollbars.
proc Scroll_Set {scrollbar geoCmd offset size} {
	if {$offset != 0.0 || $size != 1.0} {
		eval $geoCmd					;# Make sure it is visible
	}
	$scrollbar set $offset $size
} 




#
# Example 33-3
# Listbox with optional scrollbars.
proc Scrolled_Listbox { f args } {

	frame $f

	listbox $f.list \
		-xscrollcommand [list Scroll_Set $f.xscroll	[list grid $f.xscroll -row 1 -column 0 -sticky we]] \
		-yscrollcommand [list Scroll_Set $f.yscroll	[list grid $f.yscroll -row 0 -column 1 -sticky ns]]

	eval {$f.list configure} $args

	scrollbar $f.xscroll -orient horizontal	-command [list $f.list xview]
	scrollbar $f.yscroll -orient vertical	-command [list $f.list yview]

	grid $f.list -sticky news
	grid rowconfigure $f 0 -weight 1
	grid columnconfigure $f 0 -weight 1

	return $f.list
}




# executes command cmdexecuted($id)
proc executeCommand { id } {
	# global outputDataA-E
	eval [format "global outputData%s" $id]
	global outwintext1
	global outwinlabel1
	global tclCmdList    	;# list of commands
	global cmdexecuted
	global winVisible
	global abcstatus


	set winVisible($id) 1
	winSetVisibility $id
	
	$outwintext1($id) delete 1.0 end
	$outwintext1($id) insert end "waiting for output..."

	$outwinlabel1($id) configure -text "Executed command: $cmdexecuted($id)"
	set abcstatus "ABC busy ..."

	eval [format "array unset outputData%s" $id]
	eval $cmdexecuted($id)

	set abcstatus "ABC ready"

	reprintOutputWindow $id
}




# reprints output window according to print attributes
# gets data from outputData$id array
# outputData$id variable names are constructed on the run and executed using eval
proc reprintOutputWindow { id } {
	# global outputDataA-E
	eval [format "global outputData%s" $id]
	global outwintext1
	global outwinlabel1
	global cmdexecuted
	global hideNulls
	global hideRawOutput

	# command label
	$outwinlabel1($id) configure -text "Executed command: $cmdexecuted($id)"

	$outwintext1($id) delete 1.0 end

	# set arr_exists [array exists outputDataA-E]
	eval [format {set arr_exists [array exists outputData%s]} $id]

	if {$arr_exists} {
		
		# set arr_names [array names outputDataA-E]
		eval [format {set arr_names [array names outputData%s]} $id]

		# print each element of an array
		# success in the beginning
		if {[lsearch $arr_names "success"] != -1} {
			# set tmp "success: $outputDataA-E(success)\n"
			eval [format {set tmp "success: $outputData%s(success)\n"} $id]
			$outwintext1($id) insert end $tmp
		}

		foreach name $arr_names {
			if {$name == "rawOutput" || $name == "success"} {
				continue
			}
			# set elemVal $outputDataA-E($name)
			eval [format {set elemVal $outputData%s($name)} $id]

			if {$elemVal == "null" && $hideNulls($id) == 1} {
				continue
			}
			$outwintext1($id) insert end "$name: $elemVal\n"
		}

		# rawOutput at the end
		if {[lsearch $arr_names "rawOutput"] != -1 && $hideRawOutput($id) == 0} {
			# set elemVal $outputDataA-E(rawOutput)
			eval [format {set elemVal $outputData%s(rawOutput)} $id]
			
			if {$elemVal == "null" && $hideNulls($id) == 1} {
			} else {
				$outwintext1($id) insert end "rawOutput: $elemVal\n"
			}
		}
	}
}





# creates new output window
# all associated global variables are arrays indexed with 'id' - more output
# windows are possible
proc createOutputWindow {id} {
	global outwin
	global outwintext1
	global outwinlabel1

	global hideNulls
	global hideRawOutput
	global winVisible

	set outwin($id) [toplevel .outputWindow$id -width 400 -height 300]
	wm title $outwin($id) "Output window $id"
	wm minsize $outwin($id) 211 139

	# set on close event
	set onCloseProc "set winVisible($id) 0; winSetVisibility $id"
	wm protocol $outwin($id) WM_DELETE_WINDOW $onCloseProc

	set outwinlabel1($id) [label $outwin($id).label1 -text "Executed command:"]
	set outwintext1($id) [Scrolled_Text $outwin($id).text1 -width 40 -height 1 -font TkTextFont]

	set reprintProc "reprintOutputWindow $id"

	set frameout [frame $outwin($id).frameout]
	set framein [frame $frameout.framein]
	
	set outwincheck1 [checkbutton $framein.check1 -text "hide null values" -variable hideNulls($id) -command $reprintProc]
	$outwincheck1 deselect
	set outwincheck2 [checkbutton $framein.check2 -text "hide rawOutput" -variable hideRawOutput($id) -command $reprintProc]
	$outwincheck2 select

	set outwinbutton1 [button $frameout.button1 -text "Execute again"]

	pack $outwin($id).label1 -side top -anchor w -pady 2
	pack $outwin($id).text1 -side top -fill both -expand true

	pack $frameout -side bottom -expand false -fill x
	pack $framein -side left -pady 1 -anchor w ;# -expand true -fill x
	pack $outwincheck1 -side top -anchor w
	pack $outwincheck2 -side top -anchor w

	pack $outwinbutton1 -side right -anchor se -padx 5 -pady 5

	# event bindings
	# button1 click - execute command
	set execProc "executeCommand $id"
	bind $outwinbutton1 <B1-ButtonRelease-1> $execProc


	reprintOutputWindow $id


	return $outwin($id)
}



# refresh entry1 in command window
# set text to selected command with appropriate output window
proc cmdwinEntry1Refresh {} {
	global cmdwinlist1
	global tclCmdList
	global cmdwinOutChoice
	global cmdwincmd

	set cmdwinListIndex [$cmdwinlist1 curselection]
	if {$cmdwinListIndex == {}} {
		set cmdwinListIndex 0
	}

	set cmd [lindex $tclCmdList $cmdwinListIndex]
	if {[string first "-" $cmd 0] == 0} {
		# first character is a separator not a command
	} else {
		# put a command in entry1
		set cmdwincmd [lindex $tclCmdList $cmdwinListIndex]
		append cmdwincmd $cmdwinOutChoice
	}
}



# creates command window and its widgets
proc createCommandWindow {} {
	global cmdList
	global cmdwin
	global cmdwinlist1
	global cmdwinentry1
	global cmdwincmd
	global abcCmdList
	global tclCmdList
	global cmdwinOutChoice

	# open new window
	set cmdwin [toplevel .commandWindow]
	wm title $cmdwin "Command window"

	# set on close event
	set onCloseProc "set winVisible(command) 0; winSetVisibility command"
	wm protocol $cmdwin WM_DELETE_WINDOW $onCloseProc

	set cmdwinlist1 [Scrolled_Listbox $cmdwin.list1 -width 50 -height 1]

	# put command data into listbox
	foreach cmd $abcCmdList {
		$cmdwinlist1 insert end $cmd
	}


	# main frame on bottom of the window
	set frame1 [frame $cmdwin.frame1]

	# two frames in frame1 ordered from top to bottom
	set frame1top [frame $frame1.frame1top]
	set frame1bottom [frame $frame1.frame1bottom]

	# widgets in top frame
	set cmdwinentry1 [entry $frame1top.entry1 -textvariable cmdwincmd -width 50]
	cmdwinEntry1Refresh

	# widgets in bottom frame
	set cmdwinbutton1 [button $frame1bottom.button1 -text "Execute"]
	set cmdwinradio1 [radiobutton $frame1bottom.radio1 -text "A" -variable cmdwinOutChoice -value "A" -command cmdwinEntry1Refresh]
	set cmdwinradio2 [radiobutton $frame1bottom.radio2 -text "B" -variable cmdwinOutChoice -value "B" -command cmdwinEntry1Refresh]
	set cmdwinradio3 [radiobutton $frame1bottom.radio3 -text "C" -variable cmdwinOutChoice -value "C" -command cmdwinEntry1Refresh]
	set cmdwinradio4 [radiobutton $frame1bottom.radio4 -text "D" -variable cmdwinOutChoice -value "D" -command cmdwinEntry1Refresh]
	set cmdwinradio5 [radiobutton $frame1bottom.radio5 -text "E" -variable cmdwinOutChoice -value "E" -command cmdwinEntry1Refresh]

	pack $cmdwin.list1 -expand true -fill both

	pack $frame1 -side bottom -expand false -fill x
	pack $frame1top -side top -pady 1 -expand true -fill x
	pack $frame1bottom -side bottom -pady 1 -expand true -fill x

	pack $cmdwinentry1 -side left -expand true -fill x

	pack $cmdwinbutton1 -side left -padx 5 -pady 2 -anchor w -fill x
	pack $cmdwinradio1 -side left -padx 2 -anchor w
	pack $cmdwinradio2 -side left -padx 2 -anchor w
	pack $cmdwinradio3 -side left -padx 2 -anchor w
	pack $cmdwinradio4 -side left -padx 2 -anchor w
	pack $cmdwinradio5 -side left -padx 2 -anchor w



	wm geometry $cmdwin 405x300
	wm minsize $cmdwin 260 100

	# event bindings

	# listbox button release
	bind $cmdwinlist1 <ButtonRelease-1> {
		cmdwinEntry1Refresh
	}

	# button1 click - execute command
	bind $cmdwinbutton1 <B1-ButtonRelease-1> {
		set cmdexecuted($cmdwinOutChoice) $cmdwincmd
		executeCommand $cmdwinOutChoice
	}

	# entry1 - return key pressed
	bind $cmdwinentry1 <Return> {
		set cmdexecuted($cmdwinOutChoice) $cmdwincmd
		executeCommand $cmdwinOutChoice
	}


	return $cmdwin
}




# set visibility of windows according to winVisible($id)
proc winSetVisibility { id } {
	global winVisible
	global cmdwin
	global outwin

	if {$winVisible($id) == 1} {
		# visible
		if {$id == "command"} {
			# command window
			wm deiconify $cmdwin
		} elseif {$id == "console"} {
			# console
			console show
		} else {
			# output window
			wm deiconify $outwin($id)
		}
	} else {
		# not visible
		if {$id == "command"} {
			# command window
			wm withdraw $cmdwin
		} elseif {$id == "console"} {
			# console
			console hide
		} else {
			# output window
			wm withdraw $outwin($id)
		}
	}
}




# creates main window widgets
proc createMainWindow {} {
	global winVisible
	global abcstatus
	global winGeometry

	wm title . "ABCTCL"
	wm geometry . $winGeometry(.)
	wm resizable	. 0 0

	# close procedure
	wm protocol . WM_DELETE_WINDOW {
		# ask user if he really wants to quit
		set reply [tk_dialog .answer "Quit?" "Do you really want to quit?" "" 1 "   Yes   " "    No   "]
		if {$reply == 0} {
			abcDone; saveSettings; exit 1
		}
	}

	set mainwinlabel1 [label .label1 -textvariable abcstatus]

	set separator1 [ttk::separator .separator1]
	set mainwincheck1 [checkbutton .check1 -text "Command window" -variable winVisible(command) -command { winSetVisibility command }]
	set mainwincheck2 [checkbutton .check2 -text "Console" -variable winVisible(console) -command { winSetVisibility console }]
	set separator2 [ttk::separator .separator2]
	set mainwincheckA [checkbutton .checkA -text "Output window A" -variable winVisible(A) -command { winSetVisibility A }]
	set mainwincheckB [checkbutton .checkB -text "Output window B" -variable winVisible(B) -command { winSetVisibility B }]
	set mainwincheckC [checkbutton .checkC -text "Output window C" -variable winVisible(C) -command { winSetVisibility C }]
	set mainwincheckD [checkbutton .checkD -text "Output window D" -variable winVisible(D) -command { winSetVisibility D }]
	set mainwincheckE [checkbutton .checkE -text "Output window E" -variable winVisible(E) -command { winSetVisibility E }]
	set separator3 [ttk::separator .separator3]
	set frame1 [frame .frame1]
	set mainwinbutton1 [button .frame1.button1 -text "Load file ..."]
	set mainwinbutton2 [button .frame1.button2 -text "Save file as ..."]


	pack $mainwinlabel1 -side top -anchor w -pady 3 -padx 2
	pack $separator1 -side top -anchor w -fill x -padx 3 -pady 3
	pack $mainwincheck1 -side top -anchor w
	pack $mainwincheck2 -side top -anchor w
	pack $separator2 -side top -anchor w -fill x -padx 3 -pady 3
	pack $mainwincheckA -side top -anchor w
	pack $mainwincheckB -side top -anchor w
	pack $mainwincheckC -side top -anchor w
	pack $mainwincheckD -side top -anchor w
	pack $mainwincheckE -side top -anchor w
	pack $separator3 -side top -anchor w -fill x -padx 3 -pady 3
	pack $frame1 -side bottom -expand false  ;# -fill x
	pack $mainwinbutton1 -side left -anchor w -pady 3 -padx 3
	pack $mainwinbutton2 -side right -anchor w -pady 3 -padx 3


	# event bindings

	# button1 click - load file
	bind $mainwinbutton1 <B1-ButtonRelease-1> {
		set filename [tk_getOpenFile -filetypes $loadfiletypes]

		if {$filename != ""} {
   			# open file, direct output to $cmdwinOutChoice out window
			set cmdwincmd "abc_read outputData$cmdwinOutChoice \"$filename\""
			set cmdexecuted($cmdwinOutChoice) $cmdwincmd
			executeCommand $cmdwinOutChoice
		}

	}

	# button2 click - save file
	bind $mainwinbutton2 <B1-ButtonRelease-1> {
		set filename [tk_getSaveFile -filetypes $savefiletypes -defaultextension "blif"]
 
		if {$filename != ""} {
   			# save file, direct output to $cmdwinOutChoice out window
			set cmdwincmd "abcAnyCommand \"write $filename\" outputData$cmdwinOutChoice"
			set cmdexecuted($cmdwinOutChoice) $cmdwincmd
			executeCommand $cmdwinOutChoice
		}

	}

}




# saves geometry settings to file config.tcl - writes configuration script
proc saveSettings {} {
	global outwin
	global cmdwin
	global winVisible
	global hideNulls
	global hideRawOutput
	global cmdwinOutChoice
	global cmdwincmd
	global cmdexecuted


	update

	# geometry settings
	foreach id {A B C D E} {
		set winGeometry($id) [wm geometry $outwin($id)]
	}
	set winGeometry(command) [wm geometry $cmdwin]
	set winGeometry(.) [wm geometry .]
	set winGeometry(console) [console eval {wm geometry .}]

	# writing... do not report any errors during writing
	catch {set fo [open "config.tcl" w]}

	# output windows settings
	foreach id {A B C D E} {
		catch {puts $fo "set hideNulls($id) $hideNulls($id)"}
		catch {puts $fo "set hideRawOutput($id) $hideRawOutput($id)"}
		catch {puts $fo "set cmdexecuted($id) \{$cmdexecuted($id)\}"}
		catch {puts $fo ""}
	}

	# visibility settings
	foreach id {A B C D E command console} {
		catch {puts $fo "set winVisible($id) $winVisible($id)"}
	}
	catch {puts $fo ""}
	
	# geometry settings
	foreach id {A B C D E . command console} {
		catch {puts $fo "set winGeometry($id) $winGeometry($id)"}
	}
	catch {puts $fo ""}

	catch {puts $fo "set cmdwinOutChoice $cmdwinOutChoice"}
	catch {puts $fo "set cmdwincmd \{$cmdwincmd\}"}

	if {[catch {close $fo} err]} {
 		# do not report the error
	}
}




# loads configuration script, if file config.tcl does not exist or is incomplete,
# then default values are used
proc loadSettings {} {
	global outwin
	global cmdwin
	global winVisible
	global hideNulls
	global hideRawOutput
	global winGeometry
	global cmdwinOutChoice
	global cmdwincmd
	global cmdexecuted


	# just execute configuration script
	if {[catch {source config.tcl}]} {
		puts "File 'config.tcl' not found, using internal defaults."
	}

	# set loaded geometry
	foreach id {A B C D E} {
		wm geometry $outwin($id) $winGeometry($id)
	}
	wm geometry $cmdwin $winGeometry(command)
	wm geometry . $winGeometry(.)

	console eval "wm geometry . $winGeometry(console)"

	# visibility settings
	foreach id {A B C D E command console} {
		winSetVisibility $id
	}
	update

}



proc run {} {
	global abcstatus

	createMainWindow

	set result [abcInit res]
	if {$result == 1} {
		set abcstatus "ABC ready"
	} else {
		puts "abcInit FAILED"
		parray res
		set abcstatus "ABC initialization FAILED"
	}
	array unset res

	# create output windows, set their visibility
	foreach id {A B C D E} {
		createOutputWindow $id
		winSetVisibility $id
	}

	createCommandWindow

	winSetVisibility command
	winSetVisibility console

	# set console's 'on close' procedure
	console eval {wm protocol . WM_DELETE_WINDOW {consoleinterp eval {set winVisible(console) 0; winSetVisibility console}}}

	
	loadSettings

	foreach id {A B C D E} {
		reprintOutputWindow $id
	}

	update
}



run

