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


(defvar *bt-block-list*)
(defconst *bt-ethernet-type* 9.)
(defconst *bt-data-offset* 32.)
(defvar *bt-server-needed* nil)
(defstruct (bt-pkt (:type :array))
  block-number-lo
  block-number-hi)
(defvar *fake-pkt* nil)

(defun bt-pkt-receiver (unit-name part n-blocks &aux rqb)
  (when (assq *bt-ethernet-type* chaos:ethernet-pkt-handler-alist)
    (ferror nil "already have handler for ~s" *bt-ethernet-type*))
  ;;don't want to use make-list, since that would be cdr coded
  (setq *bt-block-list* (do ((i 0 (1+ i))
			     (l nil (cons nil l)))
			    ((= i n-blocks) l)))
  (si:with-decoded-disk-unit (unit unit-name "Write" nil t)
    (multiple-value-bind (first-block n-blocks)
	(si:find-disk-partition part nil unit)
      (when (null first-block)
	(ferror nil "can't find partition ~s" part))
      (setq rqb (si:get-disk-rqb))
      (unwind-protect
	  (let ((func #'(lambda (int-pkt)
			  (copy-array-portion int-pkt *bt-data-offset* (+ *bt-data-offset* (floor 1024. 2))
					      (si:rqb-buffer rqb) 0 (floor 1024. 2))
			  (let ((block-number (dpb (bt-pkt-block-number-hi int-pkt)
						   (byte 16. 16.)
						   (bt-pkt-block-number-lo int-pkt))))
			    (when (or (< block-number 0)
				      (>= block-number n-blocks))
			      (ferror nil "bad block number"))
			    (si:disk-write rqb unit (+ first-block block-number))
			    (setq *bt-block-list* (delq block-number *bt-block-list* 1))
			    (chaos:free-int-pkt int-pkt)))))
;	    (push (cons *bt-ethernet-type* func) chaos:ethernet-pkt-handler-alist)
;	    (process-wait "Band Transfer"
;			  #'(lambda () (not *bt-server-needed*)))
	    (do-forever
	      (process-wait "Fake Pkt" #'(lambda () *fake-pkt*))
	      (funcall func *fake-pkt*)
	      (setq *fake-pkt* nil)
	      (if (null *bt-server-needed*) (return nil)))
	    )
	(setq chaos:ethernet-pkt-handler-alist
	      (delq (assq *bt-ethernet-type* chaos:ethernet-pkt-handler-alist)
		    chaos:ethernet-pkt-handler-alist))))))

(defun bt-server-function (&aux conn)
  (setq conn (chaos:listen "FAST-BAND-TRANSFER"))
;  (when (null ethernet:*lambda-has-ethernet*)
;    (chaos:reject conn "I don't own the etherent")
;    (return-from bt-server-function nil))
  (chaos:accept conn)
  (send tv:who-line-file-state-sheet :add-server conn "FAST-BAND-TRANSFER")
  (with-open-stream (stream (chaos:make-stream conn))
    (let ((*readtable* (si:find-readtable-named "CL"))
	  (*read-base* 10.)
	  (*print-base* 10.)
	  (*package* (find-package "USER")))
      (let ((command-plist (read stream)))
	(let ((local-unit (getf command-plist :unit))
	      (part (getf command-plist :part))
	      (n-blocks (getf command-plist :n-blocks))
	      (direction (getf command-plist :direction)))
	  (ecase direction
	    (:write
	     (setq *bt-server-needed* t)
	     (process-run-function "BT Background" #'bt-pkt-receiver local-unit part n-blocks)
	     (print `(:ready t
			     ;:ethernet-address ,ethernet:my-ethernet-address
			     ) stream)
	     (send stream :force-output)
	     (read stream)
	     (setq *bt-server-needed* nil))))))))

(add-initialization "FAST-BAND-TRANSFER"
                    '(process-run-function "BT Server" 'bt-server-function)
                    NIL
                    'chaos:server-alist)
  
(defun bt-send-band (local-unit-name local-part to-machine to-unit to-part &aux rqb)
  (when (not (zerop to-unit))
    (ferror nil "can't handle this yet"))
  (si:with-decoded-disk-unit (unit to-machine "Write" nil t)
    (when (null (si:find-disk-partition-for-write to-part nil unit))
      (ferror nil "can't find TO-PART: ~s" to-part)))
  (si:with-decoded-disk-unit (local-unit local-unit-name "Read")
    (multiple-value-bind (first-block n-blocks)
	(si:find-disk-partition-for-read local-part)
      (setq n-blocks (min n-blocks (si:measured-size-of-partition local-part local-unit)))
      (when (null first-block)
	(ferror nil "can't find ~s" local-part))
      (let ((*readtable* (si:find-readtable-named "CL"))
	    (*read-base* 10.)
	    (*print-base* 10.)
	    (*package* (find-package "USER")))
	(with-open-stream (stream (chaos:open-stream to-machine "FAST-BAND-TRANSFER"))
	  (print `(:direction :write
			      :unit ,to-unit
			      :part ,to-part
			      :n-blocks ,n-blocks)
		 stream)
	  (send stream :force-output)
	  (let ((response (read stream)))
	    (let ((dest-adr (getf response :ethernet-address)))
	      (setq rqb (si:get-disk-rqb))
	      (dotimes (i n-blocks)
		(when (zerop (remainder i 100.))
		  (format t "~d " (floor i 100.)))
		(si:disk-read rqb local-unit (+ first-block i))
		(let ((int-pkt (chaos:allocate-int-pkt)))
		  (copy-array-portion (si:rqb-buffer rqb) 0 (floor 1024. 2)
				      int-pkt *bt-data-offset* (+ *bt-data-offset* (floor 1024. 2)))
		  (setf (bt-pkt-block-number-hi int-pkt) (ldb (byte 16. 16.) i))
		  (setf (bt-pkt-block-number-lo int-pkt) (ldb (byte 16. 0) i))
;		  (ethernet:send-int-pkt-via-ethernet
;		    int-pkt
;		    ethernet:my-ethernet-address
;		    dest-adr
;		    *bt-ethernet-type*		    
;		    (floor 1024. 2))
		  (setq *fake-pkt* int-pkt)
		  (process-wait "Fake Pkt" #'(lambda () (null *fake-pkt*)))
		  ))))
	  (print "T " stream)
	  (send stream :force-output)
	  (send stream :eof)
	  )))))
