#lang racket/base
(require racket/class
         racket/draw
         ffi/unsafe
          "../../syntax.rkt"
          "../common/event.rkt"
         "item.rkt"
	 "utils.rkt"
	 "const.rkt"
	 "window.rkt"
	 "wndclass.rkt"
         "types.rkt")

(provide
 (protect-out slider%))

(define TBS_VERT                #x0002)
(define TBS_HORZ                #x0000)

(define TBM_GETPOS              WM_USER)
(define TBM_GETRANGEMIN         (+ WM_USER 1))
(define TBM_GETRANGEMAX         (+ WM_USER 2))
(define TBM_GETTIC              (+ WM_USER 3))
(define TBM_SETTIC              (+ WM_USER 4))
(define TBM_SETPOS              (+ WM_USER 5))
(define TBM_SETRANGE            (+ WM_USER 6))
(define TBM_SETRANGEMIN         (+ WM_USER 7))
(define TBM_SETRANGEMAX         (+ WM_USER 8))

(define SS_CENTER            #x00000001)

(define THICKNESS 24)
(define MIN_LENGTH 80)

(defclass slider% item%
  (init parent cb
        label
        val lo hi
        x y w
        style
        font)
  (inherit set-control-font
           auto-size)

  (define callback cb)
  (define vertical? (memq 'vertical style))

  (define panel-hwnd
    (if (memq 'plain style)
        #f
        (CreateWindowExW 0
                         "PLTPanel"
                         #f
                         (bitwise-ior WS_CHILD)
                         0 0 0 0
                         (send parent get-content-hwnd)
                         #f
                         hInstance
                         #f)))

  (define slider-hwnd
    (CreateWindowExW/control 0
                             "PLTmsctls_trackbar32"
                             label
                             (bitwise-ior WS_CHILD WS_CLIPSIBLINGS
                                          (if vertical?
                                              TBS_VERT
                                              TBS_HORZ)
                                          (if panel-hwnd
                                              WS_VISIBLE
                                              0))
                             0 0 0 0
                             (or panel-hwnd 
                                 (send parent get-content-hwnd))
                             #f
                             hInstance
                             #f))

  (define value-hwnd
    (and panel-hwnd
         (CreateWindowExW/control 0
                                  "STATIC"
                                  (format "~s" val)
                                  (bitwise-ior SS_CENTER WS_CHILD WS_CLIPSIBLINGS WS_VISIBLE)
                                  0 0 0 0
                                  panel-hwnd
                                  #f
                                  hInstance
                                  #f)))

  (define hwnd (or panel-hwnd slider-hwnd))

  (super-new [callback cb]
             [parent parent]
             [hwnd hwnd]
             [extra-hwnds
              (if panel-hwnd
                  (list slider-hwnd value-hwnd)
                  null)]
             [style style])

  (define/override (is-hwnd? a-hwnd)
    (or (ptr-equal? hwnd a-hwnd)
        (and panel-hwnd
             (or (ptr-equal? slider-hwnd a-hwnd)
                 (ptr-equal? value-hwnd a-hwnd)))))
  
  (when value-hwnd
    (set-control-font font value-hwnd))

  (define value-w 0)
  (define value-h 0)

  (if panel-hwnd
      (auto-size font
                 (list (format "~s" lo)
                       (format "~s" hi))
                 0 0 0 0 (lambda (w h)
                           (set! value-w w)
                           (set! value-h h)
                           (if vertical?
                               (set-size #f #f (+ THICKNESS w) (max h MIN_LENGTH))
                               (set-size #f #f (max w MIN_LENGTH) (+ THICKNESS h)))))
      (if vertical?
          (set-size #f #f THICKNESS MIN_LENGTH)
          (set-size #f #f MIN_LENGTH THICKNESS)))

  (SendMessageW slider-hwnd TBM_SETRANGE 1 (MAKELPARAM lo hi))
  (set-value val)

  (define/override (set-size x y w h)
    (super set-size x y w h)
    (when panel-hwnd
      (unless (or (= w -1) (= h -1))
        (if vertical?
            (let ([dx (quotient (- w THICKNESS value-w) 2)])
              (MoveWindow slider-hwnd (->screen dx) 0
			  (->screen THICKNESS) (->screen h) #t)
              (MoveWindow value-hwnd (->screen (+ dx THICKNESS)) (->screen (quotient (- h value-h) 2))
			  (->screen value-w) (->screen value-h) #t))
            (let ([dy (quotient (- h THICKNESS value-h) 2)])
              (MoveWindow slider-hwnd 0 (->screen dy)
			  (->screen w) (->screen THICKNESS) #t)
              (MoveWindow value-hwnd (->screen (quotient (- w value-w) 2)) (->screen (+ dy THICKNESS))
			  (->screen value-w) (->screen value-h) #t))))))

  (define/override (size->screen v) (->screen* v))

  (define/override (control-scrolled)
    (when value-hwnd
      (set-text (get-value)))
    (queue-window-event this (lambda ()
                               (callback this
                                         (new control-event%
                                              [event-type 'slider]
                                              [time-stamp (current-milliseconds)])))))

  (define/public (set-value val)
    (SendMessageW slider-hwnd TBM_SETPOS 1 val)
    (when value-hwnd
      (set-text val)))
  
  (define/private (set-text val)
    (SetWindowTextW value-hwnd (format "~s" val)))
    
  (define/public (get-value)
    (SendMessageW slider-hwnd TBM_GETPOS 0 0)))
