;; Graph Paths Example
;; Demonstrates BFS traversal and Dijkstra shortest paths

(import assert :refer :all)
(import binding :refer :all)
(import control :refer :all)
(import dict :refer :all)
(import display :refer :all)
(import functional :refer :all)
(import looping :refer :all)
(import threading :refer :all)

(define contains?
  (lambda (lst value)
    (any? (lambda (x) (= x value)) lst)))

(define remove-value
  (lambda (lst value)
    (filter (lambda (x) (not (= x value))) lst)))

(define bfs
  (lambda (graph start)
    (define queue (list start))
    (define visited (list start))
    (define order (list start))
    (while (not (null? queue))
      (define node (car queue))
      (set! queue (cdr queue))
      (define edges (get node graph))
      (do-list (edge edges)
        (define neighbor (car edge))
        (if (not (contains? visited neighbor))
            (begin
              (set! visited (append visited (list neighbor)))
              (set! order (append order (list neighbor)))
              (set! queue (append queue (list neighbor)))))))
    order))

(define min-distance
  (lambda (nodes dist-env)
    (reduce (lambda (best node)
              (if (< (get node dist-env)
                     (get best dist-env))
                  node
                  best))
            nodes)))

(define dijkstra
  (lambda (graph nodes start)
    (define dist (new.env))
    (define prev (new.env))
    (do-list (n nodes)
      (assign n 9999 dist)
      (assign n #nil prev))
    (assign start 0 dist)
    (define unvisited nodes)
    (while (not (null? unvisited))
      (define current (min-distance unvisited dist))
      (set! unvisited (remove-value unvisited current))
      (define edges (get current graph))
      (do-list (edge edges)
        (define neighbor (car edge))
        (define weight (car (cdr edge)))
        (define alt (+ (get current dist) weight))
        (if (< alt (get neighbor dist))
            (begin
              (assign neighbor alt dist)
              (assign neighbor current prev)))))
    (dict :dist dist :prev prev)))

(define reconstruct-path
  (lambda (prev-env start goal)
    (define current goal)
    (define path (list current))
    (while (not (= current start))
      (define parent (get current prev-env))
      (if (null? parent)
          (begin
            (set! path (list))
            (set! current start))
          (begin
            (set! path (cons parent path))
            (set! current parent))))
    path))

(define graph
  (dict
   :A (list (list "B" 1) (list "C" 2))
   :B (list (list "C" 1) (list "D" 4) (list "E" 6))
   :C (list (list "D" 5) (list "E" 7))
   :D (list (list "E" 2))
   :E (list)))

(define nodes (list "A" "B" "C" "D" "E"))

(println "=== Graph Traversal ===")
(define bfs-order (bfs graph "A"))
(assert-equal (list "A" "B" "C" "D" "E") bfs-order)
(println (string-concat "BFS order from A: " bfs-order))

(println "\n=== Shortest Paths ===")
(define dijkstra-result (dijkstra graph nodes "A"))
(define dist-env (get "dist" dijkstra-result))
(define prev-env (get "prev" dijkstra-result))
(assert-equal 0 (get "A" dist-env))
(assert-equal 1 (get "B" dist-env))
(assert-equal 2 (get "C" dist-env))
(assert-equal 5 (get "D" dist-env))
(assert-equal 7 (get "E" dist-env))
(define shortest-path (reconstruct-path prev-env "A" "E"))
(define shortest-cost (get "E" dist-env))
(assert-equal (list "A" "B" "E") shortest-path)
(assert-equal 7 shortest-cost)

(println (string-concat "Shortest path A -> E: " shortest-path))
(println (string-concat "Total cost: " shortest-cost))

(println "\nExample complete!")
