#lang racket
(require csc151)

;;; File:
;;;   association-lists-lab.rkt
;;; Authors:
;;;   Titus Klinge
;;;   Samuel A. Rebelsky
;;;   YOUR NAME HERE
;;; Summary:
;;;   Data and procedures for the lab on association lists.

;;; Values:
;;;   grinnell-directory
;;;   grinnell-directory-annotated
;;; Type:
;;;   List of lists.
;;; Note:
;;;   * Each sublist is of length at least four and contains a last
;;;     name, a first name, a username, a phone number, and an
;;;     optional sequence of annotations.
;;;   * The
;;; Contents:
;;;   A list of people at Grinnell with contact information and some
;;;   useful attributes.
(define grinnell-directory
  (list
   (list "Rebelsky"        "Samuel"  "messy-office" "4410")
   (list "Klinge"          "Titus"   "cauldron"     "3271")
   (list "Weinman"         "Jerod"   "map-reader"   "9812")
   (list "Osera"           "PM"      "type-snob"    "4010")
   (list "Curtsinger"      "Charlie" "systems-guy"  "3127")
   (list "Vostinar"        "Anya"    "cache"        "4306")
   (list "Dahlby-Albright" "Sarah"   "cheery-coach" "4362")
   (list "Rodrigues"       "Liz"     "vivero"       "3362")
   (list "Barks"           "Sarah"   "stem-careers" "4940")
   (list "Kington"         "Raynard" "the-prez"     "3000")))

(define grinnell-directory-annotated
  (list
   (list "Rebelsky"        "Samuel"  "messy-office" "4410"
         "CSC" "151prof" "DigHum" "Professor")
   (list "Klinge"          "Titus"   "cauldron"     "3271"
         "CSC" "Assistant" "151prof")
   (list "Weinman"         "Jerod"   "map-reader"   "9812"
         "CSC" "Chair" "Associate" "NSF Grant")
   (list "Osera"           "PM"      "type-snob"    "4010"
         "Assistant" "NSF Grant" "Tutorial" "CSC")
   (list "Curtsinger"      "Charlie" "systems-guy"  "3127"
         "CSC" "Assistant" "Tutorial" "ASFAC")
   (list "Vostinar"        "Anya"    "cache"        "4306"
         "CSC" "Assistant" "Interdisciplinary")
   (list "Dahlby-Albright" "Sarah"   "cheery-coach" "4362"
         "Statistics" "CSC" "Outreach")
   (list "Rodrigues"       "Liz"     "vivero"       "3362"
         "LIB" "DigHum" "Assistant" )
   (list "Barks"           "Sarah"   "stem-careers" "4940"
         "CLS" "STEM" "Neuroscience")
   (list "Kington"         "Raynard" "the-prez"     "3000"
         "Bigwig")))

;;; Procedure:
;;;   lookup-email-by-last-name
;;; Parameters:
;;;   last-name, a string
;;;   directory, a list of directory entries
;;; Purpose:
;;;   Look up an email address in the table.
;;; Produces:
;;;   email-address, a string
;;; Preconditions:
;;;   * Each entry in directory must be a list.
;;;   * Element 0 of each entry must be a string which represents the
;;;     last name.
;;;   * Element 2 of each entry must be a string which represents the
;;;     user name.
;;;   * Email addresses have the form "username@grinnell.edu".
;;; Postconditions:
;;;   * If an entry for the last name appears once in the table,
;;;     email-address represents their email address.
;;;   * If multiple entries with a matching last name appear in the table,
;;;     email-address represents the email address for one of them.
;;;   * If no entries with a matching last name appear in the table,
;;;     email-address is the empty string, ""
;;;   * Does not affect the table.
(define lookup-email-by-last-name
  (lambda (last-name directory)
    (let ([assoc-result (assoc last-name directory)])
      (if assoc-result
          (string-append (list-ref assoc-result 2)
                         "@grinnell.edu")
          ""))))

;;; Procedure:
;;;   lookup-person-by-phone
;;; Parameters:
;;;   phone, a string
;;;   directory, a list of directory entries
;;; Purpose:
;;;   Find the name of the person who has a particular phone number.
;;; Produces:
;;;   name, a string (or the value #f)
;;; Preconditions:
;;;   * phone is a string with four digits.
;;;   * Each entry in directory must be a list.
;;;   * Element 0 of each entry must be a string which represents the
;;;     last name.
;;;   * Element 1 of each entry must be a string which represents the
;;;     first name.
;;;   * Element 3 of each entry must be a string which represents the
;;;     four-digit phone number.
;;;   * People have both first names and last names.
;;; Postconditions:
;;;   * If an entry for the phone number appears once in the table,
;;;     name represents their name.
;;;   * If multiple entries with a matching phone number appear in the 
;;;     table, name represents the name for one of them.
;;;   * If no entries with a matching phone number appear in the table,
;;;     name is #f.
;;;   * Does not affect the table.
(define lookup-person-by-phone
  (lambda (phone directory)
    (cond
      [(null? directory)
       #f]
      [(equal? phone (list-ref (car directory) 3))
       (string-append (cadar directory) " " (caar directory))]
      [else
       (lookup-person-by-phone phone (cdr directory))])))

(define assoc-ci
  (lambda (key alist)
    (cond
      ; If there are no entries left in the association list,
      ; there are no entries with the given key.
      [(null? alist)
       #f]
      ; If the key we're looking for is the key of the first
      ; entry, then use that entry.
      [(string-ci=? key (car (car alist)))
       (car alist)]
      ; Otherwise, look in the rest of the association list.
      [else
       (assoc-ci key (cdr alist))])))

(define assoc-new
  (let ([match? (lambda (val1 val2)
                  (or (and (string? val1)
                           (string? val2)
                           (string-ci=? val1 val2))
                      (equal? val1 val2)))])
    (lambda (key alist)
      (cond
        ; If there are no entries left in the association list,
        ; there are no entries with the given key.
        [(null? alist)
         #f]
        ; If the key we're looking for matches the key of the first
        ; entry, then use that entry.
        [(match? key (car (car alist)))
         (car alist)]
        ; Otherwise, look in the rest of the association list.
        [else
         (assoc-new key (cdr alist))]))))

;;;   lookup-name-by
;;; Parameters:
;;;   key, a string
;;;   position, an integer between 0 and 3, inclusive
;;;   directory, a list of directory entries
;;; Purpose:
;;;   Find the full name of a person who has a matching string at
;;;   the designated position (0 for last name, 1 for first name,
;;;   2 for username, and 3 for four-digit phone number).
;;; Produces:
;;;   name, a string (or the value #f)
;;; Preconditions:
;;;   * phone is a string with four digits.
;;;   * Each entry in directory must be a list.
;;;   * Element 0 of each entry must be a string which represents the
;;;     last name.
;;;   * Element 1 of each entry must be a string which represents the
;;;     first name.
;;;   * Element 2 of each entry must be a string which represents the
;;;     user name.
;;;   * Element 3 of each entry must be a string which represents the
;;;     four-digit phone number.
;;;   * People have both first names and last names.
;;; Postconditions:
;;;   If position is 0 and an entry with the same last name appears
;;;     somewhere in the table, name is the name associated with
;;;     one such entry.
;;;   If position is 1 and an entry with the same first name appears
;;;     somewhere in the table, name is the name associated with
;;;     one such entry.
;;;   If position is 2 and an entry with the same user name appears
;;;     somewhere in the table, name is the name associated with
;;;     one such entry.
;;;   If position is 3 and an entry with the same four-digit phone
;;;     appears somewhere in the table, name is the name associated
;;;     with one such entry.
;;;   If no matching entries appear, cname is #f.
;;;   Does not affect the table.
(define lookup-name-by
  (lambda (key position directory)
    (cond
      [(null? directory)
       #f]
      [(string-ci=? key (list-ref (car directory) position))
       (string-append (cadar directory) " " (caar directory))]
      [else
       (lookup-name-by key position (cdr directory))])))

; Look up a name by username
(define lookup-name-by-username
  (lambda (uname directory)
    (lookup-name-by uname 2 directory)))

; Look up a name by first name
(define lookup-name-by-first-name
  (section lookup-name-by <> 1 <>))

