#lang racket/base

; Utility to build a sitemap and provide links in headers
(require
  racket/string)

(provide
  sitemap
  build-main-headers
  build-secondary-headers)

(define *sitemap-hash* (make-hash)) ; sitemap: path->level1
(define *links-to-level1* (make-hash)) ; path->path
(define *sitemap-list* (list)) ; list of level1 paths in the order of definition, for correctly rendering the level1
(define (set-sitemap-list! lst)
  (set! *sitemap-list* lst))

; Site structure
(struct sitepage
  (name ; Name displayed in headers, or #f if the page should not be shown in the header (but still considered when displaying the headers, mostly relevant for second-level pages)
   path ; Absolute path to the page
   link? ; Indicate if the link is clickable when on the same page
   children ; Children of the node
  ))

(define (make-sitepage name path link? children path-level1)
  (hash-set! *links-to-level1* path path-level1)
  (sitepage name path link? children))

; Syntax to build the sitemap tree
(define-syntax sitemap
  (syntax-rules ()
    ( (sitemap (level1-name level1-path level1-link? (level2-name level2-path level2-link?) ...) ...)
      (begin
        (begin
          (hash-set!
            *sitemap-hash*
            level1-path
            (make-sitepage
              level1-name
              level1-path
              level1-link?
              (let ((parent-path level1-path))
                (list
                  (make-sitepage
                    level2-name
                    level2-path
                    level2-link?
                    (list)
                    parent-path)
                  ...))
              level1-path))
          ...)
        (set-sitemap-list! (list level1-path ...))
        ))))

; Navigation link
(define (navigation-link str-path page)
  (if (not (sitepage-name page))
      ""
      `(section ((class "nav-item"))
        ,(if (or (not (equal? str-path (sitepage-path page)))
                  (sitepage-link? page))
            `(a ((href ,(sitepage-path page))) ,(sitepage-name page))
            (sitepage-name page)))))

; Build the navigation header of level 1
(define (build-main-headers current-path)
  (let ((str-path (string-join current-path "/" #:before-first "/")))
    (map
      (lambda (x)
        (navigation-link str-path (hash-ref *sitemap-hash* x)))
      *sitemap-list*)))

; Build the navigation header of level 2
(define (build-secondary-headers current-path)
  (define str-path (string-join current-path "/" #:before-first "/"))
  (define level-1 (hash-ref *sitemap-hash* (hash-ref *links-to-level1* str-path #f) #f))
  (cond
    (level-1
      (cons
        (navigation-link str-path level-1)
        (map
          (lambda (x)
            (navigation-link str-path x))
          (sitepage-children level-1))))
    ((null? current-path)
     `((section ((class "nav-item")) "*")))
    (#t
      ; Try to get the level by removing the last element of the path
      (build-secondary-headers (reverse (cdr (reverse current-path)))))
    ))
