;;; sysml-mode.el --- Major mode for SysML v2 (Systems Modeling Language)  -*- lexical-binding: t; -*-

;; Copyright (C) 2026 DeciSym, LLC
;; SPDX-License-Identifier: ISC
;;
;; Author: Donald Anthony Pellegrino Jr., Ph.D. <don@decisym.ai>
;; Assisted-by: GPT:gpt-5-5
;; Keywords: languages, modeling, systems engineering
;; Package-Version: 20260528.1759
;; Package-Revision: e8ff9eea7f8f
;; Package-Requires: ((emacs "25.1"))
;; URL: https://github.com/decisym/sysml-mode

;;; Commentary:

;; This package defines `sysml-mode', a major mode for SysML v2
;; textual notation files.

;;; Code:

(require 'cl-lib)
(require 'elec-pair)
(require 'grep)
(require 'hideshow)
(require 'prog-mode)
(require 'project nil t)

(defgroup sysml nil
  "Major mode for editing SysML v2 files."
  :prefix "sysml-"
  :group 'languages)

(defcustom sysml-validator-script nil
  "Path to the validate-sysml command.
If nil, auto-detect from buffer's directory."
  :type '(choice (const :tag "Auto-detect" nil)
                 (file :tag "Command path"))
  :group 'sysml)

(defcustom sysml-validate-on-save nil
  "Whether to validate SysML files on save."
  :type 'boolean
  :group 'sysml)

(defconst sysml--identifier-regexp "[A-Za-z_][A-Za-z0-9_]*"
  "Regexp matching an unquoted SysML identifier.")

(defconst sysml--definition-forms
  '(("Packages" . "package")
    ("Parts" . "part def")
    ("Attributes" . "attribute def")
    ("Items" . "item def")
    ("Ports" . "port def")
    ("Actions" . "action def")
    ("States" . "state def")
    ("Requirements" . "requirement def")
    ("Constraints" . "constraint def")
    ("Connections" . "connection def")
    ("Interfaces" . "interface def")
    ("Allocations" . "allocation def")
    ("Verifications" . "verification def")
    ("Use Cases" . "use case def")
    ("Calculations" . "calc def")
    ("Analyses" . "analysis def")
    ("Concerns" . "concern def")
    ("Viewpoints" . "viewpoint def")
    ("Views" . "view def")
    ("Metadata" . "metadata def")
    ("Individuals" . "individual def")
    ("Enumerations" . "enum def"))
  "Definition forms recognized by SysML navigation and completion.
Each element is (MENU-TITLE . FORM), where FORM precedes the
definition name in SysML text.")

(defconst sysml--usage-keywords
  '("part" "attribute" "ref" "port" "item" "connection")
  "SysML usage keywords whose typed declarations define local symbols.")

(defun sysml--definition-form-regexp (form)
  "Return a regexp matching definition FORM with flexible spacing."
  (mapconcat #'regexp-quote (split-string form) "\\s-+"))

(defun sysml--definition-forms-regexp ()
  "Return a regexp matching all recognized SysML definition forms."
  (mapconcat (lambda (definition)
               (sysml--definition-form-regexp (cdr definition)))
             sysml--definition-forms
             "\\|"))

(defun sysml--any-definition-regexp (&optional symbol)
  "Return a regexp matching a SysML definition.
When SYMBOL is non-nil, match only definitions of that symbol.
Match group 1 is the definition form and group 2 is the definition
name."
  (concat "^\\s-*\\(" (sysml--definition-forms-regexp) "\\)\\s-+\\("
          (if symbol
              (regexp-quote symbol)
            sysml--identifier-regexp)
          "\\)\\_>"))

(defun sysml--definition-form-label (form)
  "Return a display label for definition FORM."
  (replace-regexp-in-string
   "\\s-+def\\'" ""
   (replace-regexp-in-string "\\s-+" " " form)))

;; Syntax highlighting
(defconst sysml-font-lock-keywords
  (eval-when-compile
    (let* (
         ;; Keyword categories based on OMG SysML v2 specification

         ;; Package and namespace keywords
         (package-keywords '("package" "import" "private" "public" "standard" "library"))

         ;; Definition keywords (def constructs)
         (definition-keywords '("def" "part" "attribute" "item" "port" "action" "state"
                                "requirement" "constraint" "connection" "interface" "allocation"
                                "verification" "use" "case" "calc" "analysis" "concern"
                                "viewpoint" "view" "metadata" "individual" "snapshot" "timeslice"
                                "enum" "variation" "variant" "stream" "message" "flow"
                                "succession" "feature" "abstract" "readonly" "derived" "end"
                                "binding" "succession"))

         ;; Keywords that can precede "def" for type definitions
         (def-types '("part" "attribute" "item" "port" "action" "state"
                      "requirement" "constraint" "connection" "interface" "allocation"
                      "verification" "use" "case" "calc" "analysis" "concern"
                      "viewpoint" "view" "metadata" "individual" "enum"))

         ;; Behavioral and structural keywords
         (behavioral-keywords '("exhibit" "perform" "then" "first" "accept" "send" "via"
                                "entry" "exit" "do" "transition" "if" "else" "while" "for"
                                "return" "assert" "assume" "require" "fork" "join" "merge"
                                "decide" "loop" "parallel" "when" "at" "new"))

         ;; Relationship and constraint keywords
         (relationship-keywords '("specializes" "subsets" "redefines" "references" "conjugate"
                                  "chains" "inverse" "readonly" "derived" "composite"
                                  "bind" "connect" "satisfy" "verify" "subject" "objective"
                                  "from" "to" "about" "actor" "stakeholder"))

         ;; Directional keywords
         (direction-keywords '("in" "out" "inout"))

         ;; Special declaration keywords
         (declaration-keywords '("doc" "comment" "language" "inv" "pre" "post" "body" "result"
                                 "filter" "rendering" "expose" "ref" "all" "that" "self"
                                 "featured" "by" "typing" "chaining" "inverting" "owned"
                                 "member" "multiplicity" "ordered" "nonunique" "sequence"
                                 "alias" "for" "occurrence" "meta"))

         ;; Logical operators
         (logical-operators '("and" "or" "xor" "not" "implies"))

         ;; Built-in types - NONE, SysML v2 has no built-in types
         ;; All types (String, Boolean, Integer, Real, etc.) are defined in standard library
         (builtin-types '())

         ;; Generate regexp patterns
         (package-keywords-regexp (regexp-opt package-keywords 'words))
         (definition-keywords-regexp (regexp-opt definition-keywords 'words))
         (def-types-regexp (regexp-opt def-types 'words))
         (behavioral-keywords-regexp (regexp-opt behavioral-keywords 'words))
         (relationship-keywords-regexp (regexp-opt relationship-keywords 'words))
         (direction-keywords-regexp (regexp-opt direction-keywords 'words))
         (declaration-keywords-regexp (regexp-opt declaration-keywords 'words))
         (logical-operators-regexp (regexp-opt logical-operators 'words))
         (builtin-types-regexp (regexp-opt builtin-types 'words)))

    `(
      ;; Documentation blocks: doc /* ... */
      ("\\<doc\\>\\s-*\\(/\\*\\(?:.\\|\n\\)*?\\*/\\)"
       (0 font-lock-doc-face))

      ;; Multi-line comments /* ... */
      ("/\\*\\(?:.\\|\n\\)*?\\*/" . font-lock-comment-face)

      ;; Single-line comments //
      ("//.*$" . font-lock-comment-face)

      ;; Package and namespace keywords
      (,package-keywords-regexp . font-lock-keyword-face)

      ;; Definition keywords
      (,definition-keywords-regexp . font-lock-keyword-face)

      ;; Behavioral keywords
      (,behavioral-keywords-regexp . font-lock-keyword-face)

      ;; Relationship keywords
      (,relationship-keywords-regexp . font-lock-keyword-face)

      ;; Direction keywords
      (,direction-keywords-regexp . font-lock-builtin-face)

      ;; Declaration keywords
      (,declaration-keywords-regexp . font-lock-constant-face)

      ;; Logical operators
      (,logical-operators-regexp . font-lock-builtin-face)

      ;; Built-in types
      (,builtin-types-regexp . font-lock-type-face)

      ;; Definition type names: "part def Vehicle" - Vehicle is colored
      ;; Optimized: Use pre-compiled regexp
      (,(concat def-types-regexp "\\s-+def\\s-+\\([A-Za-z_<][A-Za-z0-9_>]*\\)")
       (2 font-lock-function-name-face))

      ;; Package names: "package SimpleVehicleModel" - SimpleVehicleModel is colored
      ("\\<package\\s-+\\([A-Za-z][A-Za-z0-9_]*\\)" 1 font-lock-function-name-face)

      ;; Instance/property names with typing: "attribute mass :", "port x :"
      ("\\<\\(part\\|attribute\\|ref\\|port\\|item\\)\\s-+\\([A-Za-z_][A-Za-z0-9_]*\\)\\s-*:"
       (2 font-lock-variable-name-face))

      ;; State usages: "state normal;", "state healthStates {"
      ("\\<state\\s-+\\([A-Za-z_][A-Za-z0-9_]*\\)\\s-*[{;]"
       (1 font-lock-variable-name-face))

      ;; Transition definitions: "transition normal_To_maintenance"
      ("\\<transition\\s-+\\([A-Za-z_][A-Za-z0-9_]*\\)"
       (1 font-lock-variable-name-face))

      ;; Action usages after entry/exit: "entry action initial"
      ("\\<\\(entry\\|exit\\)\\s-+action\\s-+\\([A-Za-z_][A-Za-z0-9_]*\\)"
       (2 font-lock-variable-name-face))

      ;; Parameters with item: "in item ignitionCmd"
      ("\\<\\(in\\|out\\|inout\\)\\s-+item\\s-+\\([A-Za-z_][A-Za-z0-9_]*\\)"
       (2 font-lock-variable-name-face))

      ;; Parameters without item: "out temp;"
      ("\\<\\(in\\|out\\|inout\\)\\s-+\\([A-Za-z_][A-Za-z0-9_]*\\)\\s-*[;:]"
       (2 font-lock-variable-name-face))

      ;; Type references after : (not :> or :>>): ": Real", ": Time::DateTime"
      ;; Optimized: Simpler pattern without complex lookbehind
      ("\\(?:^\\|\\s-\\):\\s-*\\([A-Z][A-Za-z0-9_:]*\\)" 1 font-lock-type-face)

      ;; Operators: :> (specializes), :>> (redefines), :: (qualified name), ~ (conjugate)
      (":\\(>>\\|>\\)" . font-lock-keyword-face)
      ("::" . font-lock-keyword-face)
      ("~" . font-lock-keyword-face)

      ;; Multiplicity: [0..1], [0..*], [1], etc.
      ("\\[\\([0-9]+\\)\\.\\.\\([0-9*]+\\)\\]" . font-lock-constant-face)
      ("\\[\\([0-9*]+\\)\\]" . font-lock-constant-face)

      ;; Single-quoted identifiers (SysML v2 quoted names)
      ("'[^']*'" . font-lock-constant-face)

      ;; String literals
      ("\"[^\"]*\"" . font-lock-string-face)

      ;; Numeric literals (not part of identifiers like Wheel_1)
      ;; Use Emacs word boundaries for more efficient matching
      ("\\<\\([0-9]+\\(?:\\.[0-9]+\\)?\\)\\>" 1 font-lock-constant-face)

      ;; Boolean literals
      ("\\<\\(true\\|false\\)\\>" . font-lock-constant-face)

      ;; Metadata tags: #logical, #physical
      ("#\\(logical\\|physical\\)" . font-lock-preprocessor-face)

      ;; Annotations: @MetadataName
      ("@\\([A-Za-z][A-Za-z0-9_]*\\)" 1 font-lock-preprocessor-face))))
  "Keyword highlighting for SysML mode.")

;; Syntax table
(defvar sysml-mode-syntax-table
  (let ((st (make-syntax-table)))
    ;; C-style comments
    (modify-syntax-entry ?/ ". 124b" st)
    (modify-syntax-entry ?* ". 23" st)
    (modify-syntax-entry ?\n "> b" st)

    ;; Strings - only double quotes
    ;; Single quotes are used for quoted names in SysML, not strings
    (modify-syntax-entry ?\" "\"" st)
    (modify-syntax-entry ?' "w" st)  ; Word constituent for quoted names

    ;; Operators
    (modify-syntax-entry ?: "." st)
    (modify-syntax-entry ?> "." st)
    (modify-syntax-entry ?< "." st)
    (modify-syntax-entry ?= "." st)

    ;; Parentheses and brackets
    (modify-syntax-entry ?\( "()" st)
    (modify-syntax-entry ?\) ")(" st)
    (modify-syntax-entry ?\[ "(]" st)
    (modify-syntax-entry ?\] ")[" st)
    (modify-syntax-entry ?\{ "(}" st)
    (modify-syntax-entry ?\} "){" st)

    st)
  "Syntax table for SysML mode.")

;; Indentation
(defun sysml-indent-line ()
  "Indent current line as SysML code.
Handles brace-based indentation, attribute declarations, and
continuation lines."
  (interactive)
  (let ((indent-level 0)
        (pos (- (point-max) (point))))
    (save-excursion
      (beginning-of-line)
      (let* ((ppss (syntax-ppss))
             (depth (nth 0 ppss))          ; Get paren/brace depth
             (in-string (nth 3 ppss))      ; Check if in string
             (in-comment (nth 4 ppss))     ; Check if in comment
             (current-line-empty (looking-at "^[ \t]*$")))
        (unless (or in-string in-comment)
          ;; Calculate base indentation from nesting depth
          (setq indent-level (* (or depth 0) tab-width))
          ;; Handle special cases
          (cond
           ;; Closing brace on current line
           ((looking-at "^[ \t]*}")
            (setq indent-level (max 0 (- indent-level tab-width))))
           ;; After attribute/port/part declaration with colon
           ((and (not (bobp))
                 (save-excursion
                   (forward-line -1)
                   (looking-at ".*\\(attribute\\|port\\|part\\|ref\\|item\\).*:[ \t]*$")))
            (setq indent-level (+ indent-level tab-width)))
           ;; After state/transition/action keywords
           ((and (not (bobp))
                 (save-excursion
                   (forward-line -1)
                   (looking-at ".*\\(state\\|transition\\|action\\|constraint\\).*{[ \t]*$")))
            nil) ; Keep calculated indent
           ;; Continuation after semicolon or comma
           ((and (not (bobp))
                 (not current-line-empty)
                 (save-excursion
                   (forward-line -1)
                   (not (looking-at ".*[{};][ \t]*$"))))
            ;; Check if this looks like a continuation
            (save-excursion
              (forward-line -1)
              (when (looking-at ".*[,:=][ \t]*$")
                (setq indent-level (+ indent-level tab-width)))))))))
    ;; Apply the indentation
    (indent-line-to indent-level)
    ;; Restore point position relative to end of buffer
    (when (> (- (point-max) pos) (point))
      (goto-char (- (point-max) pos)))))

;; Imenu support for code navigation
(defconst sysml-imenu-generic-expression
  (mapcar (lambda (definition)
            (list (car definition)
                  (concat "^\\s-*"
                          (sysml--definition-form-regexp (cdr definition))
                          "\\s-+\\(" sysml--identifier-regexp "\\)\\_>")
                  1))
          sysml--definition-forms)
  "Imenu expressions for SysML mode.
Provides menu-based navigation to all major definition types.")

;; Which-function support
(defun sysml-which-function ()
  "Return current function name for `which-function-mode'.
Returns the name of the current SysML definition (part, action, state, etc.)."
  (save-excursion
    (let ((case-fold-search nil))
      ;; Search backward for any definition
      (when (re-search-backward (sysml--any-definition-regexp) nil t)
        (let ((type (match-string 1))
              (name (match-string 2)))
          (when name
            (if (and type (string-match-p "\\s-+def\\'" type))
                (format "%s %s" (sysml--definition-form-label type) name)
              name)))))))

;; Electric pairs configuration
(defvar sysml-mode-electric-pairs
  '((?{ . ?})       ; Braces for blocks
    (?\[ . ?\])     ; Brackets for multiplicity
    (?\( . ?\))     ; Parentheses for expressions
    (?\" . ?\")     ; Double quotes for strings
    (?< . ?>))      ; Angle brackets for generics
  "Electric pairs for SysML mode.
Automatically inserts matching closing delimiters.")

;; Fill paragraph function - only handle comments
(defun sysml-fill-paragraph (&optional justify)
  "Fill paragraph in SysML mode.
Only handles filling when inside comments.
Returns nil for non-comments to use default behavior.
Optional argument JUSTIFY controls text justification."
  (interactive "*P")
  (let* ((ppss (syntax-ppss))
         (in-comment (nth 4 ppss))
         (comment-start-pos (nth 8 ppss)))
    (cond
     ;; Handle /* */ comments specially
     ((and in-comment comment-start-pos
           (save-excursion
             (goto-char comment-start-pos)
             (looking-at "/\\*")))
      ;; Let Emacs handle the filling with proper settings
      (let ((fill-prefix
             (save-excursion
               (goto-char comment-start-pos)
               (beginning-of-line)
               (skip-chars-forward " \t")
               (make-string (current-column) ?\s))))
        (save-excursion
          (goto-char comment-start-pos)
          (search-forward "*/" nil t)
          (let ((comment-end (point)))
            ;; Use the standard fill-region-as-paragraph
            (fill-region-as-paragraph comment-start-pos comment-end justify))))
      t)
     ;; Handle // comments
     ((and in-comment comment-start-pos
           (save-excursion
             (goto-char comment-start-pos)
             (looking-at "//")))
      (fill-comment-paragraph justify)
      t)
     ;; Not in a comment - don't do anything
     (t nil))))

;; Prettify symbols configuration
(defcustom sysml-prettify-symbols-alist
  '(;; Operators
    (":>" . ?⊃)       ; Specialization (subset/superset symbol)
    (":>>" . ?⊇)      ; Redefinition (subset-or-equal symbol)
    ("::" . ?∷)       ; Qualified name separator (proportion symbol)
    ("<=" . ?≤)       ; Less than or equal
    (">=" . ?≥)       ; Greater than or equal
    ("!=" . ?≠)       ; Not equal
    ("->" . ?→)       ; Arrow (for flows/transitions)
    ("=>" . ?⇒)       ; Implies
    ("<->" . ?↔)      ; Bidirectional
    ("and" . ?∧)      ; Logical AND
    ("or" . ?∨)       ; Logical OR
    ("not" . ?¬)      ; Logical NOT
    ("xor" . ?⊕)      ; Logical XOR
    ("[*]" . ?∞))     ; Unbounded multiplicity
  "Alist of symbol prettifications for SysML mode.
Each element is a cons cell (STRING . CHAR) where STRING is the text
to replace and CHAR is the Unicode character to display instead.
Set to nil to disable prettification."
  :type '(alist :key-type string :value-type character)
  :group 'sysml)

(defcustom sysml-enable-prettify-symbols t
  "Whether to enable `prettify-symbols-mode' in SysML buffers."
  :type 'boolean
  :group 'sysml)

;; Validation integration

;; Buffer-local cache for validator command location
(defvar-local sysml--cached-validator nil
  "Cached path to the validator command for this buffer.")

(defun sysml-find-validator (&optional force-refresh)
  "Find the validate-sysml command in the current directory tree or system PATH.
First checks the custom `sysml-validator-script' variable, then
searches the project directory tree, and finally checks the system PATH.
The result is cached per buffer to improve performance.
Optional argument FORCE-REFRESH forces a fresh search, ignoring the cache."
  (when force-refresh
    (setq sysml--cached-validator nil))
  (or sysml--cached-validator
      (setq sysml--cached-validator
            (or sysml-validator-script
                (when buffer-file-name
                  (let ((directory (locate-dominating-file
                                    (file-name-directory buffer-file-name)
                                    "validate-sysml")))
                    (when directory
                      (expand-file-name "validate-sysml" directory))))
                (executable-find "validate-sysml")))))

(defun sysml-clear-validator-cache ()
  "Clear the cached validator command location for the current buffer.
Useful if the validator command has been moved or added."
  (interactive)
  (setq sysml--cached-validator nil)
  (message "Validator cache cleared. Next validation will search for the command."))

(defun sysml-validate-buffer ()
  "Validate the current SysML buffer using validate-sysml.
The validator can validate either a single file or a directory.
Prompts to save the buffer if it has unsaved changes."
  (interactive)
  (unless buffer-file-name
    (error "Buffer must be visiting a file to validate"))
  (when (buffer-modified-p)
    (when (y-or-n-p (format "Save %s before validation? " (buffer-name)))
      (save-buffer)))
  (let ((validator-command (sysml-find-validator)))
    (cond
     ((not validator-command)
      (message "SysML validator command not found. Set sysml-validator-script or ensure validate-sysml is in project or PATH."))
     ((not (file-exists-p validator-command))
      (error "Validator command does not exist: %s" validator-command))
     ((not (file-executable-p validator-command))
      (error "Validator command is not executable: %s" validator-command))
     (t
      (let* ((model-file (expand-file-name buffer-file-name))
             (default-directory (file-name-directory model-file)))
        (compile (mapconcat #'shell-quote-argument
                            (list validator-command model-file)
                            " ")))))))

(defun sysml-validate-on-save ()
  "Validate SysML file on save if enabled."
  (when (and sysml-validate-on-save
             buffer-file-name
             (derived-mode-p 'sysml-mode))
    (sysml-validate-buffer)))

;; Project-wide symbol search

(defconst sysml--project-source-file-regexp "\\.sysml\\'"
  "Regexp matching SysML source file names.")

(defun sysml--project-root (&optional project)
  "Return the current project root for PROJECT or `default-directory'."
  (file-name-as-directory
   (expand-file-name
    (or (and project
             (fboundp 'project-root)
             (project-root project))
        default-directory))))

(defun sysml--normalize-project-file (file root)
  "Return FILE as an absolute path, interpreting relative names under ROOT."
  (if (file-name-absolute-p file)
      file
    (expand-file-name file root)))

(defun sysml--project-source-files ()
  "Return SysML source files in the current project or directory tree."
  (let* ((project (and (fboundp 'project-current)
                       (project-current nil)))
         (root (sysml--project-root project))
         (project-files
          (when (and project (fboundp 'project-files))
            (condition-case nil
                (project-files project)
              (error nil))))
         (files (or project-files
                    (directory-files-recursively
                     root sysml--project-source-file-regexp))))
    (cl-remove-if-not
     (lambda (file)
       (and (string-match-p sysml--project-source-file-regexp file)
            (file-regular-p file)))
     (mapcar (lambda (file)
               (sysml--normalize-project-file file root))
             files))))

(defun sysml--search-files (regexp)
  "Return grep-style matches for REGEXP in SysML project files.
Each result is a list of FILE, LINE, and LINE-TEXT."
  (let ((matches nil))
    (dolist (file (sysml--project-source-files))
      (condition-case nil
          (with-temp-buffer
            (insert-file-contents file)
            (goto-char (point-min))
            (let ((last-line 0))
              (while (re-search-forward regexp nil t)
                (let ((line (line-number-at-pos (match-beginning 0))))
                  (unless (= line last-line)
                    (setq last-line line)
                    (push (list file
                                line
                                (buffer-substring-no-properties
                                 (line-beginning-position)
                                 (line-end-position)))
                          matches))))))
        (file-error nil)))
    (nreverse matches)))

(defun sysml--display-search-results (title regexp)
  "Display SysML project search results for TITLE matching REGEXP."
  (let ((matches (sysml--search-files regexp))
        (buffer (get-buffer-create "*SysML Search*"))
        (root (sysml--project-root)))
    (if (null matches)
        (message "No SysML matches for %s" title)
      (with-current-buffer buffer
        (let ((inhibit-read-only t))
          (erase-buffer)
          (setq default-directory root)
          (dolist (match matches)
            (pcase-let ((`(,file ,line ,text) match))
              (insert (format "%s:%d:%s\n" file line text)))))
        (grep-mode)
        (setq-local compilation-directory root))
      (display-buffer buffer))))

(defun sysml--definition-search-regexp (symbol)
  "Return a regexp that matches a declaration of SYMBOL."
  (sysml--any-definition-regexp symbol))

(defun sysml-find-definition-at-point ()
  "Find the definition of the symbol at point across the project.
Searches for package, def, or usage declarations of the symbol."
  (interactive)
  (let ((symbol (thing-at-point 'symbol t)))
    (unless symbol
      (error "No symbol at point"))
    (sysml--display-search-results
     (format "definitions of %s" symbol)
     (sysml--definition-search-regexp symbol))))

(defun sysml-find-references ()
  "Find all references to the symbol at point across the project.
Searches for any usage of the symbol name."
  (interactive)
  (let ((symbol (thing-at-point 'symbol t)))
    (unless symbol
      (error "No symbol at point"))
    (sysml--display-search-results
     (format "references to %s" symbol)
     (concat "\\_<" (regexp-quote symbol) "\\_>"))))

(defun sysml-list-definitions ()
  "List all definitions in the current project.
Shows packages, parts, attributes, and other definition types."
  (interactive)
  (sysml--display-search-results
   "definitions"
   (sysml--any-definition-regexp)))

;; Completion support

(defun sysml-build-keyword-list ()
  "Build the complete list of SysML keywords for completion."
  (append
   ;; All keyword categories
   '("package" "import" "private" "public" "standard" "library"
     "def" "part" "attribute" "item" "port" "action" "state"
     "requirement" "constraint" "connection" "interface" "allocation"
     "verification" "use" "case" "calc" "analysis" "concern"
     "viewpoint" "view" "metadata" "individual" "snapshot" "timeslice"
     "enum" "variation" "variant" "stream" "message" "flow"
     "succession" "feature" "abstract" "readonly" "derived" "end"
     "binding" "exhibit" "perform" "then" "first" "accept" "send" "via"
     "entry" "exit" "do" "transition" "if" "else" "while" "for"
     "return" "assert" "assume" "require" "fork" "join" "merge"
     "decide" "loop" "parallel" "when" "at" "new"
     "specializes" "subsets" "redefines" "references" "conjugate"
     "chains" "inverse" "composite" "bind" "connect" "satisfy" "verify"
     "subject" "objective" "from" "to" "about" "actor" "stakeholder"
     "in" "out" "inout" "and" "or" "xor" "not" "implies"
     "doc" "comment" "language" "inv" "pre" "post" "body" "result"
     "filter" "rendering" "expose" "ref" "all" "that" "self"
     "featured" "by" "typing" "chaining" "inverting" "owned"
     "member" "multiplicity" "ordered" "nonunique" "sequence"
     "alias" "occurrence" "meta" "true" "false")))

(defconst sysml-all-keywords (sysml-build-keyword-list)
  "List of all SysML keywords for completion.")

(defun sysml-collect-buffer-symbols ()
  "Collect all defined symbols in the current buffer for completion."
  (let ((symbols '()))
    (save-excursion
      (goto-char (point-min))
      ;; Find all definitions
      (while (re-search-forward (sysml--any-definition-regexp) nil t)
        (let ((match (match-string 2)))
          (when (and match (not (member match symbols)))
            (push match symbols))))
      (goto-char (point-min))
      ;; Find local typed usages, which are also useful completion symbols.
      (while (re-search-forward
              (concat "\\_<\\(?:" (regexp-opt sysml--usage-keywords)
                      "\\)\\s-+\\(" sysml--identifier-regexp "\\)\\s-*:")
              nil t)
        (let ((match (match-string 1)))
          (when (and match (not (member match symbols)))
            (push match symbols)))))
    symbols))

(defun sysml-completion-at-point ()
  "Completion function for `completion-at-point-functions'.
Provides completion for SysML keywords and defined symbols."
  (let ((bounds (bounds-of-thing-at-point 'symbol)))
    (when bounds
      (let* ((start (car bounds))
             (end (cdr bounds))
             (prefix (buffer-substring-no-properties start end))
             ;; Combine keywords and buffer symbols
             (candidates (append sysml-all-keywords
                                (sysml-collect-buffer-symbols))))
        (list start end
              (completion-table-dynamic
               (lambda (_)
                 (cl-remove-if-not
                  (lambda (c) (string-prefix-p prefix c t))
                  candidates)))
              :exclusive 'no
              :annotation-function
              (lambda (s)
                (cond
                 ((member s sysml-all-keywords) " <keyword>")
                 (t " <symbol>"))))))))

;; Documentation support

(defconst sysml-eldoc-keywords
  '(;; Operators
    (":>" . "specialization operator - inherit from supertype")
    (":>>" . "redefinition operator - redefine inherited feature")
    ("::" . "qualified name separator - Package::Type")

    ;; Package keywords
    ("package" . "declares a namespace container")
    ("import" . "imports elements from another package")
    ("private" . "visibility modifier - accessible only within package")
    ("public" . "visibility modifier - accessible from outside package")
    ("standard" . "marks as part of standard library")
    ("library" . "marks as library package")

    ;; Definition keywords
    ("part" . "structural element definition or usage")
    ("def" . "defines a type")
    ("attribute" . "data value definition or usage")
    ("ref" . "reference to another element (not composition)")
    ("abstract" . "cannot be instantiated directly")

    ;; Documentation
    ("doc" . "documentation comment block")

    ;; Common patterns
    ("part def" . "defines a structural component type (specializes Parts::Part)")
    ("attribute def" . "defines a data type (specializes Base::DataValue)")
    ("ref system" . "reference to external system (not composition)"))
  "ElDoc documentation for SysML keywords and operators.")

(defun sysml--operator-at-point ()
  "Return the SysML operator at point, or nil."
  (let ((position (point)))
    (cl-some
     (lambda (operator)
       (let ((start (max (point-min) (- position (length operator))))
             (end (min (point-max) (+ position (length operator)))))
         (save-excursion
           (goto-char start)
           (catch 'found
             (while (search-forward operator end t)
               (when (and (<= (match-beginning 0) position)
                          (<= position (match-end 0)))
                 (throw 'found operator)))))))
     '(":>>" ":>" "::"))))

(defun sysml-eldoc-function ()
  "Provide ElDoc documentation for SysML keywords at point."
  (let* ((word (thing-at-point 'symbol t))
         (operator (sysml--operator-at-point))
         (preceding (save-excursion
                     (ignore-errors
                       (backward-word)
                       (thing-at-point 'symbol t))))
         (two-word (when (and preceding word)
                     (concat preceding " " word)))
         (doc nil))
    ;; Try different lookups
    (setq doc (or
               ;; Try two-word combination
               (and two-word (cdr (assoc two-word sysml-eldoc-keywords)))
               ;; Try word
               (and word (cdr (assoc word sysml-eldoc-keywords)))
               ;; Try operator
               (and operator
                    (cdr (assoc operator sysml-eldoc-keywords)))))
    doc))

;; Spell checking support
(defun sysml-flyspell-verify ()
  "Function to verify if flyspell should check word at point.
Only spell-check strings and comments, not code."
  (let ((face (get-text-property (point) 'face)))
    (or
     ;; Check strings
     (eq face 'font-lock-string-face)
     ;; Check comments
     (eq face 'font-lock-comment-face)
     ;; Check documentation blocks
     (eq face 'font-lock-doc-face)
     ;; Also handle when face is a list
     (and (listp face)
          (or (memq 'font-lock-string-face face)
              (memq 'font-lock-comment-face face)
              (memq 'font-lock-doc-face face))))))

(defun sysml-spell-check-buffer ()
  "Spell check comments and strings in SysML buffer.
This is a SysML-specific wrapper around `ispell-comments-and-strings'
that ensures only comments and strings are checked, not code."
  (interactive)
  (cond
   ;; First check if spell checking executables are available
   ((not (or (executable-find "ispell")
             (executable-find "aspell")
             (executable-find "hunspell")))
    (error "No spell checker found.  Please install ispell, aspell, or hunspell"))
   ;; Then check if the Emacs ispell library is loaded
   ((not (fboundp 'ispell-comments-and-strings))
    (error "Ispell.el is not loaded.  Try M-x load-library RET ispell RET"))
   ;; All checks passed, proceed with spell checking
   (t
    (condition-case err
        (ispell-comments-and-strings)
      (error
       (error "Spell checking failed: %s" (error-message-string err)))))))

;; Template insertion skeletons
;; Following html-mode convention with C-c C-c prefix for structured insertions

;; Helper functions for context-aware templates
(defun sysml-guess-package-name ()
  "Guess package name from directory structure or file name.
Returns the directory name if it starts with a capital letter,
otherwise returns the base name of the current file."
  (when buffer-file-name
    (let* ((dir-name (file-name-nondirectory
                     (directory-file-name
                      (file-name-directory buffer-file-name))))
           (file-base (file-name-sans-extension
                      (file-name-nondirectory buffer-file-name))))
      (cond
       ;; Use directory name if it looks like a package name
       ((and dir-name (string-match "^[A-Z]" dir-name))
        dir-name)
       ;; Otherwise use file name
       ((and file-base (string-match "^[A-Z]" file-base))
        file-base)
       ;; Default to nil to prompt user
       (t nil)))))

(defun sysml-capitalize-first (str)
  "Capitalize the first letter of STR."
  (if (and str (> (length str) 0))
      (concat (upcase (substring str 0 1))
              (substring str 1))
    str))

(define-skeleton sysml-insert-package
  "Insert a SysML package declaration with smart defaults."
  (or (sysml-guess-package-name) "Package name: ")
  "package " str " {" \n
  > _ \n
  "}" >)

(define-skeleton sysml-insert-part-def
  "Insert a SysML part definition."
  "Part name: "
  "part def " str " {" \n
  > _ \n
  "}" >)

(define-skeleton sysml-insert-attribute-def
  "Insert a SysML attribute definition with common base types."
  "Attribute name: "
  "attribute def " str " :> "
  (completing-read "Base type: "
                   '("String" "Real" "Integer" "Boolean" "Natural"
                     "Time::DateTime" "ISQ::length" "ISQ::mass"
                     "ISQ::time" "ISQ::temperature" "ISQ::speed"
                     "ISQ::acceleration" "ISQ::power" "ISQ::energy")
                   nil nil "String") ";")

(define-skeleton sysml-insert-item-def
  "Insert a SysML item definition."
  "Item name: "
  "item def " str " {" \n
  > _ \n
  "}" >)

(define-skeleton sysml-insert-port-def
  "Insert a SysML port definition."
  "Port name: "
  "port def " str " {" \n
  > _ \n
  "}" >)

(define-skeleton sysml-insert-action-def
  "Insert a SysML action definition."
  "Action name: "
  "action def " str " {" \n
  > _ \n
  "}" >)

(define-skeleton sysml-insert-state-def
  "Insert a SysML state definition."
  "State name: "
  "state def " str " {" \n
  > _ \n
  "}" >)

(define-skeleton sysml-insert-requirement-def
  "Insert a SysML requirement definition with documentation."
  "Requirement name: "
  "requirement def " str " {" \n
  > "doc /* " (read-string "Description: ") " */" \n
  > "attribute id : String = \""
  (read-string "Requirement ID (e.g., REQ-001): "
               (format "REQ-%03d" (random 1000))) "\";" \n
  > "attribute text : String = \"" (read-string "Requirement text: ") "\";" \n
  > _ \n
  "}" >)

(define-skeleton sysml-insert-constraint-def
  "Insert a SysML constraint definition."
  "Constraint name: "
  "constraint def " str " {" \n
  > _ \n
  "}" >)

(define-skeleton sysml-insert-connection-def
  "Insert a SysML connection definition."
  "Connection name: "
  "connection def " str " {" \n
  > "end source : " (skeleton-read "Source type: ") ";" \n
  > "end target : " (skeleton-read "Target type: ") ";" \n
  > _ \n
  "}" >)

(define-skeleton sysml-insert-interface-def
  "Insert a SysML interface definition."
  "Interface name: "
  "interface def " str " {" \n
  > "end p1 : " (skeleton-read "Port 1 type: ") ";" \n
  > "end p2 : " (skeleton-read "Port 2 type: ") ";" \n
  > _ \n
  "}" >)

(define-skeleton sysml-insert-allocation-def
  "Insert a SysML allocation definition."
  "Allocation name: "
  "allocation def " str " {" \n
  > "end source;" \n
  > "end target;" \n
  > _ \n
  "}" >)

(define-skeleton sysml-insert-verification-def
  "Insert a SysML verification definition."
  "Verification name: "
  "verification def " str " {" \n
  > _ \n
  "}" >)

(define-skeleton sysml-insert-use-case-def
  "Insert a SysML use case definition."
  "Use case name: "
  "use case def " str " {" \n
  > "doc /* " (skeleton-read "Description: ") " */" \n
  > _ \n
  "}" >)

(define-skeleton sysml-insert-calc-def
  "Insert a SysML calculation definition."
  "Calculation name: "
  "calc def " str " {" \n
  > _ \n
  "}" >)

(define-skeleton sysml-insert-analysis-def
  "Insert a SysML analysis definition."
  "Analysis name: "
  "analysis def " str " {" \n
  > _ \n
  "}" >)

(define-skeleton sysml-insert-concern-def
  "Insert a SysML concern definition."
  "Concern name: "
  "concern def " str " {" \n
  > _ \n
  "}" >)

(define-skeleton sysml-insert-viewpoint-def
  "Insert a SysML viewpoint definition."
  "Viewpoint name: "
  "viewpoint def " str " {" \n
  > _ \n
  "}" >)

(define-skeleton sysml-insert-view-def
  "Insert a SysML view definition."
  "View name: "
  "view def " str " {" \n
  > _ \n
  "}" >)

(define-skeleton sysml-insert-metadata-def
  "Insert a SysML metadata definition."
  "Metadata name: "
  "metadata def " str " {" \n
  > _ \n
  "}" >)

(define-skeleton sysml-insert-individual-def
  "Insert a SysML individual definition."
  "Individual name: "
  "individual def " str " :> "
  (skeleton-read "Type: ") ";")

(define-skeleton sysml-insert-enum-def
  "Insert a SysML enumeration definition."
  "Enum name: "
  "enum def " str " {" \n
  > (skeleton-read "First value: ") ";" \n
  > _ \n
  "}" >)

;; Instance templates (usages)
(define-skeleton sysml-insert-part-usage
  "Insert a SysML part usage (instance)."
  "Part name: "
  "part " str " : "
  (skeleton-read "Type: ") " {" \n
  > _ \n
  "}" >)

(define-skeleton sysml-insert-attribute-usage
  "Insert a SysML attribute usage (instance)."
  "Attribute name: "
  "attribute " str " : "
  (skeleton-read "Type: ") ";")

;; Mode map with template insertion keybindings
(defvar sysml-mode-map
  (let ((map (make-sparse-keymap))
        (prefix-map (make-sparse-keymap)))
    ;; Core commands
    (define-key map (kbd "C-c C-v") 'sysml-validate-buffer)
    (define-key map (kbd "C-c C-s") 'sysml-spell-check-buffer)

    ;; Search commands
    (define-key map (kbd "M-.") 'sysml-find-definition-at-point)
    (define-key map (kbd "M-?") 'sysml-find-references)
    (define-key map (kbd "C-c C-l") 'sysml-list-definitions)

    ;; Template insertion commands (C-c C-c prefix)
    ;; Package and structural definitions
    (define-key prefix-map (kbd "k") 'sysml-insert-package)
    (define-key prefix-map (kbd "p") 'sysml-insert-part-def)
    (define-key prefix-map (kbd "a") 'sysml-insert-attribute-def)
    (define-key prefix-map (kbd "i") 'sysml-insert-item-def)
    (define-key prefix-map (kbd "o") 'sysml-insert-port-def)

    ;; Behavioral definitions
    (define-key prefix-map (kbd "t") 'sysml-insert-action-def)
    (define-key prefix-map (kbd "s") 'sysml-insert-state-def)

    ;; Requirements and verification
    (define-key prefix-map (kbd "r") 'sysml-insert-requirement-def)
    (define-key prefix-map (kbd "n") 'sysml-insert-constraint-def)
    (define-key prefix-map (kbd "f") 'sysml-insert-verification-def)

    ;; Connections and interfaces
    (define-key prefix-map (kbd "c") 'sysml-insert-connection-def)
    (define-key prefix-map (kbd "x") 'sysml-insert-interface-def)
    (define-key prefix-map (kbd "l") 'sysml-insert-allocation-def)

    ;; Use cases and analysis
    (define-key prefix-map (kbd "u") 'sysml-insert-use-case-def)
    (define-key prefix-map (kbd "b") 'sysml-insert-calc-def)
    (define-key prefix-map (kbd "y") 'sysml-insert-analysis-def)

    ;; Views and viewpoints
    (define-key prefix-map (kbd "w") 'sysml-insert-viewpoint-def)
    (define-key prefix-map (kbd "v") 'sysml-insert-view-def)
    (define-key prefix-map (kbd "g") 'sysml-insert-concern-def)

    ;; Metadata and enumerations
    (define-key prefix-map (kbd "m") 'sysml-insert-metadata-def)
    (define-key prefix-map (kbd "e") 'sysml-insert-enum-def)
    (define-key prefix-map (kbd "d") 'sysml-insert-individual-def)

    ;; Usage templates (instances)
    (define-key prefix-map (kbd ".") 'sysml-insert-part-usage)
    (define-key prefix-map (kbd ",") 'sysml-insert-attribute-usage)

    ;; Bind the prefix map
    (define-key map (kbd "C-c C-c") prefix-map)
    map)
  "Keymap for SysML mode.")

;; Deferred per-buffer initialization
(defvar sysml-mode-initialized nil
  "Whether deferred features have been initialized for this mode.")

(defun sysml-mode-lazy-init (buffer)
  "Initialize deferred features for BUFFER on the next idle time.
BUFFER must be a live SysML buffer."
  (when (buffer-live-p buffer)
    (with-current-buffer buffer
      (when (and (derived-mode-p 'sysml-mode)
                 (not sysml-mode-initialized))
        ;; Enable hideshow if available.
        (when (and (fboundp 'hs-minor-mode)
                   (not (bound-and-true-p hs-minor-mode)))
          (hs-minor-mode 1))
        ;; Setup prettify symbols if enabled.
        (when (and sysml-enable-prettify-symbols
                   (fboundp 'prettify-symbols-mode))
          (setq-local prettify-symbols-alist sysml-prettify-symbols-alist)
          (prettify-symbols-mode 1))
        (setq-local sysml-mode-initialized t)))))

;; Mode definition
;;;###autoload
(define-derived-mode sysml-mode prog-mode "SysML"
  "Major mode for editing SysML v2 files.

Validation uses `sysml-validator-script' when set.  Otherwise, the mode
looks for `validate-sysml' from the current file's directory upward and
then on the variable `exec-path'.

Use `sysml-spell-check-buffer' to check comments and strings with
`ispell-comments-and-strings'.  Enable `flyspell-prog-mode' for
on-the-fly checking.

\\{sysml-mode-map}"
  :syntax-table sysml-mode-syntax-table

  ;; Set up font-lock
  (setq-local font-lock-defaults '(sysml-font-lock-keywords))

  ;; Comments - support both // and /* */ styles like C
  (setq-local comment-start "/* ")
  (setq-local comment-end " */")
  (setq-local comment-start-skip "\\(?://+\\|/\\*+\\)\\s *")
  (setq-local comment-end-skip "\\s *\\(\\*+/\\)?")
  (setq-local comment-multi-line t)

  ;; Set up comment continuation for proper fill-paragraph behavior
  (setq-local comment-continue " * ")
  (setq-local comment-style 'extra-line)

  ;; Configure adaptive fill to handle comment prefixes properly
  (setq-local adaptive-fill-mode t)
  (setq-local adaptive-fill-regexp "[ \t]*\\(?:\\(?:/\\*+\\|\\*+\\)[ \t]*\\|//+[ \t]*\\)?")
  (setq-local adaptive-fill-first-line-regexp adaptive-fill-regexp)

  ;; Use our fill-paragraph function that only acts in comments
  (setq-local fill-paragraph-function #'sysml-fill-paragraph)

  ;; Indentation
  (setq-local indent-line-function #'sysml-indent-line)
  (setq-local tab-width 4)

  ;; ElDoc support for inline documentation
  (setq-local eldoc-documentation-function #'sysml-eldoc-function)
  (eldoc-mode 1)

  ;; Completion support
  (setq-local completion-at-point-functions
              (cons #'sysml-completion-at-point
                    completion-at-point-functions))

  ;; Imenu support for code navigation
  (setq-local imenu-generic-expression sysml-imenu-generic-expression)
  (setq-local imenu-case-fold-search nil)  ; Case-sensitive for SysML

  ;; Which-function support (shows current definition in mode line)
  (setq-local which-func-functions (list #'sysml-which-function))

  ;; Electric pairs - automatically insert matching delimiters
  (setq-local electric-pair-pairs sysml-mode-electric-pairs)
  (setq-local electric-pair-skip-whitespace 'chomp)
  (electric-pair-local-mode 1)

  ;; Code folding with hideshow.
  (setq-local hs-block-start-regexp "{")
  (setq-local hs-block-end-regexp "}")
  (setq-local hs-forward-sexp-func #'forward-sexp)
  (setq-local hs-special-modes-alist
              `((sysml-mode
                 "{"     ; block start
                 "}"     ; block end
                 "/[*/]" ; comment start
                 ,#'forward-sexp
                 nil)))  ; no adjust-block-beginning function

  ;; Schedule deferred per-buffer initialization.
  (run-with-idle-timer 0.1 nil #'sysml-mode-lazy-init (current-buffer))

  ;; Spell checking configuration for flyspell-prog-mode. Batch spell
  ;; checking is provided by sysml-spell-check-buffer.
  (setq-local flyspell-generic-check-word-predicate #'sysml-flyspell-verify)

  ;; Validation on save
  (add-hook 'after-save-hook #'sysml-validate-on-save nil t))

;; Auto-load for .sysml files
;;;###autoload
(add-to-list 'auto-mode-alist '("\\.sysml\\'" . sysml-mode))

(provide 'sysml-mode)

;;; sysml-mode.el ends here
