;;; matlab-maint.el --- matlab mode maintainer utilities -*- lexical-binding: t -*-
;;
;; Copyright (C) 2024 Eric Ludlam
;;
;; Author: Eric Ludlam <eludlam@emacsvm>
;;
;; 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 https://www.gnu.org/licenses/.

;;; Commentary:
;;
;; Interface for maintainers of matlab mode.

(require 'matlab)
(require 'matlab-shell)
(require 'matlab-netshell)
(require 'semantic/symref)
(require 'semantic/symref/list)

;;; Code:

(defgroup matlab-maint nil
  "MATLAB maintainer utilities."
  :prefix "matlab-maint-"
  :group 'matlab)

;;; Minor Mode Definition
;;
(defvar matlab-maint-mode-map
  (let ((km (make-sparse-keymap)))
    ;; compile debug cycle
    (define-key km [f8] 'matlab-maint-run-tests)
    (define-key km [f9] 'matlab-maint-compile-matlab-emacs)
    (define-key km [f10] 'matlab-maint-reload-mode)
    ;; coding
    (define-key km [f7] 'matlab-maint-symref-this)
    ;; matlab
    (define-key km [f5] 'matlab-maint-show-info)
    km)
  "Keymap used by matlab mode maintainers.")

(easy-menu-define
  matlab-maint-menu matlab-maint-mode-map "MATLAB Maintainer's Minor Mode."
  '("MMaint"
    ["Compile" matlab-maint-compile-matlab-emacs t]
    ["Clean" matlab-maint-compile-clean t]
    ["Run Tests" matlab-maint-run-tests t]
    ["Pick Emacs to run" matlab-maint-pick-emacs t]
    ["Toggle IO Logging" matlab-maint-toggle-io-tracking
     :style toggle :selected matlab-shell-io-testing ]
    ["Display logger frame" matlab-maint-toggle-logger-frame
     :style toggle :selected (and matlab-maint-logger-frame
                                  (frame-live-p matlab-maint-logger-frame)) ]
    ))

;;;###autoload
(define-minor-mode matlab-maint-minor-mode
  "Minor mode for `matlab-mode' maintainers."
  :init-value nil
  :lighter " MMaint"
  :keymap matlab-maint-mode-map)

;;;###autoload
(define-globalized-minor-mode global-matlab-maint-minor-mode
  matlab-maint-minor-mode
  (lambda ()
    "Should we turn on in this buffer? Only if in the project."
    (let ((dir (expand-file-name default-directory))
          (ml (file-name-directory (expand-file-name (locate-library "matlab")))))
      (when (string= ml (substring dir 0 (min (length dir) (length ml))))
        (matlab-maint-minor-mode 1))))
  :group 'matlab-maint)

;;; Testing stuff in M buffers
;;
(defun matlab-maint-show-info ()
  "Show info about line in current matlab buffer."
  (interactive)
  (when (eq major-mode 'matlab-mode)
    (matlab-show-line-info)
    ))

;;; HACKING ELISP
;;

(defun matlab-maint-symref-this ()
  "Open a symref buffer on symbol under cursor."
  (interactive)
  (save-buffer)
  (semantic-fetch-tags)
  (let ((ct (semantic-current-tag)))
    ;; Must have a tag...
    (when (not ct) (error "Place cursor inside tag to be searched for"))
    ;; Gather results and tags
    (message "Gathering References for %s.." (semantic-tag-name ct))
    (let* ((name (semantic-tag-name ct))
           (res (semantic-symref-find-references-by-name name)))
      (semantic-symref-produce-list-on-results res name)
      (semantic-symref-list-expand-all) )))


;;; COMPILE AND TEST
;;
;; Helpful commands for maintainers.
(defcustom matlab-maint-compile-opts '("emacs" "emacs24" "emacs25" "emacs26")
  "Various Emacs versions we can use to compile with."
  :group 'matlab-maint
  :type '(repeat (string :tag "Emacs Command: ")))

(defcustom matlab-maint-compile-emacs "emacs"
  "The EMACS to pass into make."
  :group 'matlab-maint
  :type 'string)

(defun matlab-maint-pick-emacs (emacscmd)
  "Select the EMACSCMD to use for compiling."
  (interactive (list (completing-read "Emacs to compile MATLAB: "
                                      matlab-maint-compile-opts
                                      nil
                                      t
                                      (car matlab-maint-compile-opts))))
  (setq matlab-maint-compile-emacs emacscmd))

(defun matlab-maint-compile-matlab-emacs ()
  "Run make for the matlab-emacs project."
  (interactive)
  (save-excursion
    (matlab-maint-set-buffer-to "matlab.el")
    (if (string= matlab-maint-compile-emacs "emacs")
        (compile "make")
      (compile (concat "make EMACS=" matlab-maint-compile-emacs))))
  )

(defun matlab-maint-compile-clean ()
  "Run make for the matlab-emacs project."
  (interactive)
  (save-excursion
    (matlab-maint-set-buffer-to "matlab.el")
    (compile "make clean")
    ))

(defun matlab-maint-run-tests (arg)
  "Run the test for matlab mode.
With universal ARG, ask for the code to be run with output tracking turned on."
  (interactive "P")
  (when (buffer-file-name)
    (save-buffer))
  (save-excursion
    (matlab-maint-set-buffer-to "tests/Makefile")
    (if (or arg matlab-shell-io-testing)
        ;; Ask for debug
        (compile "make TESTDEBUG=1")
      ;; No debugging
      (compile "make")))
  (switch-to-buffer "*compilation*")
  (delete-other-windows)
  (goto-char (point-max)))

(defun matlab-maint-reload-mode ()
  "Reload matlab mode, and refresh displayed ML buffers modes."
  (interactive)
  (load-library "matlab-syntax")
  (load-library "matlab-scan")
  (load-library "matlab")
  (mapc (lambda (b) (with-current-buffer b
                      (when (eq major-mode 'matlab-mode)
                        (message "Updating matlab mode in %S" b)
                        (matlab-mode))))
        (buffer-list (selected-frame)))
  (message "loading done")
  )
;;; MATLAB SHELL tools
;;

(defun matlab-maint-set-buffer-to (file)
  "Set the current buffer to FILE found in matlab-mode's source.
Return the buffer."
  (let* ((ml (file-name-directory (locate-library "matlab")))
         (newf (expand-file-name file ml)))
    (set-buffer (find-file-noselect newf))))

(defun matlab-maint-toggle-io-tracking ()
  "Toggle tracking of IO with MATLAB Shell."
  (interactive)
  (setq matlab-shell-io-testing (not matlab-shell-io-testing))
  (message "MATLAB Shell IO logging %s" (if matlab-shell-io-testing
                                            "enabled" "disabled")))

(defvar matlab-maint-logger-frame nil
  "Frame displaying log information.")

(defun matlab-maint-toggle-logger-frame ()
  "Display a frame showing various log buffers."
  (interactive)
  (if (and matlab-maint-logger-frame
           (frame-live-p matlab-maint-logger-frame))
      (progn
        (delete-frame matlab-maint-logger-frame)
        (setq matlab-maint-logger-frame nil))
    ;; Otherwise, create ...
    (setq matlab-maint-logger-frame (make-frame))
    (with-selected-frame matlab-maint-logger-frame
      (delete-other-windows)
      (switch-to-buffer "*Messages*")
      (when (matlab-netshell-client)
        (split-window-horizontally)
        (other-window 1)
        (switch-to-buffer (process-buffer (matlab-netshell-client)))
        ))))

(provide 'matlab-maint)

;;; matlab-maint.el ends here

;; LocalWords:  Ludlam eludlam emacsvm netshell symref keymap MMaint defun defcustom emacscmd setq
;; LocalWords:  TESTDEBUG mapc newf noselect progn
