; A file is one of
;   - a symbol
;     representing a "simple" file's name
;   - a directory
;     (make-dir name contents)
;     where name is a symbol, and contents is a l-o-f.

; A list-of-files (l-o-f) is one of
;   - empty
;   - (cons f lof)
;     where f is a file, and lofd is a l-o-f


(define-struct dir (name contents))

; simple-file? : any -> boolean
; Predicate for simple (i.e., non-directory) files.
(defile (simple-file? any)
  (symbol? any))

; file? : any -> boolean
; Predicate for files.
(defile (file? any)
  (or (simple-file? any)
      (dir? any)))


;;;;;;;;;;;;;;;;;;;;;;;;
; Templates

;(define (file-fn a-file)
;  (cond
;   [(simple-file? a-file) ...]
;   [(dir? a-file)         ...(dir-name a-file)...
;                          ...(lof-fn (dir-contents a-file))...]))

;(define (lof-fn a-lof)
;  (cond
;    [(empty? a-lof) ...]
;    [(cons? a-lof)  ...(file-fn (first a-lofd))...
;                    ...(lof-fn (rest a-lofd))...]))


;;;;;;;;;;;;;;;;;;;;;;;;

; find? : symbol file -> boolean
; Returns whether the filename is anywhere in the
; tree of files represented by the file.
(define (find? filename a-file)
  (cond
   [(simple-file? a-file) (symbol=? filename a-file)]
   [(dir? a-file)         (or (symbol=? filename (dir-name a-file))
			      (find-lof? (dir-contents a-file)))]))

; find-lof? : symbol l-o-f -> boolean
; Returns whether the filename is anywhere in the
; trees of files represented by the list of files.
(define (find-lof? filename a-lof)
  (cond
   [(empty? a-lof) false]
   [(dir? a-lof)   (or (find? filename (first a-lof))
		       (find-lof? filename (rest a-lof)))]))

;;;;;;;;;;;;;;;;;;;;;;;;

; any-duplicate-names? : file -> boolean
; Returns whether any (sub)directory directly or indirectly contains
; another directory or file of the same name.  It does NOT check
; for duplicated names in separate branches of the tree.
(define (any-duplicate-names? a-file)
  (cond
   [(simple-file? a-file) false]
   [(dir? a-file)         (or (find? (dir-name a-dir) (dir-contents a-file))
			      (any-duplicate-names-lof? (dir-contents a-file)))]))

; any-duplicate-names-lof? : l-o-f -> boolean
; Returns whether any (sub)directory in the list directly or indirectly
; contains another directory or file of the same name.  It does NOT check
; for duplicated names in separate branches of the tree.
(define (any-duplicate-names-lof? a-lof)
  (cond
   [(empty? a-lof) false]
   [(cons? a-lof)  (or (any-duplicate-names? (first a-lof))
		       (any-duplicate-names-lof? (rest a-lofd)))]))

;;;;;;;;;;;;;;;;;;;;;;;;

; flatten-dir-once : symbol file -> (file or l-o-f)
; Returns a structure like the original file, except that any
; (sub)directory with that name is removed and its contents
; moved up one level.
(define (flatten-dir-once dirname a-file)
  (cond
   [(simple-file? a-file) a-file]
   [(dir? a-file)
    (cond
     [(symbol=? dirname (dir-name a-file))
      (flatten-dir-once-lof dirname (dir-contents a-file))]
     [else
      (make-dir (dir-name a-file)
		(flatten-dir-once-lof dirname (dir-contents a-dir)))])]))

; flatten-dir-once-lodf : symbol l-o-f -> l-o-f
; Returns a structure like the original list of files, except that any
; (sub)directory with that name is removed and its contents
; moved up one level.
(define (flatten-dir-once-lof dirname a-lof)
  (cond
   [(empty? a-lof) empty]
   [(cons? a-lof)  (flatten-helper
		    (flatten-dir-once dirname (first a-lodf))
		    (flatten-dir-once-lof dirname (rest a-lodf)))]))

; flatten-helper : (file or l-o-f) l-o-f -> l-o-f
; Combines (an item or list) and a list into a new list.
(define (flatten-helper a-file-or-lof a-lof)
  (cond [(file? a-file-or-lof) (cons a-file-or-lof a-lof)]
	[else                  (append a-file-or-lof a-lof)]))



