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

;;; First some missing bp hacking functions 

(defun line>= (x line)
  (do ((forward line (line-next forward))
       (backward (line-previous line) (line-previous backward)))
      (nil)
    (cond ((eq x forward) (return t))
	  ((eq x backward) (return nil))
	  ((null forward) (return nil))
	  ((null backward) (return t)))))

(defmethod (bp :equal) (other)
  (and (eq line (bp-line other))
       (=& position (bp-position other))))

(defmethod (bp :print-self) (&optional (stream standard-output) ignore ignore)
  (format stream "#<~s ~z " 'bp (si:address-of self))
  (unless (or (null line) (null position))
    (print-bp-internal line position stream))
  (write-char #\> stream))
  
(defun print-bp-internal (line position stream)
  (let ((line-chars (line-chars line)))
    (princ #\" stream)
    (oustr line-chars stream 0 position)
    (write-char #\^ stream)
    (oustr line-chars stream position (-& (line-char-count line) position))
    (write-char #\" stream)))

(defmethod (line :print-self) (&optional (stream standard-output) ignore ignore)
  (format stream "#<~s ~s ~z>"
	  'line (substring chars 0 char-count) (si:address-of self)))

(defun bp-< (bp1 bp2)
  (let ((line1 (bp-line bp1))
	(line2 (bp-line bp2)))
    (cond ((eq line1 line2)
	   (< (bp-position bp1) (bp-position bp2)))
	  (t (not (line>= line1 line2))))))

(defmacro move-bp (bp to-bp-arg)
  (let ((to-bp (gentemp "BP")))
    `(let ((,to-bp ,to-bp-arg))
       (send ,bp :move (bp-line ,to-bp) (bp-position ,to-bp)))))

(defmacro mark ()
  '(get-mark))

;;; Same as MAKE-BP but doesn't tell LINE about it

(defun make-temp-bp (bp)
  (make-instance 'bp :buffer :temp-bp
		     :line (bp-line bp)
		     :position (bp-position bp)))

(defmacro flush-bp (bp)
  `(send ,bp :expire))

(defmacro with-bp ((var bp) &body corpus)
  `(let ((,var (copy-bp ,bp)))
     (unwind-protect
	 (progn
	   ,@corpus)
       (flush-bp ,var))))
       
(defmacro with-bp* (bindings &body corpus)
  (let ((x-bindings (loop for (var bp) in bindings
			  collecting `(,var (copy-bp ,bp))))
	(flushings (loop for (var . ignore) in bindings
			 collecting `(flush-bp ,var))))
    `(let* ,x-bindings
       (unwind-protect
	   (progn
	     ,@corpus)
	 ,@flushings))))

(defmacro preserving-point (&body corpus)
  `(with-bp (*editor-cursor* *editor-cursor*)
     ,@corpus))

;;; This exists but in a slightly different form

(defmacro region ((bp1 bp2) &body corpus)
  `(with-bp* ((,bp1 *editor-cursor*)
	      (,bp2 (or (mark) (ed-warn :no-mark))))
     (cond ((bp-< ,bp2 ,bp1)
	    (psetq ,bp1 ,bp2
		   ,bp2 ,bp1)))
     ,@corpus))

;;; This is to make up for the lack of motion functions
;;; which don't hack the point

(defmacro with-point-at ((bp) &body corpus)
  `(with-bp (*editor-cursor* *editor-cursor*)
     (move-bp *editor-cursor* ,bp)
     ,@corpus
     (make-temp-bp *editor-cursor*)))

(defmacro funcall-for-bp (motion-fn n start-bp)
  `(with-point-at (,start-bp)
     (let ((*argument* ,n))
       (funcall ,motion-fn))))

;;; An optimized version of UN-KILL

(defun insert-interval (bp interval)
  (with-point-at (bp)
    (loop with max-index = (1-& (vector-length interval))
	  for i from 0 to max-index
	  do (unless (null (aref interval i))
	       (send (edit-cursor-line *editor-cursor*)
		     :insert-string (edit-cursor-position *editor-cursor*)
		     (aref interval i))
	       (when (<& i max-index)
		 (send (edit-cursor-line *editor-cursor*)
		       :break (edit-cursor-position *editor-cursor*)))))))

(defmacro delete-interval (bp1 bp2 ignore)
  `(delete-between-marks ,bp1 ,bp2))

(defmacro copy-interval (bp1 bp2 ignore)
  `(progn (setq *last-kill-command-count* -1000) ;ickypoo hack.
	  (kill-between-marks ,bp1 ,bp2)
	  (pop-kill)))


;;; Another missing function

(defun forward-line ()
  (cond ((zerop& *argument*)
	 (send *editor-cursor* :set-pos 0))
	((minusp& *argument*)
	 (do ((line (bp-line *editor-cursor*)
		    (line-previous line))
	      (n (-& *argument*) (1-& n)))
	     ((if (null line)
		  (ed-warn :at-start)
		(<=& n 0))
	      (send *editor-cursor* :move line 0))))
	(:plusp&
	 (do ((line (bp-line *editor-cursor*)
		    (line-next line))
	      (n *argument* (1-& n)))
	     ((if (null line)
		  (ed-warn :at-end)
		(<=& n 0))
	      (send *editor-cursor* :move line 0)))))
  nil)

;;; Let's put this here for no good reason

(defun steve-beep ()	;but dont call it beep. It takes a different number of args.
  (write-char #\bell))

(defun type-ahead-p ()
  (or (send terminal-io :listen)
      executing-keyboard-macro))
	  
