(module wxlitem racket/base
  (require racket/class
           racket/file
           (only-in racket/base remq)
           racket/snip/private/prefs
           (prefix-in wx: "kernel.rkt")
           "lock.rkt"
           "helper.rkt"
           "const.rkt"
           "wx.rkt"
           "check.rkt"
           "wxwindow.rkt"
           "wxitem.rkt"
           "wxpanel.rkt")

  (provide (protect-out wx-choice%
                        wx-list-box%
                        wx-radio-box%
                        wx-gauge%
                        wx-slider%))

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

  (define (is-horiz? style parent)
    (cond
     [(memq 'vertical-label style) #f]
     [(memq 'horizontal-label style) #t]
     [else (eq? (send (send parent get-window) get-label-position) 'horizontal)]))

  (define (make-sub horiz? proxy this ha va)
    (if horiz?
        (begin
          (send this alignment ha va)
          this)
        (let ([p (make-object wx-vertical-pane% #f proxy this null #f)])
	  (send p skip-enter-leave-events #t)
          (send p skip-subwindow-events? #t)
          (send (send p area-parent) add-child p)
          (send p alignment ha va)
          p)))

  (define (make-label label proxy p font)
    (and label 
         (let ([l (make-object wx-message% #f proxy p label -1 -1 null font)])
           (send l x-margin 0) (send l y-margin 0)
	   (send l skip-enter-leave-events #t)
           (send l skip-subwindow-events? #t)
           l)))

  (define (filter-style style)
    (remq 'deleted style))

  (define-syntax-rule (bounce c (m . args) ...)
    (begin
      (define/public m (lambda args (send c m . args)))
      ...))

  ;; ----------------------------------------
  
  (define wx-label-panel%
    (class wx-control-horizontal-panel% 
      (init proxy parent label style font halign valign)
      (inherit area-parent skip-enter-leave-events set-event-positions-wrt)
      (define c #f)

      (define/override (enable on?) (if c (send c enable on?) (void)))
      (define/override (is-enabled?) (if c (send c is-enabled?) #t))
      (define/override (is-window-enabled?) (if c (send c is-window-enabled?) #t))

      (super-make-object #f proxy parent (if (memq 'deleted style) '(deleted) null) #f)
      (skip-enter-leave-events #t)
      (unless (memq 'deleted style)
        (send (area-parent) add-child this))
      (define horiz? (is-horiz? style parent))
      (define p (make-sub horiz? proxy this (if horiz? 'left halign) valign))

      (define l (make-label label proxy p font))
      (define/public (set-label s) (when l (send l set-label s)))
      (define/public (get-label) (and l (send l get-label)))

      (define/override (client-to-screen x y)
        (if c
            (send c client-to-screen x y)
            (super client-to-screen x y)))
      (define/override (screen-to-client x y)
        (if c
            (send c screen-to-client x y)
            (super screen-to-client x y)))

      (define/public (get-p) p)
      (define/public (set-c v sx? sy?) 
        (set! c v)
        (set-event-positions-wrt c)
        (when l (send l set-event-positions-wrt c))
        (when p (send p set-event-positions-wrt c))
        (send c stretchable-in-x sx?)
        (send c stretchable-in-y sy?)
        (send c skip-subwindow-events? #t))))

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

  (define wx-internal-choice% 
    (class (make-window-glue% (make-simple-control% wx:choice% 0 0))
      (init mred proxy parent cb label x y w h choices style font)
      (override*
       [handles-key-code 
        (lambda (x alpha? meta?) 
          (or (memq x '(up down))
              (and alpha? (not meta?))))])
      (super-make-object mred proxy style parent cb label x y w h choices (cons 'deleted style) font)))

  (define wx-choice%
    (class wx-label-panel% 
      (init mred proxy parent cb label x y w h choices style font)
      (inherit stretchable-in-y stretchable-in-x get-p set-c)

      (super-make-object proxy parent label style font 'left 'center)

      (define c (make-object wx-internal-choice% mred proxy (get-p) cb label x y w h choices 
                             (filter-style style) font))
      (set-c c #t #f)

      (bounce 
       c
       (set-selection i)
       (get-selection)
       (number)
       (clear)
       (append lbl)
       (delete i))

      (stretchable-in-y #f)
      (stretchable-in-x #f)))

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

  (define list-box-wheel-step #f)

  (define wx-internal-list-box%
    (make-window-glue% 
     (class (make-control% wx:list-box% 0 0 #t #t)
       (init parent cb label kind x y w h choices style font 
             label-font columns column-order)
       (inherit get-first-item
		set-first-visible-item
                number-of-visible-items)
       (private*
        [scroll (lambda (dir)
                  (unless list-box-wheel-step
                    (set! list-box-wheel-step (get-preference* '|GRacket:wheelStep| (lambda () 3)))
                    (unless (and (number? list-box-wheel-step)
                                 (exact? list-box-wheel-step)
                                 (integer? list-box-wheel-step)
                                 (<= 1 list-box-wheel-step 100))
                      (set! list-box-wheel-step 3)))
                  (let ([top (get-first-item)])
                    (set-first-visible-item
                     (max 0 (+ top (* (min list-box-wheel-step (number-of-visible-items)) dir))))))])
       (override*
        [handles-key-code (lambda (x alpha? meta?)
                            (case x
                              [(up down) #t]
                              [else (and alpha? (not meta?))]))]
        [pre-on-char (lambda (w e)
                       (or (super pre-on-char w e)
                           (and (not (eq? (system-type) 'macosx)) ; scrolling is built into NSListView 
                                (case (send e get-key-code)
                                  [(wheel-up) (scroll -1) #t]
                                  [(wheel-down) (scroll 1) #t]
                                  [else #f]))))])
       (super-make-object style parent cb label kind x y w h choices (cons 'deleted style) font 
                          label-font columns column-order))))

  (define wx-list-box%
    (class wx-label-panel% 
      (init mred proxy parent cb label kind x y w h choices style font label-font columns column-order)
      (inherit get-p set-c)

      (super-make-object proxy parent label style font 'left 'top)

      (define c (make-object wx-internal-list-box% mred proxy (get-p) cb label kind x y w h choices 
                             (filter-style style) font label-font columns column-order))
      (set-c c #t #t)

      (bounce
       c
       (get-label-font)
       (set-string i s col)
       (set-selection i)
       (get-selection)
       (get-selections)
       (visible-range)
       (get-first-item)
       (number-of-visible-items)
       (set-first-visible-item i)
       (number)
       (get-row n)
       (set-data i v)
       (get-data i)
       (selected? i)
       (delete i)
       (clear)
       (set choices . more)
       (reset)
       (get-column-order)
       (set-column-order l)
       (set-column-label i l)
       (set-column-size i w mn mx)
       (get-column-size i)
       (delete-column i)
       (append-column l))
      (define/public select 
        (case-lambda
         [(i) (send c select i)]
         [(i on?) (send c select i on?)]
         [(i on? extend?) (send c select i on? extend?)]))
      (define/public append
        (case-lambda
         [(s) (send c append s)]
         [(s v) (send c append s v)]))))

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

  (define wx-internal-radio-box%
    (make-window-glue% 
     (class (make-simple-control% wx:radio-box% 0 0)
       (init parent cb label x y w h choices major style font)
       (inherit number orig-enable set-selection command)
       (override*
        [enable
         (case-lambda
	   [(on?) (super enable on?)]
	   [(which on?) (when (< -1 which (number))
			  (vector-set! enable-vector which (and on? #t))
			  (orig-enable which on?))])]
        [is-enabled?
         (case-lambda
	   [() (super is-enabled?)]
	   [(which) (and (< -1 which (number))
			 (vector-ref enable-vector which))])])

       (define is-vertical? (memq 'vertical style))
       (public*
        [vertical? (lambda () is-vertical?)]
        [char-to-button (lambda (i)
                          (as-exit
                           (lambda ()
                             (set-selection i)
                             (command (make-object wx:control-event% 'radio-box)))))])

       (super-make-object style parent cb label x y w h choices major (cons 'deleted style) font)

       (define enable-vector (make-vector (number) #t)))))

  (define wx-radio-box%
    (class wx-label-panel% 
      (init mred proxy parent cb label x y w h choices major style font)
      (inherit stretchable-in-y stretchable-in-x get-p set-c)

      (super-make-object proxy parent label style font 'left 'center)

      (define c (make-object wx-internal-radio-box% mred proxy (get-p) cb label x y w h choices 
                             major (filter-style style) font))
      (set-c c #t #t)

      (define enable-vector (make-vector (length choices) #t))

      (define/override enable
        (case-lambda
         [(on?) (super enable on?)]
         [(i on?) 
          (when (< -1 i (vector-length enable-vector))
            (vector-set! enable-vector i on?)
            (send c enable-button i on?))]))
      
      (define/override is-enabled?
        (case-lambda
         [() (super is-enabled?)]
         [(which) (and (< -1 which (vector-length enable-vector))
                       (vector-ref enable-vector which))]))

      (bounce 
       c
       (button-focus i)
       (set-selection i)
       (get-selection))
      (stretchable-in-y #f)
      (stretchable-in-x #f)))

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

  (define wx-internal-gauge%
    (make-window-glue% 
     (class (make-control% wx:gauge% 0 0 #f #f)
       (init parent label range style font)
       (inherit get-client-size get-width get-height set-size 
		stretchable-in-x stretchable-in-y set-min-height set-min-width
		get-parent)
       (override* [gets-focus? (lambda () #f)])
       ;; # pixels per unit of value.
       (define pixels-per-value 1)
       (super-make-object style parent label range -1 -1 -1 -1 (cons 'deleted style) font)

       (let-values ([(client-width client-height) (get-two-int-values 
                                                   (lambda (a b) (get-client-size a b)))])
         (let ([delta-w (- (get-width) client-width)]
               [delta-h (- (get-height) client-height)]
               [vertical-labels? (eq? (send (send (get-parent) get-window) get-label-position) 'vertical)]
               [horizontal? (memq 'horizontal style)])
           (set-min-width (if horizontal?
                              (let ([cw (min const-max-gauge-length
                                             (* range pixels-per-value))])
                                (max (if vertical-labels?
                                         cw
                                         (+ cw delta-w))
                                     (get-width)))
                              ;; client-height is the default
                              ;; dimension in the minor direction.
                              (+ client-width delta-w)))
           (set-min-height (if horizontal?
                               (+ client-height delta-h)
                               (let ([ch (min const-max-gauge-length
                                              (* range pixels-per-value))])
                                 (max (if vertical-labels?
                                          (+ ch delta-h)
                                          ch)
                                      (get-height)))))))

       (if (memq 'horizontal style)
           (begin
             (stretchable-in-x #t)
             (stretchable-in-y #f))
           (begin
             (stretchable-in-x #f)
             (stretchable-in-y #t))))))

  (define wx-gauge%
    (class wx-label-panel% 
      (init mred proxy parent label range style font)
      (inherit stretchable-in-y stretchable-in-x get-p set-c)
      
      (super-make-object proxy parent label style font 'center 'center)
      
      (define c (make-object wx-internal-gauge% mred proxy (get-p) label range
                             (filter-style style) font))
      (set-c c 
             (memq 'horizontal style)
             (memq 'vertical style))

      (bounce 
       c
       (get-range)
       (set-range rng)
       (get-value)
       (set-value v))
      (let ([h? (and (memq 'horizontal style) #t)])
        (stretchable-in-x h?)
        (stretchable-in-y (not h?)))))

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

  (define wx-internal-slider%
    (make-window-glue% 
     (class (make-control% wx:slider% 0 0 #f #f)
       (init parent func label value min-val max-val style font)
       (inherit set-min-width set-min-height stretchable-in-x stretchable-in-y
		get-client-size get-width get-height get-parent)
       ;; # pixels per possible setting.
       (define pixels-per-value 3)
       ;; 3 is good because with horizontal sliders under Xt, with 1 or 2
       ;; pixels per value, the thumb is too small to display the number,
       ;; which looks bad.
       
       (super-make-object style parent func label value min-val max-val -1 -1 -1 (cons 'deleted style) font)
	 
       (let-values ([(client-w client-h) (get-two-int-values (lambda (a b)
                                                               (get-client-size a b)))])
         (let* ([horizontal? (memq 'horizontal style)]
                [vertical-labels? (eq? (send (send (get-parent) get-window) get-label-position) 'vertical)]
                [range (+ (* pixels-per-value (add1 (- max-val min-val)))
                          (cond
                           [(and horizontal? (not vertical-labels?)) (- (get-width) client-w)]
                           [(and (not horizontal?) vertical-labels?) (- (get-height) client-h)]
                           [else 0]))])
           ((if horizontal? (lambda (v) (set-min-width v)) (lambda (v) (set-min-height v)))
            (max ((if horizontal? (lambda () (get-width)) (lambda () (get-height))))
                 (min const-max-gauge-length range)))
           (stretchable-in-x horizontal?)
           (stretchable-in-y (not horizontal?)))))))

  (define wx-slider%
    (class wx-label-panel% 
      (init mred proxy parent func label value min-val max-val style font)
      (inherit stretchable-in-y stretchable-in-x get-p set-c)

      (super-make-object proxy parent label style font 'center 'center)

      (define c (make-object wx-internal-slider% mred proxy (get-p) func label value min-val max-val
                             (filter-style style) font))

      (let ([vert? (or (memq 'vertical style)
                       (memq 'upward style))])
        (set-c c
               (not vert?)
               vert?)
        (let ([h? (not vert?)])
          (stretchable-in-x h?)
          (stretchable-in-y (not h?))))

      (bounce 
       c
       (get-value)
       (set-value v))))

)
