;
; Simple Grid Protocol 1.2 - Copyright (c) Brendan Kosowski 2004, 2005, 2015, 2019
;
; Porta Function makes a programming task portable and sends it to the
; grid for execution on the machine with the highest ACR (default) or
; Nth best ACR (N is 0 for best ACR, 1 for next best ACR and so forth).
; Task will be self executed if local machine has the same IP as the
; grid machine executing the task.
; Returned values:
;    1: Result of portable task execution
;    2: IP Address of machine that executed portable task
;    3: T (if self executed) or NIL (if grid executed)
;

; put this block of code before porta
; connects a socket to a stream (the sbcl way)
(setq *print-right-margin* 100000000)
(require :sb-bsd-sockets)
(defun sb-socket-connect (port ipaddr)
  (let ((socket (make-instance 'sb-bsd-sockets:inet-socket :type :stream :protocol :tcp)))
  (sb-bsd-sockets:socket-connect socket (sb-bsd-sockets:make-inet-address ipaddr) port)
  (sb-bsd-sockets:socket-make-stream socket :input t :output t :buffering :none))
)
; end block of code before porta

(defun porta (task &optional (nth_best_ip 0) (return_list NIL))
  ;(sleep 0)
  (let ((fr NIL) (token NIL) (track_ip NIL) (my_ip NIL) (stm NIL) (lin NIL) (grid NIL) (self NIL) (result NIL) (best_ip NIL))
	(setq fr (open #p"/home/grid/token.dat" :direction :input))
	(setq token (read-line fr))
	(close fr)

	(setq fr (open #p"/home/grid/track_ip.dat" :direction :input))
	(setq track_ip (read-line fr))
	(close fr)

	(setq fr (open #p"/home/grid/my_ip.dat" :direction :input))
	(setq my_ip (read-line fr))
	(close fr)

	(setq stm (sb-socket-connect 17667 track_ip))
	(loop
		(setq lin (read-line stm))
		(when (search "Grid" lin) (return))
	)

	(setq lin (read-line stm))
	(when (string= lin "token?")
		(format stm "~a~%" token)
	)

	(setq lin (read-line stm))
	(when (string= lin "what?")
		(format stm "show~%")
	)

	(setq grid (read-from-string (read-line stm)))

	(setq lin (read-line stm))
	(when (string= lin "what?")
		(format stm "bye~%")
	)

	(close stm)

	(setq stm nil)
	(setq self nil)
	(loop	
    ;MOD: This is the IP rotary system (so if there are less IPs than
    ;     porta needs, porta will loop back to the best after the worst.
    ;(setq best_ip (first (nth nth_best_ip grid)))
		(setq best_ip (first (nth (rem nth_best_ip (length grid)) grid)))
		(if (string= my_ip best_ip)
			(setq self t)
			(ignore-errors (setq stm (sb-socket-connect 17666 best_ip)))
		)
		(when stm (return))
		(when self (return))
	)

	(if self
		(setq result (eval task))
		(progn

			(loop
				(setq lin (read-line stm))
				(when (search "Grid" lin) (return))
			)

			(setq lin (read-line stm))
			(when (string= lin "token?")
				(format stm "~a~%" token)
			)

			(setq lin (read-line stm))
			(when (string= lin "what?")
				(format stm "eval~%")
			)

			(setq lin (read-line stm))
			(when (string= lin "code?")
				(prin1 task stm)
				(format stm "~%")
			)

			(setq result (read-from-string (read-line stm)))

			(setq lin (read-line stm))
			(when (string= lin "what?")
				(format stm "bye~%")
			)

			(close stm)
		)
	)
	(if return_list
		(list result best_ip self)
		(values result best_ip self)
	)
  )
)
