

;;; the directory in which source files are located
(defvar *source-directory* nil)

(setf *source-directory* (concatenate 'string (pathname-device *load-truename*) ":" (directory-namestring *load-truename*)))

;; clear the environment

(clear-all)

;;; global variables 
(defparameter *cycle-count* 0) ;; represents the current cycle
(defparameter *simulation-cycle-count* 0) ;; represents the current cycle
(defparameter *number-of-cycles* 0 "Specifies the number of processing cycles that were run as part of the simulation.") ;; the number of cycles that will be run as part of the simulation
(defvar *cycle-length*) ;; the length (in seconds) of each cycle
(defparameter *number-of-agents* 1 "Specifies the total number of agents in the simulation.")
(defparameter *server-process* nil)
(defvar *port-number* 6666 "The port number that ACT-R models will listen on.")
(defvar *trace-socket-activity* nil "Specifies whether socket activity is reported.")
(defvar *default-delay-for-monitor* 30 "Specifies the default amount of time (in seconds) that the simulation will wait for monitoring 
		applications to connect.")
(defvar *base-model-name* 'base-model "Specifies the name used for the base model that is used to run the update cycles.")
(defvar *system-agent-name* 'system "Specifies the name used for the a generic system agent that reports on the progress of the simulation.")
(defparameter *is-real-time* t "Indicates whether the simulation experiment was run in real time.")

;; loads the specified file from the source directory
(defun load-file (filename)
	(load (concatenate 'string *source-directory* filename))
)

;;; load all the files required by the simulation
(load-file "database.lisp")
(load-file "self-module.lisp")
(load-file "reporting-module.lisp")
(load-file "functions.lisp")
(load-file "socket.lisp")
(load-file "device.lisp")
(load-file "xna-demov1.lisp")

(defun run-one-agent (&key (cycles 60))
	(run-simulation :num-actr-agents 1 :cycles cycles)
)


;;; This is the main function to run the simulation
(defun run-simulation (&key (agent-prefix "AGENT") 
											(cycles 60)
											(speed 1.0)
											(cycle-length 1.0)
											(num-actr-agents 1)
											(model-code *agent-model-code*))
  

	;; check numeric arguments
	(if (or (not (numberp cycles))
				(not (numberp speed))
				(not (numberp cycle-length))
				(not (numberp num-actr-agents)))
		(progn
			(format t "~&One or more numeric arguments could not be interpreted as numbers.~%Exiting run-simulation.")
			(return-from run-simulation)))	
	
	;check the agent prefix
	(if (not (is-non-zero-length-string agent-prefix t))
		(progn
				(format t "~&The agent prefix was not recognized as a valid string.~%Exiting run-simulation.")
				(return-from run-simulation)))
	
	
	;check that we have at least one ACT-R agent
	(if (= num-actr-agents 0)
		(progn
				(format t "~&Total agents is zero. No ACT-R agents are expected to participate in the simulation.~%Exiting run-simulation.")
				(return-from run-simulation)))

	(setf *cycle-count* 0)
	(setf *number-of-agents* num-actr-agents)
	(setf *simulation-cycle-count* 0)
	(setf *cycle-length* cycle-length)
	(setf *number-of-cycles* cycles)
  
	;; reset all the databases
	(reset-all-databases)
	;; remove all models and meta-processes to start fresh each time
	(clear-all)
	;; create all base agent properties
	(init-all-agent-properties) 
	;; create a starting model so that we can call the update cycle method
	(define-model base-model '(sgp :v nil))

	(configure-act-r-agents *number-of-agents* agent-prefix model-code)
	;create a system agent
	(configure-system-agent 'SYSTEM-AGENT)
	;create an entry in the database for the external game agent
	(configure-monitor-agent)
	;instantiate all the act-r agent models
	(create-act-r-models)
	;; configure agent properties
	(configure-all-agent-properties)
	;; create a 'self' chunk for the self buffer and sets slot values based on the settings specified in the triplestore
	(init-selves)

	(with-model base-model
		;; every second starting at 1s run the update-cycle command
		(schedule-periodic-event 1 'update-cycle :params nil :maintenance t :initial-delay 1))
  
	(unless (= speed 1.0)
		(when (and (numberp speed) (plusp speed))
			(mp-real-time-management :units-per-second (/ internal-time-units-per-second speed))))

	(let ((total-time (* *number-of-cycles* *cycle-length*))) ; total-time is the number of seconds that the simulation will run for
		(format t "~&Starting server.~%")
		(setf *server-process* (process-run-function "TCP Server" 'start-server)) ;create server process on separate thread
		(let* ((cnt 0)
				(monitor-ready nil)
				(max-count *default-delay-for-monitor*))
			(while (or (< cnt max-count) (not monitor-ready))
				(if (not monitor-ready) ; external environment not ready
					(progn ; if we are waiting for external environment to connected
						(if (= cnt 0) (format t "~&Waiting for external environment to connect...~%"))
						(if (is-monitor-connected)
							(progn
								(format t "~&External environment is connected and ready.~%") 
								(setf monitor-ready t)))))
				(if (not monitor-ready)
					;we are still waiting
					(progn
						(sleep 1)
						(incf cnt))
					  (setf cnt max-count))) ; we no longer need to wait
			(if monitor-ready
				(progn
					(format t "~&Starting simulation...~%")
					(run total-time :real-time *is-real-time*)
					(format t "~&The simulation finished successully."))
			  (format t "~&One or more agents failed to connect.~%The simulation was not run."))))

	;; if the server is running then kill it.
	(if *server-process* 
		(progn
			(send-stop-message-to-clients)
			(format t "~&Stopping server.")
			(kill-server-process *server-process*)))
)


