(defparameter *width* 120)

(defun date (start end)
 (format nil "~A/~A" start end))

(defun gpa (gpa bounds)
 (format nil "~A/~A" gpa bounds))

(defun education (&key school location start end degree majorgpa totalgpa)
 (let
  ((locationtext (format nil "~A: ~A - ~A" location start end))
   ;(majortext (format nil "~A Major GPA" majorgpa))
   ;(totaltext (format nil "~A Total GPA" totalgpa))
   )
 (format nil "Education~%~V,,,'-A~%~A~V@A~%~V@A~%"
                                           (* 2 (/ *width* 3))
                                           "-"
                                           school
                                           (- *width* (length school))
                                           locationtext
                                           (- *width* (- (length locationtext) (length degree)))
                                           degree
                                           ;(- *width* (- (length locationtext) (length majortext)))
                                           ;majortext
                                           ;(- *width* (- (length locationtext) (length totaltext)))
                                           ;totaltext
                                           )))

(defun skills (skill-list)
 (format nil "Relevant Skills~%~V,,,'-A~%~{~{~A~V@A~}~%~}"
                                           (* 2 (/ *width* 3))
                                           "-"
                                           (mapit
                                            (let
                                             ((left-text (handle-desc-element (car it) :left 0)))
                                             (list
                                              left-text
                                              (- *width* (length left-text))
                                              (if (stringp (cadr it))
                                                  (cadr it)
                                                  (format nil "~V@A~{~{~%~V@A~}~}"
                                                   (- *width* (length left-text))
                                                   (caadr it)
                                                   (mapit (list *width* it) (cdadr it))))))
                                            skill-list)))

(defun handle-desc-list (list left)
 (format nil "~{~{~,,V@A~A~}~^~%~}" (mapit (list left "  * " (handle-desc-element it :left (+ 4 left) :ignore-left t)) list)))

(defun handle-paragraph-element (p-elem)
 (cond
  ((stringp p-elem) p-elem)
  ((and (listp p-elem) (eql (car p-elem) 'bold)) (format nil "_~A_" (cadr p-elem)))
  ((and (listp p-elem) (eql (car p-elem) 'invisible)) (format nil "~VA" (length (cadr p-elem)) " "))))

(defun handle-paragraph (paragraph left &key ignore-left)
 (breakup-string (format nil "~{~A~}" (mapcar #'handle-paragraph-element paragraph)) left :ignore-left ignore-left))

(defun handle-desc-element (desc-element &key (left 4) ignore-left)
 (cond
  ((stringp desc-element) (breakup-string desc-element left :ignore-left ignore-left))
  ((and (listp desc-element) (eql 'list (car desc-element)))
   (handle-desc-list (cdr desc-element) left))
  ((listp desc-element) (handle-paragraph desc-element left :ignore-left ignore-left))
  ))

(defun breakup-string (string left &key ignore-left)
 (if (< (length string) (- *width* left))
     (format nil "~,,V@A" (if ignore-left 0 left) string)
     (let
      ((break-space-pos (position #\Space string :from-end t :end (- *width* left))))
      (format nil "~,,V@A~%~A" (if ignore-left 0 left)
                               (subseq string 0 break-space-pos)
                               (breakup-string (subseq string (1+ break-space-pos)) left)))))

(defun print-description (description)
 (format nil "~{~A~%~}" (mapcar #'handle-desc-element description))
)

(defun pos (&key position start end description one-line)
 (format nil "  ~A (~A - ~A)~%~A" position
                                  start
                                  (or end "Present")
                                  (print-description description)))

(defun all-work (work)
 (format nil "Work Experience~%~V,,,'-A~%~{~A~%~}"
                                           (* 2 (/ *width* 3))
                                           "-"
                                           work))

(defun work (&key company positions)
 (format nil "~A~%~{~A~^~%~}" company positions))

(defun all-oss (oss-projs)
 (format nil "Open Source Projects~%~V,,,'-A~%~{~A~}"
                                           (* 2 (/ *width* 3))
                                           "-"
                                           oss-projs))

(defun oss-proj (&key project project-desc project-website role start end)
 (format nil "~A (~A)~%  ~A (~A - ~A)~%~A" project project-website role start (or end "Present") (print-description project-desc)))

(defun resume (&key name email phone addr1 addr2 education skills work oss)
 (format nil "~:@(~V@A~)~%~A~V@A~%~A~V@A~%~%~A~%~A~%~A~A" (+ (/ *width* 2) (/ (length name) 2))
                                      name
                                      email
                                      (- *width* (length email))
                                      addr1
                                      phone
                                      (- *width* (length phone))
                                      addr2
                                      education
                                      skills
                                      (all-work work)
                                      (all-oss oss)))

(defun dump-to-text (resume-file out-file)
 (with-open-file (out out-file :direction :output :if-exists :supersede)
  (with-open-file (in resume-file)
   (format out "~A" (eval (read in))))))