(defmeth graph-proto :add-control (c) (send self :add-overlay c))
(defmeth graph-proto :delete-control (c) (send self :delete-overlay c))

(defproto graph-control-proto 
  '(action location title) nil graph-overlay-proto)

(defmeth graph-control-proto :location (&optional (new nil set))
  (when set
        (send self :erase)
        (setf (slot-value 'location) new)
        (send self :redraw))
  (slot-value 'location))

(defmeth graph-control-proto :title (&optional (new nil set))
  (when set
        (send self :erase)
        (setf (slot-value 'title) new)
        (send self :redraw))
  (slot-value 'title))

(defmeth graph-control-proto :erase ()
  (let ((graph (send self :graph))
        (loc (send self :location))
        (sz (send self :size)))
    (if graph (apply #'send graph :erase-rect (append loc sz)))))

(defmeth graph-control-proto :size () 
  (let ((graph (send self :graph))
        (title (send self :title)))
    (if graph
        (list (+ 10 5 (send graph :text-width title)) 20)
        (list 10 10))))

(defmeth graph-control-proto :redraw ()
  (let* ((graph (send self :graph))
         (loc (send self :location))
         (loc-x (first loc))
         (loc-y (second loc))
         (title (send self :title)))
    (send self :erase)
    (send graph :frame-rect loc-x (+ 5 loc-y) 10 10)
    (send graph :draw-text title (+ 15 loc-x) (+ 15 loc-y) 0 0)))

(defmeth graph-control-proto :do-click (x y a b)
  (let* ((graph (send self :graph))
         (loc (send self :location))
         (loc-x (first loc))
         (loc-y (+ 5 (second loc))))
    (when (and (< loc-x x (+ loc-x 10)) (< loc-y y (+ loc-y 10)))
          (send graph :paint-rect (+ 1 loc-x) (+ 1 loc-y) 8 8)
          (send self :do-action (list a b))
          (send graph :while-button-down
                #'(lambda (x y) (send self :do-action nil)) nil)
          (send graph :erase-rect (+ 1 loc-x) (+ 1 loc-y) 8 8)
          t)))

(defmeth graph-control-proto :do-action (x) (sysbeep))

;;; Rockers

(defproto rocker-control-proto () () graph-control-proto)

(defmeth rocker-control-proto :size () 
  (let ((graph (send self :graph))
        (title (send self :title)))
    (if graph
        (list (+ 10 5 10 5 (send graph :text-width title)) 20)
        (list 10 10))))

(defmeth rocker-control-proto :redraw ()
  (let* ((graph (send self :graph))
         (loc (send self :location))
         (loc-x (first loc))
         (loc-y (second loc))
         (title (send self :title)))
    (send self :erase)
    (send graph :frame-rect loc-x (+ 5 loc-y) 10 10)
    (send graph :frame-rect (+ 15 loc-x) (+ 5 loc-y) 10 10)
    (send graph :draw-text title (+ 30 loc-x) (+ 15 loc-y) 0 0)))

(defmeth rocker-control-proto :do-click (x y a b)
  (let* ((graph (send self :graph))
         (loc (send self :location))
         (loc-x1 (first loc))
         (loc-x2 (+ 15 loc-x1))
         (loc-y (+ 5 (second loc))))
    (if (< loc-y y (+ loc-y 10))
        (let* ((arg (cond 
                     ((< loc-x1 x (+ loc-x1 10)) '-)
                     ((< loc-x2 x (+ loc-x2 10)) '+)))
               (loc-x (case arg (- loc-x1) (+ loc-x2))))
          (when arg
                (send graph :paint-rect (+ 1 loc-x) (+ 1 loc-y) 8 8)
                (send self :do-action (list a b) arg)
                (send graph :while-button-down
                      #'(lambda (x y) (send self :do-action nil arg)) nil)
                (send graph :erase-rect (+ 1 loc-x) (+ 1 loc-y) 8 8)
                t)))))

(defmeth rocker-control-proto :do-action (x arg) (sysbeep))

;;; Slider

(defproto slider-control-proto 
  '(index sequence display) () graph-control-proto)

(defmeth slider-control-proto :isnew (sequence &key 
                                               (title "Value")
                                               (display sequence)
                                               (location '(10 20))
                                               (index 0)
                                               graph)
  (call-next-method :title title :location location)
  (send self :sequence sequence :display display)
  (send self :index index)
  (if graph (send graph :add-control self)))

(defmeth slider-control-proto :size () 
  (let ((graph (send self :graph))
        (title (send self :title)))
    (list 100 30)))

(defmeth slider-control-proto :redraw ()
  (let* ((graph (send self :graph))
         (loc (send self :location))
         (loc-x (first loc))
         (loc-y (second loc))
         (w (first (send self :size))))
    (when graph
          (send graph :draw-text (send self :title) loc-x (+ loc-y 15) 0 0)
          (send graph :frame-rect loc-x (+ loc-y 20) w 10)
          (send self :draw-indicator))))

(defmeth slider-control-proto :draw-indicator (&optional index)
  (let* ((graph (send self :graph))
         (loc (send self :location))
         (loc-x (first loc))
         (loc-y (second loc))
         (w (first (send self :size)))
         (min (send self :min))
         (max (send self :max))
         (index (if index index (send self :index)))
         (val (floor (* (- w 7) (/ (- index min) (- max min))))))
    (when graph
          (let ((tw (send graph :text-width (send self :title))))
            (send graph :start-buffering)
            (send graph :erase-rect (+ 1 tw loc-x) loc-y (- w tw) 20)
            (send graph :draw-text 
                  (format nil "~a" (elt (send self :display) index))
                  (+ loc-x w) (+ loc-y 15) 2 0)
            (send graph :buffer-to-screen (+ 1 tw loc-x) loc-y (- w tw) 20))
          (send graph :erase-rect (+ 1 loc-x) (+ 21 loc-y) (- w 2) 8)
          (send graph :paint-rect (+ 1 loc-x val) (+ 21 loc-y) 5 8))))

(defmeth slider-control-proto :min () 0)

(defmeth slider-control-proto :max () (- (length (slot-value 'sequence)) 1))

(defmeth slider-control-proto :sequence (&optional (seq nil set) &key 
                                                   (display seq))
  (when set
        (setf (slot-value 'sequence) (coerce seq 'vector))
        (setf (slot-value 'display) (coerce display 'vector)))
  (slot-value 'sequence))

(defmeth slider-control-proto :display () (slot-value 'display))

(defmeth slider-control-proto :index (&optional (new nil set))
  (if set
      (let* ((new (max (send self :min) (min new (send self :max)))))
        (setf (slot-value 'index) new)
        (send self :draw-indicator)
        (send self :do-action (elt (send self :sequence) new))))
  (slot-value 'index))

(defmeth slider-control-proto :do-click (x y a b)
  (let* ((graph (send self :graph))
         (loc (send self :location))
         (loc-x (nth 0 loc))
         (loc-y (nth 1 loc))
         (w (first (send self :size))))
    (when (and (< loc-x x (+ loc-x w)) (< (+ loc-y 20) y (+ loc-y 30)))
          (let ((pos (+ (floor (* (- w 7) (/ (send self :index) 
                                             (send self :max))))
                        loc-x)))
            (cond
              ((<= pos x (+ pos 5))
               (let ((off (- x pos)))
                 (send graph :while-button-down
                       #'(lambda (x y)
                           (let ((val (max (+ loc-x 1)
                                           (min (- x off) 
                                                (+ loc-x (- w 6))))))
                             (setf pos val)
                             (send self :draw-indicator 
                                   (floor (* (send self :max) 
                                             (/ (- pos loc-x) (- w 7)))))))))
                 (send self :index 
                       (floor (* (send self :max) 
                                 (/ (- pos loc-x) (- w 7))))))
              ((< loc-x x pos)
               (send graph :while-button-down
                     #'(lambda (x y)
                         (let ((pos (+ (floor (* w (/ (send self :index) 
                                                      (send self :max))))
                                       loc-x)))
                           (if (< x pos)
                               (send self :index (- (send self :index) 1)))))
                     nil))
              ((< pos x (+ loc-x w))
               (send graph :while-button-down
                     #'(lambda (x y)
                         (let ((pos (+ (floor (* w (/ (send self :index) 
                                                      (send self :max))))
                                       loc-x)))
                           (if (> x pos)
                               (send self :index (+ (send self :index) 1)))))
                     nil))))
          t)))

;;;;
;;;; Rotation example
;;;;

;;; Rotation around axes

(defproto spin-rotate-control-proto '(v) () rocker-control-proto)

(defmeth spin-rotate-control-proto :isnew (v)
  (call-next-method :v v :location (list 10 (case v (0 10) (1 30) (2 50)))))

(defmeth spin-rotate-control-proto :title ()
  (send (send self :graph) :variable-label (slot-value 'v)))

(defmeth spin-rotate-control-proto :do-action (first sign)
  (let ((graph (send self :graph)))
    (if first
        (let* ((v (slot-value 'v))
               (v1 (if (= v 0) 1 0))
               (v2 (if (= v 2) 1 2))
               (trans (send graph :transformation))
               (cols (column-list 
                      (if trans 
                          trans 
                          (identity-matrix (send graph :num-variables)))))
               (angle (send graph :angle)))
          (send graph :idle-on (car first))
          (send graph :slot-value 'rotation-type
                (make-rotation (nth v1 cols) (nth v2 cols) 
                               (case sign (+ angle) (- (- angle)))))))
    (send graph :rotate)))

;;; Plot Rocking Control

(defproto spin-rock-control-proto '(v) () graph-control-proto)

(defmeth spin-rock-control-proto :isnew ()
  (call-next-method :location '(10 70) :title "Rock Plot"))

(defmeth spin-rock-control-proto :do-action (first) 
  (send (send self :graph) :rock-plot))

(defmeth spin-proto :rock-plot (&optional (k 2))
  (let ((angle (send self :angle)))
    (dotimes (i k) (send self :rotate-2 0 2 angle))
    (dotimes (i (* 2 k)) (send self :rotate-2 0 2 (- angle)))
    (dotimes (i k) (send self :rotate-2 0 2 angle))))

;;;; Speed Control

(defproto spin-speed-control-proto () () slider-control-proto)

(defmeth spin-speed-control-proto :isnew (&optional (points 21))
  (call-next-method (rseq 0 .2 points) :location '(10 90) :title "Speed"))

(defmeth spin-speed-control-proto :do-action (v)
  (let ((graph (send self :graph)))
    (if graph (send graph :angle v))))

;;;; Installation method

(defmeth spin-proto :add-spin-controls ()
  (send self :margin 110 0 0 20)
  (apply #'send self :size (+ (send self :size) '(100 0)))
  (send self :resize)
  (send self :add-control (send spin-rotate-control-proto :new 0))
  (send self :add-control (send spin-rotate-control-proto :new 1))
  (send self :add-control (send spin-rotate-control-proto :new 2))
  (send self :add-control (send spin-rock-control-proto :new))
  (send self :add-control (send spin-speed-control-proto :new)))


        
