(module mrpanel mzscheme
  (require mzlib/class
	   mzlib/class100
	   (prefix wx: "kernel.ss")
	   "lock.ss"
	   "const.ss"
	   "check.ss"
	   "helper.ss"
	   "wx.ss"
	   "kw.ss"
	   "wxpanel.ss"
	   "mrwindow.ss"
	   "mrcontainer.ss")

  (provide pane%
	   vertical-pane%
	   horizontal-pane%
	   grow-box-spacer-pane%
	   panel%
	   vertical-panel%
	   horizontal-panel%
	   tab-panel%
	   group-box-panel%)

  (define-keywords pane%-keywords
    subarea%-keywords
    container%-keywords
    area%-keywords)

  (define-local-member-name get-initial-label)

  (define pane%
    (class100*/kw (make-subarea% (make-container% area%)) ()
		  [(parent) pane%-keywords]
      (private-field [wx #f])
      (sequence 
	(let* ([who (cond ; yuck! - we do this to make h-p and v-p subclasses of p
		     [(is-a? this vertical-pane%) 'vertical-pane]
		     [(is-a? this horizontal-pane%) 'horizontal-pane]
		     [(is-a? this grow-box-spacer-pane%) 'grow-box-spacer-pane]
		     [else 'pane])]
	       [cwho `(constructor ,who)])
	  (check-container-parent cwho parent)
	  (as-entry
	   (lambda ()
	     (super-init (lambda ()
                           (set! wx (make-object (case who
                                                   [(vertical-pane) wx-vertical-pane%]
                                                   [(horizontal-pane) wx-horizontal-pane%]
                                                   [(grow-box-spacer-pane) wx-grow-box-pane%]
                                                   [else wx-pane%])
                                                 this this (mred->wx-container parent) null
                                                 #f))
                           wx)
			 (lambda () wx)
			 (lambda () wx)
			 (lambda ()
			   (check-container-ready cwho parent))
			 parent)
	     (send (send wx area-parent) add-child wx)))
	  (send parent after-new-child this)))))

  (define vertical-pane% (class100*/kw pane% () [(parent) pane%-keywords] (sequence (super-init parent))))
  (define horizontal-pane% (class100*/kw pane% () [(parent) pane%-keywords] (sequence (super-init parent))))
  (define grow-box-spacer-pane% (class100*/kw pane% () [(parent) pane%-keywords] (sequence (super-init parent))))

  (define-keywords panel%-keywords
    window%-keywords
    subarea%-keywords
    container%-keywords
    area%-keywords)

  (define panel%
    (class100*/kw (make-subwindow%
                   (make-area-container-window% 
                    (make-window% #f (make-subarea% (make-container% area%)))) )
                  (subwindow<%>)
		  [(parent [style null]) panel%-keywords]
      (private-field [wx #f])
      (public [get-initial-label (lambda () #f)])
      (sequence 
	(let* ([who (cond ; yuck! - we do this to make h-p and v-p subclasses of p
		     [(is-a? this tab-panel%) 'tab-panel]
		     [(is-a? this group-box-panel%) 'group-box-panel]
		     [(is-a? this vertical-panel%) 'vertical-panel]
		     [(is-a? this horizontal-panel%) 'horizontal-panel]
		     [else 'panel])]
	       [cwho `(constructor ,who)]
               [can-canvas? (memq who '(vertical-panel
                                        horizontal-panel
                                        panel))]
               [as-canvas? (lambda () (or (memq 'vscroll style)
                                          (memq 'auto-vscroll style)
                                          (memq 'hscroll style)
                                          (memq 'auto-hscroll style)))])
	  (check-container-parent cwho parent)
	  (check-style cwho #f (append '(border deleted)
                                       (if can-canvas? 
                                           '(hscroll vscroll auto-hscroll auto-vscroll)
                                           null)) 
                       style)
	  (as-entry
	   (lambda ()
	     (super-init (lambda () (set! wx (make-object (case who
							    [(vertical-panel) 
                                                             (if (as-canvas?)
                                                                 wx-vertical-canvas-panel%
                                                                 wx-vertical-panel%)]
							    [(tab-panel) wx-vertical-tab-panel%]
							    [(group-box-panel) wx-vertical-group-panel%]
							    [(horizontal-panel) 
                                                             (if (as-canvas?)
                                                                 wx-horizontal-canvas-panel%
                                                                 wx-horizontal-panel%)]
							    [else (if (as-canvas?)
                                                                      wx-canvas-panel%
                                                                      wx-panel%)])
							  this this (mred->wx-container parent) 
                                                          (cons 'transparent style)
                                                          (get-initial-label)))
                                 wx)
			 (lambda () wx) 
			 (lambda () wx) 
			 (lambda () (check-container-ready cwho parent))
			 #f parent #f)
	     (unless (memq 'deleted style)
	       (send (send wx area-parent) add-child wx))))
	  (send parent after-new-child this)))))

  (define vertical-panel% 
    (class100*/kw panel% () 
                  [(parent [style null]) panel%-keywords] 
                  (sequence (super-init parent style))
                  (public [set-orientation (λ (x) (send (mred->wx this) set-orientation x))]
                          [get-orientation (λ () (send (mred->wx this) get-orientation))])))
  (define horizontal-panel% 
    (class100*/kw panel% () 
                  [(parent [style null]) panel%-keywords] 
                  (sequence (super-init parent style))
                  (public [set-orientation (λ (x) (send (mred->wx this) set-orientation x))]
                          [get-orientation (λ () (send (mred->wx this) get-orientation))])))

  (define list-append append)

  (define tab-panel%
    (class100*/kw vertical-panel% ()
		  [(choices parent [callback (lambda (b e) (void))] [style null] [font no-val]) panel%-keywords]
      (private-field [save-choices choices])
      (override [get-initial-label (lambda () save-choices)])

      (sequence
	(let ([cwho '(constructor tab-panel)])
	  (unless (and (list? choices) (andmap label-string? choices))
	    (raise-type-error (who->name cwho) "list of strings (up to 200 characters)" choices))
	  (check-callback cwho callback)
	  (check-container-parent cwho parent)
	  (check-style cwho #f '(deleted no-border) style)
	  (check-font cwho font))
	(super-init parent (if (memq 'no-border style)
                               (if (eq? (car style) 'no-border)
                                   (cdr style)
                                   (list (car style)))
                               (cons 'border style)))
        (send (mred->wx this) set-callback (lambda (wx e) (callback (wx->mred wx) e))))

      (public
	[get-number (lambda () (length save-choices))]
	[append (entry-point
		 (lambda (n)
		   (check-label-string '(method tab-panel% append) n)
		   (let ([n (string->immutable-string n)])
		     (set! save-choices (list-append save-choices (list n)))
		     (send (mred->wx this) append n))))]
	[get-selection (lambda () (and (pair? save-choices)
				       (send (mred->wx this) get-selection)))]
	[set-selection (entry-point
			(lambda (i)
			  (check-item 'set-selection i)
			  (send (mred->wx this) set-selection i)))]
	[delete (entry-point
		 (lambda (i)
		   (check-item 'delete i)
		   (set! save-choices (let loop ([p 0][l save-choices])
					(if (= p i)
					    (cdr l)
					    (cons (car l) (loop (add1 p) (cdr l))))))
		   (send (mred->wx this) delete i)))]
	[set-item-label (entry-point
			 (lambda (i s)
			   (check-item 'set-item-label i)
			   (check-label-string '(method tab-panel% set-item-label) s)
			   (let ([s (string->immutable-string s)])
                             (set! save-choices (let loop ([save-choices save-choices][i i])
                                                  (if (zero? i)
                                                      (cons s (cdr save-choices))
                                                      (cons (car save-choices) (loop (cdr save-choices) (sub1 i))))))
			     (send (mred->wx this) set-label i s))))]
	[set
	 (entry-point (lambda (l) 
			(unless (and (list? l) (andmap label-string? l))
			  (raise-type-error (who->name '(method tab-panel% set)) 
					    "list of strings (up to 200 characters)" l))
			(set! save-choices (map string->immutable-string l))
			(send (mred->wx this) set l)))]
	[get-item-label (entry-point
			 (lambda (i)
			   (check-item 'get-item-label i)
			   (list-ref save-choices i)))])
      
      (private
	[check-item
	 (lambda (method n)
	   (check-non-negative-integer `(method tab-panel% ,method) n)
	   (let ([m (length save-choices)])
	     (unless (< n m)
	       (raise-mismatch-error (who->name `(method tab-panel% ,method)) 
				     (if (zero? m)
					 "panel has no tabs; given index: " 
					 (format "panel has only ~a tabs, indexed 0 to ~a; given out-of-range index: " 
						 m (sub1 m)))
				     n))))])))

  (define group-box-panel%
    (class100*/kw vertical-panel% ()
		  [(label parent [style null] [font no-val]) panel%-keywords]
      (private-field
       [lbl label])
      (override [get-initial-label (lambda () lbl)])

      (sequence
	(let ([cwho '(constructor group-box-panel)])
	  (check-label-string cwho label)
	  (check-container-parent cwho parent)
	  (check-style cwho #f '(deleted) style)
	  (check-font cwho font))

	;; Technically a bad way to change margin defaults, since it's
	;;  implemented with an update after creation:
	(when (eq? horiz-margin no-val) (set! horiz-margin 2))
	(when (eq? vert-margin no-val) (set! vert-margin 2))

	(super-init parent (if (memq 'deleted style)
                               '(deleted)
                               null)))

      (override
	[set-label (entry-point
		    (lambda (s)
		      (check-label-string '(method group-box-panel% set-label) s)
		      (set! lbl (if (immutable? s)
				    s
				    (string->immutable-string s)))
		      (send (mred->wx this) set-label s)))]
	[get-label (lambda () lbl)]))))
