#lang scheme/base 

(require scheme/class
         (prefix-in scheme: (only-in scheme/base read))
         mrlib/cache-image-snip)

(provide visible-matrix%
         matrix-snip-class%
         (rename-out (matrix-snip-class snip-class))
         v?
         v-m
         matrix<%>
         build-bitmap ;; from cache-image-snip
         overlay-bitmap
         )

(define matrix<%> 
  (interface ()
    ->rectangle ;; -> [Listof [Listof X]]
    ))

(define visible-matrix<%> 
  (interface ()
    get-M ;; -> matrix<%> u [Listof [Listof X]]
    ))

;; ---------------------------------------------------------------------------

(define (v-m VM) (send VM get-M))
(define (v? VM) (is-a? VM visible-matrix%))

;; representing a matrix that renders itself as an image, as in "image.rkt"
(define visible-matrix%
  (class cache-image-snip% 
    (inherit set-snipclass get-argb)
    (inherit-field dc-proc argb-proc width height argb px py)
    
    (define/override (equal-to? other recur)
      (define my-mat (or (and M (send M ->rectangle)) R))
      (define other-mat
	(cond
	  [(is-a? other matrix<%>)
	   (send other ->rectangle)]
	  [(is-a? other visible-matrix%)
	   (define other-m (get-field M other))
	   (if other-m
	       (send other-m ->rectangle)
	       (get-field R other))]
	  [else #f]))
      (recur my-mat other-mat))

    (define/override (equal-hash-code-of hash-code)
      (hash-code M))

    (define/override (equal-secondary-hash-code-of hash-code)
      (hash-code M))
    
    (init M_0)
    
    (field 
     [M (if (is-a? M_0 matrix<%>) M_0 #f)]
     [R (cond
          [M #f]
          [(pair? M_0) M_0]
          [else
           (error 'visible% "expects matrix or rectangle, given: ~e" M_0)])])

    (define/public (get-M) (if M M R))
    
    ;; create a matrix from this instance 
    (define/override (copy)
      (new visible-matrix%
           (M_0 (get-M)) (dc-proc dc-proc) (argb-proc argb-proc)
           (width width) (height height) (px px) (py py) (argb argb)))

    (define/private (->s-expr)
      (list (if R R (send M ->rectangle))
            (list (argb-vector (get-argb)) width px py)))
    
    (define/override (write f)
      (define x (format "~s" (->s-expr)))
      (define y (string->bytes/utf-8 x))
      (send f put y))
    
    (super-new)
    (set-snipclass matrix-snip-class)))

;; the snip class for matrices
(define matrix-snip-class% 
  (class cache-image-snip-class% 
    (super-new)
    (define/override (read f)
      (define b (send f get-bytes))
      (data->snip 
       (and b
            (not (equal? b #""))
            (with-handlers ((exn:fail:read? (λ (x) #f)))
              (scheme:read (open-input-bytes b))))))
    (define/override (data->snip data) 
      (define _ (unless data (error 'read "in matrix-snip-class% failed")))
      (define new-cache-image-snip (super data->snip (cadr data)))
      (define-values (w h) (send new-cache-image-snip get-size))
      (define M (car data))
      ;; .. but we need to produce a visible-matrix% instead
      (new visible-matrix% 
           (M_0 M)
           (dc-proc (send new-cache-image-snip get-dc-proc))
           (argb-proc (send new-cache-image-snip get-argb-proc))
           (width w)
           (height h)
           (argb (get-argb new-cache-image-snip))
           (px (get-px new-cache-image-snip))
           (py (get-py new-cache-image-snip))))))
(define get-argb (class-field-accessor cache-image-snip% argb))
(define get-px (class-field-accessor cache-image-snip% px))
(define get-py (class-field-accessor cache-image-snip% py))

;; setting up the 'snip class' 
(define matrix-snip-class (new matrix-snip-class%))
(send matrix-snip-class set-version 1)
(send matrix-snip-class set-classname (format "~s" `(lib "matrix.ss" "htdp")))
(send the-drscheme-snip-class #;(get-the-snip-class-list) add matrix-snip-class)
