;;; -*- Mode:LISP; Package:NETWORK; Base:10; Readtable:CL -*-

#|

  Copyright LISP Machine, Inc. 1987
   See filename "Copyright.Text" for
  licensing and release information.

|#

(export '( *loopback-interface*))

(defstream loopback-interface
	   (network-interface)
	   "LOOP-"
  )

(defvar *loopback-interface* nil "The Loopback network interface")
(defvar *loopback-receive-list* (make-fifo) "List of packets waiting to be received")

;;;Debugging parameters
(defvar *damage-percentage* 0)
(defvar *delay-percentage* 0)
(defvar *delay-maximum-interval* 2)
(defvar *drop-percentage* 0)
(defvar *duplicate-percentage* 0)

(defop (loopback-interface :peek-special-fields) (ni)
  (declare (ignore ni))
  (list (tv:scroll-parse-item
	  `(:mouse-item
	     (nil :buttons
		  ,(make-list 3 :initial-element
			      `(nil :eval (process-run-function "Set Loopback Debug Variables"
								#'set-loopback-debugging-variables)))
		  :documentation
		  "Click to set Loopback Debugging Variables")
	     :string "Debugging Variables:")
	  `(:symeval net:*damage-percentage* nil ("  Damage ~A%"))
	  `(:symeval net:*delay-percentage* nil ("  Delay ~A%"))
	  `(:symeval net:*delay-maximum-interval* nil (" up to ~A seconds"))
	  `(:symeval net:*drop-percentage* nil ("  Drop ~A%"))
	  `(:symeval net:*duplicate-percentage* nil ("  Duplicate ~A%")))))

(defun set-loopback-debugging-variables ()
  (tv:choose-variable-values '((*damage-percentage* "Damage %" :number-or-nil)
			       (*delay-percentage* "Delay %" :number-or-nil)
			       (*delay-maximum-interval* "Delay max" :number-or-nil)
			       (*drop-percentage* "Drop %" :number-or-nil)
			       (*duplicate-percentage* "Duplicate %" :number or-nil))
			     :label "Loopback Debugging Parameters"
			     :function #'(lambda (ignore variable old-value new-value)
					   (if new-value
					       (case variable
						 ((*damage-percentage* *delay-percentage*
						   *drop-percentage* *duplicate-percentage*)
						  (unless (<= 0 new-value 100)
						    (set variable old-value)))
						 (*delay-maximum-interval*
						  (unless (<= 0 new-value 60)
						    (set variable old-value))))
					     (set variable old-value))
					   nil)))

(defmacro loop-pkt (elt)
  `(car ,elt))

(defmacro loop-type (elt)
  `(cdr ,elt))

(defun loopback-reset (stream)
  (declare (ignore stream))
  (do ((elt (pop-fifo *loopback-receive-list*)
	    (pop-fifo *loopback-receive-list*)))
      ((null elt))
    (free-packet (loop-pkt elt))))

(defun loopback-send-int-pkt (int-pkt source dest type n-16-bit-words)
  (declare (ignore source))
  (declare (ignore dest))
  (setf (fill-pointer int-pkt) n-16-bit-words)
  (push-fifo (cons int-pkt type) *loopback-receive-list*)
  t)

(defun loopback-packet-ready ()
  (not (fifo-empty-p *loopback-receive-list*)))

(defun loopback-get-next-packet ()
  (declare (values packet type source destination))
  (let ((elt (pop-fifo *loopback-receive-list*)))
    (when elt
      (values (loop-pkt elt)			;the packet
	      (loop-type elt)			;type code
	      0					;sender
	      0					;receiver
	      nil				;broadcast-p
	      ))))

(defun setup-loopback (tag &aux alist)
  (when *loopback-interface*
    (setq alist (ni-protocol-alist *loopback-interface*))
    (funcall *loopback-interface* :close))
  
  (setq *loopback-receive-list* (make-fifo))
  
  (setq *loopback-interface*
	(make-loopback-interface :tag tag
				 :interface :loopback
				 :keyword :loopback
				 :minimum-data-length 0
				 :maximum-data-length 1500.
				 :sent-header-length 0
				 :rcvd-header-length 0
				 :sent-trailer-length 0
				 :rcvd-trailer-length 0
				 :protocol-alist alist
				 :disable-function 'loopback-reset
				 :reset-function 'loopback-reset
				 :packet-ready-function 'loopback-packet-ready
				 :get-next-packet-function 'loopback-get-next-packet
				 :send-function  'loopback-send-int-pkt
				 :gauge-name "LOOP"
				 ))
  
  (funcall *loopback-interface* :open)
  (funcall *loopback-interface* :enable))

(add-initialization "Start Loopback interface" '(setup-loopback "LOOPBACK") :network-driver)
