(define C-PROC
  (lambda [c]
      (let [[p (pattern c)]]
         (and (rail p) (= (length p) 1)
              (= (first p) 'PROC-NF)))))

(define C-ARGS
  (lambda [c]
      (let [[p (pattern c)]]
         (and (rail p) (= (length p) 1)
              (= (first p) 'ARGS-NF)))))

(define C-FIRST
  (lambda [c]
      (let [[p (pattern c)]]
         (and (rail p) (= (length p) 1)
              (= (first p) 'FIRST-NF)))))

(define C-REST!
  (lambda [c]
      (let [[p (pattern c)]]
         (and (rail p) (= (length p) 1)
              (= (first p) 'REST-NF)))))

(define C-IF
  (lambda [c]
      (let [[p (pattern c)]]
         (and (rail p) (= (length p) 1)
              (= (first p) 'PREMISE-NF)))))

(define C-BEGIN
  (lambda [c]
      (let [[p (pattern c)]]
         (and (rail p) (= (length p) 1)
              (= (first p) 'JUNK-NF)))))

(define C-REPLY
  (lambda [c]
      (let [[p (pattern c)]]
         (and (rail p) (= (length p) 1)
              (= (first p) 'RESULT-NF)))))

(define C-SET
  (lambda [c]
      (let [[p (pattern c)]]
         (and (rail p) (= (length p) 1)
              (= (first p) 'RHS-NF)))))


(define TRACE-CONTINUATIONS
   (lambda [c]
      (let [[new-c ((tracer c) c)]]
         (if (closure new-c)
             (trace-continuations new-c)
             'OK))))



(define TRACE-NON-STANDARD-CONTINUATION
   (lambda [c]
      (begin
         (print-string "Non-standard continuation" primary-stream)
         (let [[[w x] (who c)]]
            (cond [(= w 'PROBABLY)
                   (begin
                      (print-string ", probably " primary-stream)
                      (print x primary-stream))]
                  [(= w 'INSIDE)
                   (begin
                      (print-string ", probably internal to " primary-stream)
                      (print x primary-stream))]
                  [$T (print-string w primary-stream)]))
         (newline primary-stream)
         (print-string "Backtrace will resume with whatever you THROW it." primary-stream)
         (newline primary-stream)
         (print-string "You'll be in an environment with these non-global variables: " primary-stream)
         (print-vector (non-local-variables (environment c)))
         (read-normalise-print #t (environment c) primary-stream))))



(define FFF
   (lambda reflect [exp env esc cont]
      (begin
         (print ps "+++ Backtrace +++ " cr)
         (trace-continuations ↑cont)
         (print ps cr "+++ End backtrace +++ " cr)
         (normalize (first exp) env esc cont))))



(define TRACE-C-PROC
   (lambda [c]
      (begin
         (print ps "C-PROC" cr
            "     PROC-NF => "
            (binding 'PROC-NF (closure-environment c)) cr)
         (binding 'CONT (closure-environment c)))))



(define TRACE-C-ARGS
   (lambda [c]
      (begin
         (print ps "C-ARGS" cr
            "     ARGS-NF => "
            (binding 'ARGS-NF (closure-environment c)) cr)
         (binding 'CONT (closure-environment c)))))



(define TRACE-C-FIRST
   (lambda [c]
      (begin
         (print ps "C-FIRST" cr
            "     FIRST-NF => "
            (binding 'FIRST-NF (closure-environment c)) cr)
         (binding 'CONT (closure-environment c)))))



(define TRACE-C-REST
   (lambda [c]
      (begin
         (print ps "C-REST" cr)
         (binding 'CONT (closure-environment c)))))


(define TRACER
   (lambda [x]
      (cond [(not (closure x)) trace-non-closure]
            [(c-proc x) trace-c-proc] 
            [(c-args x) trace-c-args] 
            [(c-first x) trace-c-first] 
            [(c-rest x) trace-c-rest] 
            [(c-if x) trace-c-if] 
            [(c-begin x) trace-c-begin] 
            [(c-set x) trace-c-set]
            [(c-reply x) trace-c-reply]
            [$T trace-non-standard-continuation])))



(define TRACE-NON-CLOSURE
   (lambda [x]
      (begin
         (print ps "Backtrace terminated at a non-closure: " cr
            x cr)
         'DONE)))



(define TRACE-C-REPLY
   (lambda [c]
      (begin
         (print ps "C-REPLY" cr
            "     RESULT-NF => "
            (binding 'LEVEL (closure-environment c)) cr)
         'DONE)))



(define TRACE-C-IF
   (lambda [c]
      (begin
         (print ps "C-IF" cr
            "     PREMISE-NF => "
            (binding 'PREMISE-NF (closure-environment c)) cr)
         (binding 'CONT (closure-environment c)))))



(define TRACE-C-BEGIN
   (lambda [c]
      (begin
         (print ps "C-BEGIN" cr)
         (binding 'CONT (closure-environment c)))))


(define TRACE-C-SET
   (lambda [c]
      (begin
         (print ps "C-SET" cr)         
         (binding 'CONT (closure-environment c)))))



(define WHO
   (lambda [x]
      (if (not (closure x))
          [" (not a closure)" '?]
          [" of unknown origin" '?])))



(define MAKE-FRAME
   (lambda [frame-kind frame-closure frame-env frame-cont frame-tag]
       (list frame-kind frame-closure frame-env frame-cont frame-tag)))

(define FRAME-KIND first)
(define FRAME-CLOSURE second)
(define FRAME-ENV third)
(define FRAME-CONT fourth)



(define EXTRACT-FRAME
   (lambda [proc]
      (cond [(not (closure proc))
             (error-extract-frame-expecting-closure)]
            [(c-proc! proc) (make-standard-frame 'PROC proc)]
            [(c-args! proc) (make-standard-frame 'ARGS proc)]
            [(c-first! proc) (make-standard-frame 'FIRST proc)]
            [(c-rest! proc) (make-standard-frame 'REST proc)]
            [(c-reply proc)
             (make-frame
               'REPLY
                proc
                \(binding 'ENV (closure-environment proc))
                'NONE
                '*YOU*)]
            [(c-begin proc) (make-standard-frame 'BLOCK proc)]
            [(c-set proc) (make-standard-frame 'SET proc)]
            [(c-if proc) (make-standard-frame 'IF proc)]
            [(= proc ↑id) (make-frame 'ID proc 'NONE 'NONE '*NONE*)]
            [$true (extract-frame-from-stranger proc)])))


(define MAKE-STANDARD-FRAME
   (lambda [frame-name proc]
      (make-frame
        frame-name
        proc
        \(binding 'ENV (closure-environment proc))
        \(binding 'CONT (closure-environment proc))
        '?)))



(define EXTRACT-FRAME-FROM-STRANGER
   (lambda [proc]
      (let [[non-globals (variables (non-global-segment (environment proc)))]]
         (begin
            (print ps "Non-standard continuation encountered." cr
               "Non-globals available to you: "
               non-globals cr 
               "Give me the 5 fields of stack frame: ")
            (designation (read-structure ps) (closure-environment proc))))))



(define FOO
   (lambda [x]
      (begin
         (foo (set x (if (and (or $F (ggg (= x 1)))) 1 2)))
         1)))



(define GGG
   (rlambda [[exp] env cont]
      (begin
         (backtrace cont)
         (normalise exp env cont))))



(define EXTRACT-STACK
   (lambda [proc]
      (let [[frame (extract-frame proc)]]
         (if (function (frame-cont frame))
             (cons frame (extract-stack ↑(frame-cont frame)))
             (list frame)))))



(define FRAME-TAG fifth)



(define COLLAPSE-STACK
   (lambda [frame-stack]
      (cond [(null frame-stack) frame-stack] 
            [(null (rest frame-stack)) frame-stack] 
            [(= ↑(frame-env (first frame-stack))
                ↑(frame-env (second frame-stack)))
             (collapse-stack
                (cons 
                   (make-frame
                     (clump (frame-kind (first frame-stack))
                       (frame-kind (second frame-stack)))
                     (clump (frame-closure (first frame-stack))
                       (frame-closure (second frame-stack)))
                     (frame-env (first frame-stack))
                     (frame-cont (first frame-stack))
                     (if (= (frame-tag (second frame-stack)) '?)
                         (frame-tag (first frame-stack))
                         (frame-tag (second frame-stack))))
                   (tail 2 frame-stack)))]
            [$T (cons (first frame-stack)
                      (collapse-stack (rest frame-stack)))])))



(define ENV-AFTER-EXPAND-CLOSURE
   (lambda [env proc]
      (if (and (sequence env) (closure proc)) 
          (let [[locals (pattern-variables proc)]
                [all-vars (map first (non-global-segment env))]
                [non-locals (map first (non-global-segment (environment proc)))]]
             (= (append locals non-locals) all-vars))
          $F)))



(define FILL-IN-TAGS
   (lambda [frame-stack]
      (map fill-in-frame-tag frame-stack)))



(define FILL-IN-FRAME-TAG
   (lambda [frame]
      (do [[x (non-global-segment (frame-env frame)) (rest x)]]
          [[(null x) frame]
           [(env-after-expand-closure (frame-env frame) (second (first x)))
            (make-frame (frame-kind frame)
                        (frame-closure frame)
                        (frame-env frame)
                        (frame-cont frame)
                        (first (first x)))]])))



(define SHOW-STACK
   (lambda [frame-stack]
      (if (null frame-stack)
          'OK
          (begin
             (show-frame (first frame-stack))
             (show-stack (rest frame-stack))))))



(define SHOW-FRAME
   (lambda [arg]
      (let [[tag (fourth arg)]]
         (print ps " <- " tag))))



(define BACKTRACE
   (lambda [cont]
      (letseq [[f1 (extract-stack ↑cont)]
               [f2 (collapse-stack f1)]
               [f3 (fill-in-tags f2)]]
         (begin
            (print ps cr "Present state")
            (show-stack f3)
            (print ps cr)))))