#lang racket/base

(require (prefix-in planet: planet/config) racket/path racket/list)

;; `library-roots' is an alist of root-path, and the number of levels
;; below that which indicates a "library".  This is hard-wired now to
;; the different roots, and should probably be improved at some point.
;; `path->library-root' takes in a path and returns a path to the root
;; of its library, defined by the above.  (Assumes that none of the
;; roots is a subdirectory of another.)

(provide library-roots path->library-root)

(define library-roots
  `(,@(map (lambda (p) (cons p 1)) (current-library-collection-paths))
    ,(cons (planet:CACHE-DIR) 4)
    ;; add planet links, each as a root (if there is a change in
    ;; the format, this will just ignore these paths, but these
    ;; collections will throw an error in raco setup)
    ,@(with-handlers ([exn? (lambda (e)
                              (printf "WARNING: bad planet links at ~a:\n ~a"
                                      (planet:HARD-LINK-FILE) (exn-message e))
                              '())])
        (if (not (file-exists? (planet:HARD-LINK-FILE)))
          '()
          (with-input-from-file (planet:HARD-LINK-FILE)
            (lambda ()
              (let loop ([r '()])
                (let ([x (read)])
                  (if (eof-object? x)
                    (reverse r)
                    (let ([x (and (list? x) (= 7 (length x)) (list-ref x 4))])
                      (loop (if (bytes? x)
                              (cons (cons (simple-form-path (bytes->path x)) 0) r)
                              r))))))))))))

(define path->library-root
  (let ([t #f])
    (define (init-table)
      (set! t (make-hash))
      (for ([x (in-list library-roots)])
        (hash-set! t (reverse (explode-path (car x))) (cdr x))))
    (lambda (path)
      (unless (complete-path? path)
        (raise-type-error 'path->library-root "complete-path" path))
      (unless t (init-table))
      (let loop ([rpath (reverse (explode-path (simple-form-path path)))]
                 [subdir '()])
        (let ([x (hash-ref t rpath #f)])
          (cond [(and x ((length subdir) . >= . x))
                 (apply build-path (append (reverse rpath) (take subdir x)))]
                [(or x (null? rpath)) #f]
                [else (loop (cdr rpath) (cons (car rpath) subdir))]))))))
