;; Copyright (C) 2015-2016 Free Software Foundation, Inc

;; Author: Rocky Bernstein <rocky@gnu.org>

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <http://www.gnu.org/licenses/>.

(declare-function realgud:terminate &optional cmdbuf)

(defconst realgud-track-char-range 10000
  "Max number of characters from end of buffer to search for stack entry.")

;; Shell process buffers that we can hook into:
(require 'esh-mode)
(require 'ansi-color)
(require 'comint)

(require 'load-relative)
(require-relative-list
 '("core"           "file"     "fringe"
   "helper"         "init"     "loc"    "lochist"
   "regexp"         "shortkey" "window"
   "bp"
   ) "realgud-")

(require-relative-list
 '("buffer/command" "buffer/helper" "buffer/source") "realgud-buffer-")

(defcustom realgud-short-key-on-tracing? nil
"If non-nil, set short-key mode for any source buffer that is traced into"
  :type 'symbolp
  :group 'realgud)

(declare-function fn-p-to-fn?-alias                   'realgud-helper)
(declare-function realgud-bp-add-info                 'realgud-bp)
(declare-function realgud-bp-del-info                 'realgud-bp)
(declare-function realgud-bp-enable-disable-info      'realgud-bp)
(declare-function realgud-cmdbuf-add-srcbuf           'realgud-buffer-command)
(declare-function realgud-cmdbuf-debugger-name        'realgud-buffer-command)
(declare-function realgud-cmdbuf-info-bp-list=        'realgud-buffer-command)
(declare-function realgud-cmdbuf-info-divert-output?= 'realgud-buffer-command)
(declare-function realgud-cmdbuf-info-in-debugger?    'realgud-buffer-command)
(declare-function realgud-cmdbuf-info-in-debugger?=   'realgud-buffer-command)
(declare-function realgud-cmdbuf-info-last-input-end= 'realgud-buffer-command)
(declare-function realgud-cmdbuf-init                 'realgud-buffer-command)
(declare-function realgud-cmdbuf-loc-hist             'realgud-buffer-command)
(declare-function realgud-cmdbuf-mode-line-update     'realgud-buffer-command)
(declare-function realgud-cmdbuf-mode-line-update     'realgud-buffer-command)
(declare-function realgud-cmdbuf-pat                  'realgud-buffer-command)
(declare-function realgud-cmdbuf?                     'realgud-buffer-command)
(declare-function realgud-cmdbuf-info-in-srcbuf?=     'realgud-buffer-command)
(declare-function realgud:debugger-name-transform     'realgud-helper)
(declare-function realgud:terminate                   'realgud-core)
(declare-function realgud:file-loc-from-line          'realgud-file)
(declare-function realgud-fringe-history-set          'realgud-fringe)
(declare-function realgud-get-cmdbuf                  'realgud-buffer-command)
(declare-function realgud-get-srcbuf-from-cmdbuf      'realgud-buffer-helper)
(declare-function realgud-loc-goto                    'realgud-loc)
(declare-function realgud-loc-hist-add                'realgud-lochist)
(declare-function realgud-loc-hist-index              'realgud-lochist)
(declare-function realgud-loc-hist-item               'realgud-lochist)
(declare-function realgud-loc?                        'realgud-loc)
(declare-function realgud-short-key-mode-setup        'realgud-shortkey)
(declare-function realgud-srcbuf-init-or-update       'realgud-source)
(declare-function realgud-srcbuf-loc-hist             'realgud-source)
(declare-function realgud-window-src                  'realgud-window)
(declare-function realgud-window-src-undisturb-cmd    'realgud-window)
(declare-function realgud-window-update-position      'realgud-window)

(make-variable-buffer-local  (defvar realgud-track-mode))
(fn-p-to-fn?-alias 'realgud-loc-p)

(defvar realgud-track-divert-string)

(defun realgud-track-comint-output-filter-hook(text)
  "An output-filter hook custom for comint shells.  Find
location/s, if any, and run the action(s) associated with
finding a new location/s.  The parameter TEXT appears because it
is part of the comint-output-filter-functions API. Instead we use
marks set in buffer-local variables to extract text"

  ;; Instead of trying to piece things together from partial text
  ;; (which can be almost useless depending on Emacs version), we
  ;; monitor to the point where we have the next dbgr prompt, and then
  ;; check all text from comint-last-input-end to process-mark.

  ;; FIXME: Add unwind-protect?
  (if (and realgud-track-mode (realgud-cmdbuf? (current-buffer)))
      (let* ((cmd-buff (current-buffer))
	     (cmd-mark (point-marker))
	     (shortkey
	      (realgud-cmdbuf-info-src-shortkey?
	       realgud-cmdbuf-info))
	     (curr-proc (get-buffer-process cmd-buff))
	     (cmdbuf-last-output-end
	      (realgud-cmdbuf-info-last-input-end realgud-cmdbuf-info))
	     (last-output-end
	      (if curr-proc
		  (process-mark curr-proc)
		cmdbuf-last-output-end))
	     (last-output-start (max comint-last-input-start
				     (- last-output-end realgud-track-char-range))))
	;; Sometimes we get called twice and the second time nothing
	;; changes. Guard against this.
	(unless (= last-output-start last-output-end)
	  (unless (= last-output-end cmdbuf-last-output-end)
	    (setq last-output-start (max last-output-start
					 cmdbuf-last-output-end))
	    )
	  ;; Done with using old command buffer's last-input-end.
	  ;; Update that for next time.
	  (realgud-cmdbuf-info-last-input-end= last-output-start)
	  (realgud:track-from-region last-output-start
				     last-output-end cmd-mark cmd-buff
				     shortkey 't))
	)
    )
  )

(defun realgud-track-eshell-output-filter-hook()
  "An output-filter hook custom for eshell shells.  Find
location(s), if any, and run the action(s) associated with We use
marks set in buffer-local variables to extract text"

  ;; FIXME: Add unwind-protect?
  (if realgud-track-mode
      (lexical-let* ((cmd-buff (current-buffer))
		     (cmd-mark (point-marker))
		     (shortkey
		      (realgud-cmdbuf-info-src-shortkey?
		       realgud-cmdbuf-info))
		     (loc (realgud:track-from-region
			   eshell-last-output-start
			   eshell-last-output-end cmd-mark cmd-buff
			   shortkey)))
	(realgud-track-loc-action loc cmd-buff 't shortkey))
    ))

(defun realgud-track-term-output-filter-hook(text)
  "An output-filter hook custom for ansi-term shells.  Find
location/s, if any, and run the action(s) associated with
finding a new location/s.  The parameter TEXT appears because it
is part of the comint-output-filter-functions API. Instead we use
marks set in buffer-local variables to extract text"
  (if (and realgud-track-mode (realgud-cmdbuf? (current-buffer)))
      (realgud-track-loc text (point-marker))
    ))

(defun realgud:track-complain-if-not-in-cmd-buffer (&optional buf errorp)
  "Complain if BUF (default: current buffer) is not a command buffer.
With non-nil ERRORP, raise an exception.  Otherwise, print a
message."
  (setq buf (or buf (current-buffer)))
  (unless (realgud-cmdbuf? buf)
    (funcall (if errorp #'error #'message)
             "Buffer %s is not a debugger command buffer" buf)
    t))

(defun realgud:track-from-region(from to &optional cmd-mark opt-cmdbuf
				      shortkey-on-tracing? no-warn-if-no-match?)
  "Find and position a buffer at the location found in the marked region.

You might want to use this function interactively after marking a
region in a debugger-tracked shell buffer (see `realgud-track-mode')
or a more dedicated debugger command buffer.

The marked region location should match the regexp found in the
buffer-local variable `realgud-cmdbuf-info' structure under the
field loc-regexp. You can see what this is by
evaluating (realgud-cmdbuf-info-loc-regexp realgud-cmdbuf-info)"

  (interactive "r")
  (if (> from to) (psetq to from from to))
  (let* ((text (buffer-substring-no-properties from to))
	 (loc (realgud-track-loc text cmd-mark))
	 ;; If we see a selected frame number, it is stored
	 ;; in frame-num. Otherwise, nil.
	 (frame-num)
	 (text-sans-loc)
	 (bp-loc)
	 (cmdbuf (or opt-cmdbuf (current-buffer)))
	 )
    (unless (realgud:track-complain-if-not-in-cmd-buffer cmdbuf t)
	(if (not (equal "" text))
	    (with-current-buffer cmdbuf
	      (if (realgud-sget 'cmdbuf-info 'divert-output?)
		  (realgud-track-divert-prompt text cmdbuf to))
	      ;; FIXME: instead of these fixed filters,
	      ;; put into a list and iterate over that.
	      (realgud-track-termination? text)
	      (setq text-sans-loc (or (realgud-track-loc-remaining text) text))
	      (realgud-track-bp-enable-disable text-sans-loc
					       (realgud-cmdbuf-pat "brkpt-enable")
					       't)
	      (realgud-track-bp-enable-disable text-sans-loc
					       (realgud-cmdbuf-pat "brkpt-disable")
					       nil)
	      (setq frame-num (realgud-track-selected-frame text))
	      (setq bp-loc (realgud-track-bp-loc text-sans-loc cmd-mark cmdbuf))
	      (if bp-loc
		  (let ((src-buffer (realgud-loc-goto bp-loc)))
		    (realgud-cmdbuf-add-srcbuf src-buffer cmdbuf)
		    (with-current-buffer src-buffer
		      (realgud-bp-add-info bp-loc)
		      )))
	      (if loc
		  (let ((selected-frame
			 (or (not frame-num)
			     (eq frame-num (realgud-cmdbuf-pat "top-frame-num")))))
		    (realgud-track-loc-action loc cmdbuf (not selected-frame)
                                              shortkey-on-tracing?)
		    (realgud-cmdbuf-info-in-debugger?= 't)
                    (realgud-cmdbuf-mode-line-update))
                (dolist (bp-loc (realgud-track-bp-delete text-sans-loc cmd-mark cmdbuf))
                  (let ((src-buffer (realgud-loc-goto bp-loc)))
                    (realgud-cmdbuf-add-srcbuf src-buffer cmdbuf)
                    (with-current-buffer src-buffer
                      (realgud-bp-del-info bp-loc)
                      ))))
              )
          )
        )
    )
  )

(defun realgud-track-hist-fn-internal(fn)
  "Update both command buffer and a source buffer to reflect the
selected location in the location history. If we started in a
command buffer, we stay in a command buffer. Moving inside a
command buffer always shows the corresponding source
file. However it is possible in shortkey mode to show only the
source code window, even the commmand buffer is updated albeit
unshown."

  (let ((cmdbuf (realgud-get-cmdbuf (current-buffer))))
    (if cmdbuf
	(let* ((loc-hist (realgud-cmdbuf-loc-hist cmdbuf))
	       (window (selected-window))
	       (position (funcall fn loc-hist))
	       (stay-in-cmdbuf?
		(or (eq (current-buffer) cmdbuf)
		    (with-current-buffer cmdbuf
		      (not (realgud-sget 'cmdbuf-info 'in-srcbuf?)))))
	       (loc (realgud-loc-hist-item loc-hist))
	       (srcbuf (realgud-get-srcbuf-from-cmdbuf cmdbuf loc))
	       )
	  (set-buffer (realgud-loc-goto loc))

	  ;; Make sure command buffer is updated
	  (realgud-window-update-position cmdbuf
				       (realgud-loc-cmd-marker loc))

	  ;; FIXME turn into fn. combine with realgud-track-loc-action.
	  (if stay-in-cmdbuf?
	      (let ((cmd-window (realgud-window-src-undisturb-cmd srcbuf)))
		(if cmd-window (select-window cmd-window)))
	    (realgud-window-src srcbuf)
	  )

	  ;; Make sure source buffer is updated
	  (realgud-window-update-position srcbuf
				       (realgud-loc-marker loc))

	  (message "history position %s line %s"
		   (realgud-loc-hist-index loc-hist)
		   (realgud-loc-line-number loc))
	  (select-window window)))
  ))

;; FIXME: Can we dry code more via a macro?
(defun realgud-track-hist-newer()
  (interactive)
  (realgud-track-hist-fn-internal 'realgud-loc-hist-newer))

(defun realgud-track-hist-newest()
  (interactive)
  (realgud-track-hist-fn-internal 'realgud-loc-hist-newest))

(defun realgud-track-hist-older()
  (interactive)
  (realgud-track-hist-fn-internal 'realgud-loc-hist-older))

(defun realgud-track-hist-oldest()
  (interactive)
  (realgud-track-hist-fn-internal 'realgud-loc-hist-oldest))

(defun realgud-track-loc-action (loc cmdbuf &optional not-selected-frame
				  shortkey-on-tracing?)
  "If loc is valid, show loc and do whatever actions we do for
encountering a new loc."
  (if (realgud-loc? loc)
      (let*
	  ((cmdbuf-loc-hist (realgud-cmdbuf-loc-hist cmdbuf))
	   (cmdbuf-local-overlay-arrow?
	    (with-current-buffer cmdbuf
	      (local-variable-p 'overlay-arrow-variable-list)))
	   (stay-in-cmdbuf?
	    (with-current-buffer cmdbuf
	      (not (realgud-sget 'cmdbuf-info 'in-srcbuf?))))
	   (shortkey-mode?
	    (with-current-buffer cmdbuf
	      (realgud-sget 'cmdbuf-info 'src-shortkey?)))
	   (srcbuf)
	   (srcbuf-loc-hist)
	   )

	(setq srcbuf (realgud-loc-goto loc))
	(realgud-srcbuf-init-or-update srcbuf cmdbuf)
	(setq srcbuf-loc-hist (realgud-srcbuf-loc-hist srcbuf))
	(realgud-cmdbuf-add-srcbuf srcbuf cmdbuf)

	(with-current-buffer srcbuf
	  (realgud-short-key-mode-setup
	   (and shortkey-on-tracing?
		(or realgud-short-key-on-tracing? shortkey-mode?))
	   ))

        ;; Do we need to go back to the process/command buffer because other
        ;; output-filter hooks run after this may assume they are in that
        ;; buffer? If so, we may have to use set-buffer rather than
	;; switch-to-buffer in some cases.
	(set-buffer cmdbuf)

	(unless (realgud-sget 'cmdbuf-info 'no-record?)
	  (realgud-loc-hist-add srcbuf-loc-hist loc)
	  (realgud-loc-hist-add cmdbuf-loc-hist loc)
	  (realgud-fringe-history-set cmdbuf-loc-hist cmdbuf-local-overlay-arrow?)
	  )

	;; FIXME turn into fn. combine with realgud-track-hist-fn-internal
	(if stay-in-cmdbuf?
	    (let ((cmd-window (realgud-window-src-undisturb-cmd srcbuf)))
	      (with-current-buffer srcbuf
		(if (and (boundp 'realgud-overlay-arrow1)
			 (markerp realgud-overlay-arrow1))
		    (progn
		      ;; Doesn't work
		      ;; (if not-selected-frame
		      ;; 	  (set-fringe-bitmap-face 'hollow-right-triangle
		      ;; 				  'realgud-overlay-arrow1)
		      ;; 			; else
		      ;; 	(set-fringe-bitmap-face 'realgud-right-triangle1
		      ;; 				'realgud-overlay-arrow1)
		      ;; 	)
		      (realgud-window-update-position srcbuf realgud-overlay-arrow1)))
		)
	      (if cmd-window (select-window cmd-window)))
	  ; else
	  (with-current-buffer srcbuf
	    (realgud-window-src srcbuf)
	    (realgud-window-update-position srcbuf realgud-overlay-arrow1))
	  ;; reset 'in-srcbuf' to allow the command buffer to keep point focus
	  ;; when used directly. 'in-srcbuf' is set 't' early in the stack
	  ;; (prior to common command code, e.g. this) when any command is run
	  ;; from a source buffer
	  (with-current-buffer cmdbuf
	    (realgud-cmdbuf-info-in-srcbuf?= nil))
	  )
	))
  )

(defun realgud-track-loc(text cmd-mark &optional opt-regexp opt-file-group
			   opt-line-group no-warn-on-no-match?
			   opt-ignore-file-re)
  "Do regular-expression matching to find a file name and line number inside
string TEXT. If we match, we will turn the result into a realgud-loc struct.
Otherwise return nil."

  ;; NOTE: realgud-cmdbuf-info is a buffer variable local to the process running
  ;; the debugger. It contains a realgud-cmdbuf-info "struct". In that struct are
  ;; the fields loc-regexp, file-group, line-group, alt-file-group, and alt-line-group.
  ;;
  ;; By setting the the fields of realgud-cmdbuf-info appropriately, we
  ;; can accomodate a family of debuggers -- one at a time -- for the
  ;; buffer process.

  (unless (realgud:track-complain-if-not-in-cmd-buffer)
      (let
	  ((loc-regexp (or opt-regexp
			   (realgud-sget 'cmdbuf-info 'loc-regexp)))
	   (file-group (or opt-file-group
			   (realgud-sget 'cmdbuf-info 'file-group)))
	   (line-group (or opt-line-group
			   (realgud-sget 'cmdbuf-info 'line-group)))
	   (alt-file-group (realgud-sget 'cmdbuf-info 'alt-file-group))
	   (alt-line-group (realgud-sget 'cmdbuf-info 'alt-line-group))
	   (text-group (realgud-sget 'cmdbuf-info 'text-group))
	   (ignore-file-re (or opt-ignore-file-re
			       (realgud-sget 'cmdbuf-info 'ignore-file-re)))
	   (callback-loc-fn (realgud-sget 'cmdbuf-info 'callback-loc-fn))
	   )
	(if loc-regexp
	    (if (string-match loc-regexp text)
		(let* ((filename (or (match-string file-group text)
				     (match-string alt-file-group text)))
		       (line-str (or (match-string line-group text)
				     (match-string alt-line-group text)))
		       (source-str (and text-group
					(match-string text-group text)))
		       (lineno (string-to-number (or line-str "1"))))
		  (when source-str
		    (setq source-str (ansi-color-filter-apply
				      source-str)))
		  (cond (callback-loc-fn
			 (funcall callback-loc-fn text
				  filename lineno source-str
				  ignore-file-re cmd-mark))
			('t
			 (unless line-str
			   (message "line number not found -- using 1"))
			 (if (and filename lineno)
			     (realgud:file-loc-from-line filename lineno
							 cmd-mark
							 source-str nil
							 ignore-file-re)
			   ;; else
			   nil)))))
	  ;; else
	  (and (message
		(concat "Buffer variable for regular expression pattern not"
                        " given and not passed as a parameter"))
               nil)))
    )
  )

(defun realgud-track-bp-loc(text &optional cmd-mark cmdbuf ignore-file-re)
  "Do regular-expression matching to find a file name and line number inside
string TEXT. If we match, we will turn the result into a realgud-loc struct.
Otherwise return nil. CMD-MARK is set in the realgud-loc object created.
"

  ; NOTE: realgud-cmdbuf-info is a buffer variable local to the process
  ; running the debugger. It contains a realgud-cmdbuf-info "struct". In
  ; that struct is the regexp hash to match positions. By setting the
  ; the fields of realgud-cmdbuf-info appropriately we can accomodate a
  ; family of debuggers -- one at a time -- for the buffer process.

  (setq cmdbuf (or cmdbuf (current-buffer)))
  (with-current-buffer cmdbuf
    (unless (realgud:track-complain-if-not-in-cmd-buffer cmdbuf t)
        (let* ((loc-pat (realgud-cmdbuf-pat "brkpt-set")))
	  (if loc-pat
	      (let ((bp-num-group   (realgud-loc-pat-num loc-pat))
		    (loc-regexp     (realgud-loc-pat-regexp loc-pat))
		    (file-group     (realgud-loc-pat-file-group loc-pat))
		    (line-group     (realgud-loc-pat-line-group loc-pat))
		    (text-group     (realgud-loc-pat-text-group loc-pat))
		    (ignore-file-re (realgud-loc-pat-ignore-file-re loc-pat))
		    )
		(if loc-regexp
		    (if (string-match loc-regexp text)
			(let* ((bp-num (match-string bp-num-group text))
			       (filename (match-string file-group text))
			       (line-str (match-string line-group text))
			       (source-str (and text-group (match-string text-group text)))
			       (lineno (string-to-number (or line-str "1")))
			       )
			  (unless line-str
			    (message "line number not found -- using 1"))
			  (if (and filename lineno)
			      (let ((loc-or-error
				     (realgud:file-loc-from-line
				      filename lineno
				      cmd-mark
				      source-str
				      (string-to-number bp-num)
				      ignore-file-re
				      )))
				(if (stringp loc-or-error)
				    (progn
				      (message loc-or-error)
				      ;; set to return nil
				      nil)
				  ;; else
				  (progn
				    ;; Add breakpoint to list of breakpoints
				    (realgud-cmdbuf-info-bp-list=
				     (cons loc-or-error (realgud-sget 'cmdbuf-info 'bp-list)))
				    ;; Set to return location
				    loc-or-error)))
			    nil)))
		  nil))
            nil))
      )
    )
)

(defun realgud-track-bp-delete(text &optional cmd-mark cmdbuf ignore-file-re)
  "Do regular-expression matching to see if a breakpoint has been
deleted inside string TEXT. Return a list of breakpoint locations
of the breakpoints found in command buffer."

  ; NOTE: realgud-cmdbuf-info is a buffer variable local to the process
  ; running the debugger. It contains a realgud-cmdbuf-info "struct". In
  ; that struct is the regexp hash to match positions. By setting the
  ; the fields of realgud-cmdbuf-info appropriately we can accomodate a
  ; family of debuggers -- one at a time -- for the buffer process.

  (setq cmdbuf (or cmdbuf (current-buffer)))
  (with-current-buffer cmdbuf
    (unless (realgud:track-complain-if-not-in-cmd-buffer cmdbuf t)
      (let* ((loc-pat (realgud-cmdbuf-pat "brkpt-del")))
        (when loc-pat
          (let ((bp-num-group (realgud-loc-pat-num loc-pat))
                (loc-regexp   (realgud-loc-pat-regexp loc-pat)))
            (when (and loc-regexp (string-match loc-regexp text))
              (let* ((bp-nums-str (match-string bp-num-group text))
                     (bp-num-strs (split-string bp-nums-str "[^0-9]+" t))
                     (bp-nums (mapcar #'string-to-number bp-num-strs))
                     (info realgud-cmdbuf-info)
                     (all-bps (realgud-cmdbuf-info-bp-list info))
                     (found-locs nil))
                (dolist (loc all-bps)
                  (when (memq (realgud-loc-num loc) bp-nums)
                    (push loc found-locs)
                    ;; Remove loc from breakpoint list
                    (realgud-cmdbuf-info-bp-list=
                     (remove loc (realgud-cmdbuf-info-bp-list info)))))
                ;; return the locations
                found-locs))))))))

(defun realgud-track-bp-enable-disable(text loc-pat enable? &optional cmdbuf)
  "Do regular-expression matching see if a breakpoint has been enabled or disabled inside
string TEXT. If we match, we will do the action to the breakpoint found and return the
breakpoint location. Otherwise return nil.
"
  (setq cmdbuf (or cmdbuf (current-buffer)))
  (with-current-buffer cmdbuf
    (if (realgud-cmdbuf?)
	(let* ((found-loc nil))
	  (if loc-pat
	      (let ((bp-num-group (realgud-loc-pat-num loc-pat))
		    (loc-regexp   (realgud-loc-pat-regexp loc-pat)))
		(if (and loc-regexp (string-match loc-regexp text))
		    (let* ((bp-num (string-to-number (match-string bp-num-group text)))
			   (info realgud-cmdbuf-info)
			   (bp-list (realgud-cmdbuf-info-bp-list info))
			   (loc)
			   )
		      (while (and (not found-loc) (setq loc (car-safe bp-list)))
			(setq bp-list (cdr bp-list))
			(when (eq (realgud-loc-num loc) bp-num)
			  (setq found-loc loc)
			  (let ((src-buffer (realgud-loc-goto loc)))
			    (realgud-cmdbuf-add-srcbuf src-buffer cmdbuf)
			    (with-current-buffer src-buffer
			      (realgud-bp-enable-disable-info bp-num enable? loc src-buffer)
			      )))
			)
		      ;; return the location:
		      found-loc)
		  nil))
	    nil))
      (and (message "Current buffer %s is not a debugger command buffer"
		    (current-buffer)) nil)
      )
    )
)

(defun realgud-track-loc-remaining(text)
  "Return the portion of TEXT starting with the part after the
loc-regexp pattern"
  (if (realgud-cmdbuf?)
      (let* ((loc-pat (realgud-cmdbuf-pat "loc"))
	     (loc-regexp (realgud-loc-pat-regexp loc-pat))
	     )
	(if loc-regexp
	    (if (string-match loc-regexp text)
		(substring text (match-end 0))
	      nil)
	  nil))
    nil)
  )

(defun realgud-track-selected-frame(text)
  "Return a selected frame number found in TEXT or nil if none found."
  (if (realgud-cmdbuf?)
      (let ((selected-frame-pat (realgud-cmdbuf-pat "selected-frame"))
	    (frame-num-regexp)
	    )
	(if (and selected-frame-pat
		 (setq frame-num-regexp (realgud-loc-pat-regexp
					 selected-frame-pat)))
	    (if (string-match frame-num-regexp text)
		(let ((frame-num-group (realgud-loc-pat-num selected-frame-pat)))
		  (string-to-number (match-string frame-num-group text)))
	      nil)
	  nil))
    nil)
  )


(defun realgud-track-termination?(text)
  "Return 't and call `realgud:terminate' we we have a termination message"
  (if (realgud-cmdbuf?)
      (let ((termination-re (realgud-cmdbuf-pat "termination"))
	    )
	(if (and termination-re (string-match termination-re text))
	    (progn
	      (realgud:terminate (current-buffer))
	      't)
	  nil)
	)
    )
  )

(defun realgud-track-divert-prompt(text cmdbuf to)
  "Return a cons node of the part before the prompt-regexp and the part
   after the prompt-regexp-prompt. If not found return nil."
  (with-current-buffer cmdbuf
    ;; (message "+++3 %s, buf: %s" text (buffer-name))
    (if (realgud-cmdbuf?)
	(let* ((prompt-pat (realgud-cmdbuf-pat "prompt"))
	       (prompt-regexp (realgud-loc-pat-regexp prompt-pat))
	       )
	  (if prompt-regexp
	      (if (string-match prompt-regexp text)
		  (progn
		    (setq realgud-track-divert-string
			  (substring text 0 (match-beginning 0)))
		    ;; We've got desired output, so reset divert output.
		    (realgud-cmdbuf-info-divert-output?= nil)
		    (cond ((search-backward-regexp prompt-regexp)
			   (kill-region realgud-last-output-start (point))
			   (goto-char (point-max)))
			  ('t (kill-region realgud-last-output-start to)))
		    )
	      ))
	  )
      )
    )
  )

(defun realgud-goto-line-for-loc-pat (pt &optional opt-realgud-loc-pat)
  "Display the location mentioned in line described by
PT. OPT-REALGUD-LOC-PAT is used to get regular-expresion pattern
matching information. If not supplied we use the current buffer's \"location\"
pattern found via realgud-cmdbuf information. nil is returned if we can't
find a location. non-nil if we can find a location.
"
  (interactive "d")
  (save-excursion
    (goto-char pt)
    (let*
	((cmdbuf (current-buffer))
	 (cmd-mark (point-marker))
	 (curr-proc (get-buffer-process cmdbuf))
	 (start (line-beginning-position))
	 (end (line-end-position))
	 (loc-pat (or opt-realgud-loc-pat (realgud-cmdbuf-pat "loc")))
	 (loc)
	 )
      (unless (and loc-pat (realgud-loc-pat-p loc-pat))
	(error "Can't find location information for %s" cmdbuf))
      (setq loc (realgud-track-loc (buffer-substring-no-properties start end)
				cmd-mark
				(realgud-loc-pat-regexp loc-pat)
				(realgud-loc-pat-file-group loc-pat)
				(realgud-loc-pat-line-group loc-pat)
				nil
				(realgud-loc-pat-ignore-file-re loc-pat)
				))
      (if (stringp loc)
	  (message loc)
	(if loc (or (realgud-track-loc-action loc cmdbuf) 't)
	  nil))
      ))
    )

(defun realgud:track-set-debugger (debugger-name)
  "Set debugger name and information associated with that
debugger for the buffer process. This info is returned or nil if
we can't find a debugger with that information.`.
"
  ;; FIXME: turn into fn which can be used by realgud-backtrack-set-debugger
  (interactive
   (list (completing-read "Debugger name: " realgud-pat-hash)))
  (let* ((base-variable-name
	  (or (gethash debugger-name realgud:variable-basename-hash)
	      debugger-name))
         (regexp-hash (gethash debugger-name realgud-pat-hash))
         (command-hash (gethash debugger-name realgud-command-hash))
	)
    (unless regexp-hash
      ;; FIXME: phase out realgud:debugger-name-transform
      (setq base-variable-name (realgud:debugger-name-transform debugger-name))
      (setq regexp-hash (gethash base-variable-name realgud-pat-hash))
      (setq command-hash (gethash base-variable-name realgud-command-hash))
      )
    (if regexp-hash
	(let* (
	       (mode-name (concat " " (capitalize base-variable-name) "-Track"))
	       (specific-track-mode (intern (concat base-variable-name "-track-mode")))
	       )
	  (realgud-cmdbuf-init (current-buffer)
                               debugger-name regexp-hash
                               command-hash base-variable-name)
	  (if (and (not (eval specific-track-mode))
		   (functionp specific-track-mode))
	      (funcall specific-track-mode 't))
	  )
      (progn
	(message "I don't have %s listed as a debugger." debugger-name)
	nil)
      )))

;; FIXME: need better name for this and next fn.
(defun realgud-goto-line-for-pt-and-type (pt type pat-hash)
  "Position the source code at the location that is matched by
PAT-HASH with key TYPE. The line at PT is used as the string
to match against and has location info embedded in it"
  (realgud-goto-line-for-loc-pat pt (gethash type pat-hash)))


(defun realgud-goto-line-for-pt (pt pattern-key)
  "Position the source code at the location indicated by a
pattern found in the command buffer with pattern-key
PATTERN-KEY. (PATTERN-KEY is something like 'debugger-backtrace'
or 'loc'.) The line at PT is used as the string to match against
and has location info embedded in it"
  (interactive "d")
  (unless (realgud-cmdbuf?)
    (error "You need to be in a debugger command buffer to run this"))
  (let* ((debugger-name (realgud-cmdbuf-debugger-name))
	 (debugger-pat-hash (gethash debugger-name realgud-pat-hash)))
    (realgud-goto-line-for-pt-and-type pt pattern-key debugger-pat-hash)
    )
  )

(defun realgud:goto-debugger-backtrace-line (pt)
  "Position the source code at the location indicated by matching a
command buffer's debugger backtrace pattern against the line at PT."
  (interactive "d")
  (unless (realgud-goto-line-for-pt pt "debugger-backtrace")
    (message "Line didn't match a debugger backtrace location.")
    ))

(defun realgud:goto-lang-backtrace-line (pt)
  "Position the source code at the location indicated by matching a
command buffer's programming-language backtrace pattern against the line at PT."
  (interactive "d")
  (unless (realgud-goto-line-for-pt pt "lang-backtrace")
    (message "Line didn't match a programming-language backtrace location.")
    ))

(defun realgud:goto-debugger-loc-line (pt)
  "Position the source-code at the location indicated by matching a
command buffer's debugger location pattern against the line at PT."
  (interactive "d")
  (unless (realgud-goto-line-for-pt pt "loc")
    (message "Line didn't match a debugger location indicator line.")
    ))

(provide-me "realgud-")
