#lang racket/base
(require racket/class
         racket/gui/base
         racket/pretty
         framework/notify
         macro-debugger/syntax-browser/interfaces)
(provide syntax-keymap%)

(define keymap/popup%
  (class* keymap% (keymap/popup<%>)
    (init editor)
    (super-new)
    (inherit add-function
             map-function
             chain-to-keymap)

    (define/public (add-context-menu-items menu)
      (void))

    (map-function "rightbutton" "popup-context-menu")
    (add-function "popup-context-menu"
                  (lambda (editor event)
                    (popup-context-menu editor event)))

    (define/private (popup-context-menu editor event)
      (define-values (x y)
        (send editor dc-location-to-editor-location
              (send event get-x)
              (send event get-y)))
      (define admin (send editor get-admin))
      (define menu (new popup-menu%))
      (add-context-menu-items menu)
      (send admin popup-menu menu x y))

    ;; FIXME: move out of constructor to use sites
    (chain-to-keymap (send editor get-keymap) #t)
    (send editor set-keymap this)))

(define syntax-keymap%
  (class keymap/popup%
    (init-field controller
                config)
    (inherit add-function
             map-function
             call-function
             chain-to-keymap)
    (super-new)

    (define/private (selected-syntax)
      (send controller get-selected-syntax))

    ;; Functionality

    (add-function "copy-syntax-as-text"
                  (lambda (_ event)
                    (define stx (send controller get-selected-syntax))
                    (when stx
                      (send the-clipboard set-clipboard-string
                            (let ([out (open-output-string)])
                              (pretty-print (syntax->datum stx) out)
                              (get-output-string out))
                            (send event get-time-stamp)))))

    (add-function "clear-syntax-selection"
                  (lambda (i e)
                    (send controller set-selected-syntax #f)))

    (add-function "show-syntax-properties"
                  (lambda (i e)
                    (send config set-props-shown? #t)))

    (add-function "hide-syntax-properties"
                  (lambda (i e)
                    (send config set-props-shown? #f)))

    (define ((pretty-print-as sym) i e)
      (let ([stx (selected-syntax)])
        (when (identifier? stx)
          (send config set-pretty-styles
                (hash-set (send config get-pretty-styles)
                          (syntax-e stx)
                          sym)))))

    (define/override (add-context-menu-items menu)
      (new menu-item% (label "Copy") (parent menu)
           (demand-callback
            (lambda (i)
              (send i enable (and (selected-syntax) #t))))
           (callback
            (lambda (i e)
              (call-function "copy-syntax-as-text" i e))))
      (new separator-menu-item% (parent menu))
      (new menu-item%
           (label "Clear selection")
           (parent menu)
           (demand-callback
            (lambda (i)
              (send i enable (and (selected-syntax) #t))))
           (callback 
            (lambda (i e)
              (call-function "clear-syntax-selection" i e))))
      (notify:menu-option/notify-box menu "View syntax properties"
                                     (get-field props-shown? config))
      (let ([pretty-menu
             (new menu%
                  (label "Change layout")
                  (parent menu)
                  (demand-callback
                   (lambda (i)
                     (send i enable (and (identifier? (selected-syntax)) #t)))))])
        (for ([sym+desc '((and    "like and")
                          (begin  "like begin (0 up)")
                          (lambda "like lambda (1 up)")
                          (do     "like do (2 up)"))])
          (new menu-item%
               (label (format "Format identifier ~a" (cadr sym+desc)))
               (parent pretty-menu)
               (demand-callback
                (lambda (i)
                  (let ([stx (selected-syntax)])
                    (when (identifier? stx)
                      (send i set-label
                            (format "Format ~s ~a" (syntax-e stx) (cadr sym+desc)))))))
               (callback
                (pretty-print-as (car sym+desc)))))))))
