#
# Copyright 2004-2013 University of Zagreb.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# This work was supported in part by Croatian Ministry of Science
# and Technology through the research contract #IP-2003-143.
#

#****h* imunes/imunes.tcl
# NAME
#    imunes.tcl
# FUNCTION
#    Starts imunes in batch or interactive mode. Include procedures from
#    external files and initializes global variables.
#
#	imunes [-b] [-e experiment_id] [filename]
#
#    When starting the program in batch mode the option -b must be
#    specified.
#
#    When starting the program with defined filename, configuration for
#    file "filename" is loaded to imunes.
#****

#
# Include procedure definitions from external files. There must be
# some better way to accomplish the same goal, but that's how we do it
# for the moment.
#
# The ROOTDIR and LIBDIR variables will be automatically set to the proper
# value by the installation script.
#

#****v* imunes.tcl/ROOTDIR
# NAME
#    ROOTDIR
# FUNCTION
#    The location of imunes library files. The ROOTDIR and LIBDIR variables
#    will be automatically set to the proper value by the installation script.
#*****

#****v* imunes.tcl/LIBDIR
# NAME
#    LIBDIR
# FUNCTION
#    The location of imunes library files. The ROOTDIR and LIBDIR variables
#    will be automatically set to the proper value by the installation script.
#*****

set LIBDIR ""
set ROOTDIR "."

if { $ROOTDIR == "." } {
    set BINDIR ""
} else {
    set BINDIR "bin"
}

try {
    source "$ROOTDIR/$LIBDIR/helpers.tcl"
} on error { result options } {
    puts stderr "Could not find file helpers.tcl in $ROOTDIR/$LIBDIR:"
    puts stderr $result
    exit 1
}

safePackageRequire [list cmdline platform ip base64]

set initMode 0
set execMode interactive
set debug 0
set printVersion 0
set prepareFlag 0
set forceFlag 0

set options {
    {e.arg	"" "Specify experiment ID"}
    {eid.arg	"" "Specify experiment ID"}
    {b		"Turn on batch mode"}
    {batch	"Turn on batch mode"}
    {d.secret	"Turn on debug mode"}
    {p		"Prepare virtual root file system"}
    {prepare	"Prepare virtual root file system"}
    {f		"Force virtual root preparation (delete existing vroot)"}
    {force	"Force virtual root preparation (delete existing vroot)"}
    {i		"Setup devfs rules for virtual nodes (Only on FreeBSD)"}
    {v		"Print IMUNES version"}
    {version	"Print IMUNES version"}
    {h		"Print this message"}
}

set usage [getPrettyUsage $options]
parseCmdArgs $options $usage

set baseTitle "IMUNES"
set imunesVersion "Unknown"
set imunesChangedDate ""
set imunesLastYear ""
set imunesAdditions ""

fetchImunesVersion
if { $printVersion } {
    printImunesVersion
    exit
}

set isOSfreebsd false
set isOSlinux false
set isOSwin false

setPlatformVariables

if { $prepareFlag } {
    prepareVroot
    exit
}

# Runtime libriaries
foreach file [glob -directory $ROOTDIR/$LIBDIR/runtime *.tcl] {
    if { [string match -nocase "*linux.tcl" $file] != 1 } {
	safeSourceFile $file
    }
}

if {! [info exists eid_base]} {
    set eid_base [genExperimentId]
}

# Set default L2 node list
set l2nodes "hub lanswitch click_l2 rj45 stpswitch filter packgen ext"
# Set default L3 node list
set l3nodes "genericrouter quagga xorp static click_l3 host pc nat64"
# Set default supported router models
set supp_router_models "xorp quagga static"

if { $isOSlinux } {
    # Limit default nodes on linux
    set l2nodes "lanswitch rj45 ext"
    set l3nodes "genericrouter quagga static pc host nat64"
    set supp_router_models "quagga static"
    safeSourceFile $ROOTDIR/$LIBDIR/runtime/linux.tcl
    if { $initMode == 1 } {
	#puts "INFO: devfs preparation is done only on FreeBSD."
	exit
    }
}
if { $isOSfreebsd } {
    safeSourceFile $ROOTDIR/$LIBDIR/runtime/freebsd.tcl
    if { $initMode == 1 } {
	prepareDevfs
	exit
    }
}

if { $execMode == "batch" } {
    set err [checkSysPrerequisites]
    if { $err != "" } {
	puts $err
	exit
    }
}

# Configuration libraries
foreach file [glob -directory $ROOTDIR/$LIBDIR/config *.tcl] {
    safeSourceFile $file
}

# The following files need to be sourced in this particular order. If not
# the placement of the toolbar icons will be altered.
# L2 nodes
foreach file $l2nodes {
    safeSourceFile "$ROOTDIR/$LIBDIR/nodes/$file.tcl"
}
# L3 nodes
foreach file $l3nodes {
    safeSourceFile "$ROOTDIR/$LIBDIR/nodes/$file.tcl"
}
# additional nodes
safeSourceFile "$ROOTDIR/$LIBDIR/nodes/localnodes.tcl"
safeSourceFile "$ROOTDIR/$LIBDIR/nodes/annotations.tcl"

#
# Global variables are initialized here
#

#****v* imunes.tcl/prefs
# NAME
#    prefs
# FUNCTION
#    Contains the list of preferences. When starting a program
#    this list is empty.
#*****

# Clipboard
namespace eval cf::clipboard {}
set cf::clipboard::node_list {}
set cf::clipboard::link_list {}
set cf::clipboard::annotation_list {}
set cf::clipboard::canvas_list {}
set cf::clipboard::image_list {}

set cfg_list {}
set curcfg ""

#****v* imunes.tcl/editor_only
# NAME
#    editor_only -- if set, Experiment -> Execute is disabled
# FUNCTION
#    IMUNES GUI can be used in editor-only mode.i
#    This variable can be modified in .imunesrc.
set editor_only false

set winOS false
if { $isOSwin } {
    set winOS true
}

if { !$isOSwin } {
    catch {exec convert -version | head -1 | cut -d " " -f 1,2,3} imInfo
} else {
    set imInfo $env(PATH)
}

set hasIM true
if { [string match -nocase "*imagemagick*" $imInfo] != 1} {
    set hasIM false
}

set runtimeDir "/var/run/imunes"

#
# Read config files, the first one found: .imunesrc, $HOME/.imunesrc
#
# XXX
readConfigFile

#
# Initialization should be complete now, so let's start doing something...
#

if {$execMode == "interactive"} {
    safePackageRequire Tk "To run the IMUNES GUI, Tk must be installed."
    foreach file "canvas copypaste drawing editor help theme linkcfgGUI \
	mouse nodecfgGUI widgets" {
	safeSourceFile "$ROOTDIR/$LIBDIR/gui/$file.tcl"
    }
    source "$ROOTDIR/$LIBDIR/gui/initgui.tcl"
    source "$ROOTDIR/$LIBDIR/gui/topogen.tcl"
    if { $debug == 1 } {
	source "$ROOTDIR/$LIBDIR/gui/debug.tcl"
    }

    newProject
    if { $argv != "" && [file exists $argv] } {
	set ::cf::[set curcfg]::currentFile $argv
	openFile
    }
    updateProjectMenu
    # Fire up the animation loop
    animate
    # Event scheduler - should be started / stopped on per-experiment base?
#     evsched
} else {
    if {$argv != ""} {
	if { ![file exists $argv] } {
	    puts "Error: file '$argv' doesn't exist"
	    exit
	}
	global currentFileBatch
	set currentFileBatch $argv
	set fileId [open $argv r]
	set cfg ""
	foreach entry [read $fileId] {
	    lappend cfg $entry
	}
	close $fileId

	set curcfg [newObjectId cfg]
	lappend cfg_list $curcfg
	namespace eval ::cf::[set curcfg] {}

	loadCfg $cfg

	if { [checkExternalInterfaces] } {
	    return
	}
	if { [allSnapshotsAvailable] == 1 } {
	    deployCfg
	    createExperimentFilesFromBatch
	}
    } else {
	set configFile "$runtimeDir/$eid_base/config.imn"
	set ngmapFile "$runtimeDir/$eid_base/ngnodemap"
	if { [file exists $configFile] && [file exists $ngmapFile] \
	    && $regular_termination } {
	    set fileId [open $configFile r]
	    set cfg ""
	    foreach entry [read $fileId] {
		lappend cfg $entry
	    }
	    close $fileId

	    set curcfg [newObjectId cfg]
	    lappend cfg_list $curcfg
	    namespace eval ::cf::[set curcfg] {}
	    upvar 0 ::cf::[set ::curcfg]::ngnodemap ngnodemap
	    upvar 0 ::cf::[set ::curcfg]::eid eid
	    set eid $eid_base

	    set fileId [open $ngmapFile r]
	    array set ngnodemap [gets $fileId]
	    close $fileId

	    loadCfg $cfg

	    terminateAllNodes $eid_base
	} else {
	    vimageCleanup $eid_base
	}

	deleteExperimentFiles $eid_base
    }
}
