#lang racket/base

;; Functions that create legend entries and lists of legend entries.

(require racket/class racket/match racket/list racket/string racket/sequence racket/contract
         "format.rkt"
         "draw.rkt"
         "utils.rkt"
         "area.rkt")

(provide (all-defined-out))

;; ===================================================================================================
;; Line legends

(define (line-legend-entry label color width style)
  (legend-entry label (λ (plot-area x-min x-max y-min y-max)
                        (define y (* 1/2 (+ y-min y-max)))
                        (send plot-area set-pen color width style)
                        (send plot-area set-alpha 1)
                        (send plot-area draw-line (vector x-min y) (vector x-max y)))))

(define (line-legend-entries label zs colors widths styles)
  (define z-min (first zs))
  (define z-max (last zs))
  (define digits (digits-for-range z-min z-max))
  (define hash
    (for/fold ([hash  empty]) ([z      (in-list zs)]
                               [color  (in-cycle (maybe-apply/list colors zs))]
                               [width  (in-cycle (maybe-apply/list widths zs))]
                               [style  (in-cycle (maybe-apply/list styles zs))])
      (define entry-label (real->plot-label z digits))
      (assoc-cons hash (list color width style) entry-label)))
  
  (reverse
   (for/list ([entry  (in-list hash)])
     (match-define (cons args vs) entry)
     (apply line-legend-entry
            (if (= 1 (length vs))
                (format "~a = ~a" label (first vs))
                (format "~a ∈ {~a}" label (string-join (reverse vs) ",")))
            args))))

;; ===================================================================================================
;; Rectangle legends

(define (rectangle-legend-entry label fill-color fill-style line-color line-width line-style)
  (legend-entry label (λ (plot-area x-min x-max y-min y-max)
                        (send plot-area set-brush fill-color fill-style)
                        (send plot-area set-pen line-color line-width line-style)
                        (send plot-area set-alpha 1)
                        (send plot-area draw-rectangle (vector x-min y-min) (vector x-max y-max)))))

(define (rectangle-legend-entries label zs fill-colors fill-styles line-colors line-widths line-styles)
  (define z-min (first zs))
  (define z-max (last zs))
  (define digits (digits-for-range z-min z-max))
  (define hash
    (for/fold ([hash  empty]) ([z           (in-list zs)]
                               [fill-color  (in-cycle (maybe-apply/list fill-colors zs))]
                               [fill-style  (in-cycle (maybe-apply/list fill-styles zs))]
                               [line-color  (in-cycle (maybe-apply/list line-colors zs))]
                               [line-width  (in-cycle (maybe-apply/list line-widths zs))]
                               [line-style  (in-cycle (maybe-apply/list line-styles zs))])
      (define entry-label (real->plot-label z digits))
      (assoc-cons hash (list fill-color fill-style line-color line-width line-style) entry-label)))
  
  (reverse
   (for/list ([entry  (in-list hash)])
     (match-define (cons (list fill-color fill-style line-color line-width line-style) vs) entry)
     (rectangle-legend-entry (if (= 1 (length vs))
                                 (format "~a = ~a" label (first vs))
                                 (format "~a ∈ {~a}" label (string-join (reverse vs) ",")))
                             fill-color fill-style line-color line-width line-style))))

;; ===================================================================================================
;; Interval legends

(define (interval-legend-entry label fill-color fill-style line-color line-width line-style
                               line1-color line1-width line1-style
                               line2-color line2-width line2-style)
  (legend-entry label (λ (plot-area x-min x-max y-min y-max)
                        (send plot-area set-alpha 1)
                        ;; rectangle
                        (send plot-area set-pen line-color line-width line-style)
                        (send plot-area set-brush fill-color fill-style)
                        (send plot-area draw-rectangle (vector x-min y-min) (vector x-max y-max))
                        ;; bottom line
                        (send plot-area set-pen line1-color line1-width line1-style)
                        (send plot-area draw-line (vector x-min y-max) (vector x-max y-max))
                        ;; top line
                        (send plot-area set-pen line2-color line2-width line2-style)
                        (send plot-area draw-line (vector x-min y-min) (vector x-max y-min)))))

(define (interval-legend-entries label zs fill-colors fill-styles line-colors line-widths line-styles
                                 line1-colors line1-widths line1-styles
                                 line2-colors line2-widths line2-styles)
  (define z-min (first zs))
  (define z-max (last zs))
  (define digits (digits-for-range z-min z-max))
  (define hash
    (for/fold ([hash  empty]) ([za           (in-list zs)]
                               [zb           (in-list (rest zs))]
                               [fill-color   (in-cycle (maybe-apply/list fill-colors zs))]
                               [fill-style   (in-cycle (maybe-apply/list fill-styles zs))]
                               [line-color   (in-cycle (maybe-apply/list line-colors zs))]
                               [line-width   (in-cycle (maybe-apply/list line-widths zs))]
                               [line-style   (in-cycle (maybe-apply/list line-styles zs))]
                               [line1-color  (in-cycle (maybe-apply/list line1-colors zs))]
                               [line1-width  (in-cycle (maybe-apply/list line1-widths zs))]
                               [line1-style  (in-cycle (maybe-apply/list line1-styles zs))]
                               [line2-color  (in-cycle (maybe-apply/list line2-colors zs))]
                               [line2-width  (in-cycle (maybe-apply/list line2-widths zs))]
                               [line2-style  (in-cycle (maybe-apply/list line2-styles zs))])
      (define entry-label
        (format "[~a,~a]" (real->plot-label za digits) (real->plot-label zb digits)))
      (assoc-cons hash
                  (list fill-color fill-style line-color line-width line-style
                        line1-color line1-width line1-style
                        line2-color line2-width line2-style)
                  entry-label)))
  
  (reverse
   (for/list ([entry  (in-list hash)])
     (match-define (cons (list fill-color fill-style line-color line-width line-style
                               line1-color line1-width line1-style
                               line2-color line2-width line2-style)
                         vs)
       entry)
     (interval-legend-entry (format "~a ∈ ~a" label (string-join (reverse vs) " ∪ "))
                            fill-color fill-style line-color line-width line-style
                            line1-color line1-width line1-style
                            line2-color line2-width line2-style))))

(define (contour-intervals-legend-entries label z-min z-max zs
                                          fill-colors fill-styles line-colors line-widths line-styles
                                          contour-colors contour-widths contour-styles)
  (define interval-zs (append (list z-min) zs (list z-max)))
  
  (define ccs (append (list 0)
                      (sequence-take (in-cycle (maybe-apply/list contour-colors zs)) 0 (length zs))
                      (list 0)))
  (define cws (append (list 0)
                      (sequence-take (in-cycle (maybe-apply/list contour-widths zs)) 0 (length zs))
                      (list 0)))
  (define css (append '(transparent)
                      (sequence-take (in-cycle (maybe-apply/list contour-styles zs)) 0 (length zs))
                      '(transparent)))
  
  (interval-legend-entries label interval-zs
                           fill-colors fill-styles line-colors line-widths line-styles
                           ccs cws css (rest ccs) (rest cws) (rest css)))

;; ===================================================================================================
;; Point legends

(define (point-legend-entry label symbol color size line-width)
  (legend-entry label (λ (plot-area x-min x-max y-min y-max)
                        (send plot-area set-pen color line-width 'solid)
                        (send plot-area set-alpha 1)
                        (send plot-area draw-glyphs
                              (list (vector (* 1/2 (+ x-min x-max)) (* 1/2 (+ y-min y-max))))
                              symbol size))))

(define (vector-field-legend-entry label color line-width line-style)
  (legend-entry label (λ (plot-area x-min x-max y-min y-max)
                        (send plot-area set-pen color line-width line-style)
                        (send plot-area set-alpha 1)
                        (send plot-area draw-arrow-glyph
                              (vector (* 1/2 (+ x-min x-max)) (* 1/2 (+ y-min y-max)))
                              (* 1/4 (- x-max x-min)) 0))))
