;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;; treemake.el ;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;converts an ACEDB model into tree stick figure

;;;this is not swell code; sorry.

;;;assumptions: all comments removed from model first.
;;;view narrowed (C-x n n) to model before proceeding.

;;;*** this macro does not save the original model
;;;*** it replaces it with the stick figure

;;here we untabify
(defun remove-tabs ()
  (interactive)
  (goto-char (point-min))
  (untabify (point-min) (point-max)))

;;here we connect XREF to its neighbors
(defun connect-xref ()
  (interactive)
  (goto-char (point-min))
  (while (re-search-forward " XREF " nil t)
    (replace-match "_XREF_" nil nil)))

;;here we delete all REPEAT and UNIQUE
(defun kill-repeat-unique ()
  (interactive)
  (goto-char (point-min))
  (while (re-search-forward "REPEAT\\|UNIQUE" nil t)
    (replace-match "" nil nil)))

;;here we replace fields with a pound sign symbol:
;;we look for Text, Int, Float, #Class, ?Class
;;then delete it and replace with "#"

;;each token needs to be expressed as
;;space;token;space-or-newline
;;otherwise we catch things like "Int" in "Intragenic"


(defun replace-fields ()
  (interactive)
  (goto-char (point-min))

  (while (re-search-forward
  " Text\\( \\|\n\\)\\| Int\\( \\|\n\\)\\| Float\\( \\|\n\\)\\| #\\| \\?"
  nil t)

    (backward-char 1)
    (re-search-backward " " nil t)
    (forward-char 1)
    (let ((beginning (point)))
      (re-search-forward " \\|\n" nil t)
      (backward-char 1)
      (delete-region beginning (point))
      (delete-horizontal-space)
      (insert-string " # ")
      (backward-char 1))))

;;here we replace all alphabetic (a thru z) characters
;;with an underscore "_" character
;;we convert dashes, too

(defun replace-alphabetics ()
  (interactive)
  (goto-char (point-min))
  (while (re-search-forward "[A-Za-z-]" nil t)
    (replace-match "_" nil nil)))

;;here we start to build the vertical connections
;;between sections of the model; i.e., this is the
;;first pass. note execution starts from the BOTTOM
;;of the model.

;;this starts to connect branches to their roots; i.e.
;;it begins to put vertical bars in the right places.
;;a vertical bar belongs wherever you find this pattern:
;;newline;at least one space;underscore
;;replace the underscore with "|"

(defun initialize-bars ()
  (interactive)
  (goto-char (point-min))
  (while (re-search-forward "\n +_" nil t)
    (backward-delete-char 1)
    (insert-string "|")))

;;this finishes the branch connection. It must
;;be executed starting from the BOTTOM of the
;;model. Here is the logic:

;;it finds a vertical bar at some position xxx
;;it then moves vertically to the character above
;;it lays down a mark, moves one char right, and kills region
;;it then runs a little test
;;  if it killed a space, then replace the space with a vertical bar
;;  if it killed something else, then just put it back
;;then it goes back to xxx
;;finally backspace one character to get ready for next cycle

(defun finish-bars ()
  (interactive)
  (goto-char (point-max))
  (while (re-search-backward "|" nil t)
    (previous-line 1)
    (let ((beginning (point)))
      (forward-char 1)
      (kill-region beginning (point))
      (if (equal (first kill-ring) " ")
	  (insert-string "|")
	  (insert-string (first kill-ring)))
      (backward-char 1)
      (next-line 1)
      (backward-char 1))))


;;finally, connect horizontal bars using dots

(defun connect-with-dots ()
  (interactive)
  (goto-char (point-min))
  (while (re-search-forward "_ +_" nil t)
    (backward-char 1)
    (let ((beginning (point)))
      (re-search-backward "_")
      (forward-char 1)
      (narrow-to-region beginning (point))
      (goto-char (point-min))
      (while (re-search-forward " " nil t)
	(replace-match "\." nil nil))
      (widen))))

;;treemake--top level

(defun treemake ()
  (interactive)
  (remove-tabs)
  (connect-xref)
  (kill-repeat-unique)
  (replace-fields)
  (replace-alphabetics)
  (initialize-bars)
  (finish-bars)
  (connect-with-dots))