(defparameter *root-dir* (make-pathname :directory '(:relative)))

(defun make-relative (path)
  (let ((dir (pathname-directory path)))
    (make-pathname :directory (append '(:relative) (nthcdr (position "src" dir :test #'string=) dir))
                   :defaults path)))

(defun get-source-paths (path)
  (directory (merge-pathnames path *root-dir*)))

(defun default-files ()
  (get-source-paths
   (make-pathname :directory '(:relative "src")
                  :name :wild
                  :type "cpp")))

(defun software-renderer ()
  (get-source-paths
   (make-pathname :directory '(:relative "src" "Backends" "Rendering")
                  :name "Software"
                  :type "cpp")))

(defun x11-platform ()
  (get-source-paths
   (make-pathname :directory '(:relative "src" "Backends" "Platform")
                  :name "X11"
                  :type "cpp")))

(defun x11-window ()
  (get-source-paths
   (make-pathname :directory '(:relative "src" "Backends" "Rendering" "Window" "Software")
                  :name "X11"
                  :type "cpp")))

(defun solaris-audio ()
  (get-source-paths
   (make-pathname :directory '(:relative "src" "Backends" "Audio" "SoftwareMixer")
                  :name "Solaris"
                  :type "cpp")))

(defun null-audio ()
  (get-source-paths
   (make-pathname :directory '(:relative "src" "Backends" "Audio")
                  :name "Null"
                  :type "cpp")))

(defun software-mixer ()
  (append
   (get-source-paths
    (make-pathname :directory '(:relative "src" "Backends" "Audio")
                   :name "SoftwareMixer"
                   :type "cpp"))
   (get-source-paths
    (make-pathname :directory '(:relative "src" "Backends" "Audio" "SoftwareMixer")
                   :name "Mixer"
                   :type "cpp"))))

(defun null-controller ()
  (get-source-paths
   (make-pathname :directory '(:relative "src" "Backends" "Controller")
                  :name "Null"
                  :type "cpp")))

(defun get-sources ()
  (append
   (software-renderer)
   (software-mixer)
   (x11-platform)
   (null-controller)
   (x11-window)
   (solaris-audio)
   (default-files)))

(defun get-build-path (path)
  (let ((dir (pathname-directory path)))
    (make-pathname :directory (append '(:relative "build")
                                      (nthcdr (position "src" dir :test #'string=) dir))
                   :type "o"
                   :defaults path)))

(defun make-objects-list (sources)
  (format nil "objects =~{ ~A~}" (mapcar 'get-build-path sources)))

(defun make-compile-command ()
  (let ((str
         (format nil "g++ $(optlevel) -c $< -o $@")))
    (setf str (format nil "~A -I./fakes/" str))
    (format nil "%.o: %.cpp~%~C~A~%~%"
            #\Tab
            str)))

(defun make-boilerplate ()
  (format nil "all: CSE2~%optlevel = -O2~%"))

(defun make-link-command ()
  (format nil "CSE2: $(objects)~%~Cg++ $(optlevel) -o $@ $^ -lX11 -lXext -lm~%"
          #\Tab))

(defun make-build-script (output-path)
  (with-open-file (stream output-path :direction :output :if-exists :supersede :if-does-not-exist :create)
    (setf *root-dir* (make-pathname :directory '(:relative)))
    (let ((sources (get-sources)))
      (write-string (make-objects-list sources) stream)
      (terpri stream)
      (terpri stream)
      (write-string (make-boilerplate) stream)
      (terpri stream)
      (write-string (make-compile-command) stream)
      (write-string (make-link-command) stream))))