;;; init.el --- Emacs configuration  -*- lexical-binding: t; -*-

;; Copyright (C) 2023-2069  Thanos Apollo

;; Author: Thanos Apollo <public@thanosapollo.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 <https://www.gnu.org/licenses/>.

;;; Commentary:
;; ╭━━━━┳╮╱╱╱╱╱╱╱╱╱╱╱╱╱╱╭━━━╮╱╱╱╱╱╭╮╭╮╱╱╱╱╱╱╱╱╭━━━╮
;; ┃╭╮╭╮┃┃╱╱╱╱╱╱╱╱╱╱╱╱╱╱┃╭━╮┃╱╱╱╱╱┃┃┃┃╱╱╱╱╱╱╱╱┃╭━━╯
;; ╰╯┃┃╰┫╰━┳━━┳━╮╭━━┳━━╮┃┃╱┃┣━━┳━━┫┃┃┃╭━━╮╱╱╱╱┃╰━━┳╮╭┳━━┳━━┳━━╮
;; ╱╱┃┃╱┃╭╮┃╭╮┃╭╮┫╭╮┃━━┫┃╰━╯┃╭╮┃╭╮┃┃┃┃┃╭╮┃╭━━╮┃╭━━┫╰╯┃╭╮┃╭━┫━━┫
;; ╱╱┃┃╱┃┃┃┃╭╮┃┃┃┃╰╯┣━━┃┃╭━╮┃╰╯┃╰╯┃╰┫╰┫╰╯┃╰━━╯┃╰━━┫┃┃┃╭╮┃╰━╋━━┃
;; ╱╱╰╯╱╰╯╰┻╯╰┻╯╰┻━━┻━━╯╰╯╱╰┫╭━┻━━┻━┻━┻━━╯╱╱╱╱╰━━━┻┻┻┻╯╰┻━━┻━━╯
;; ╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱┃┃
;; ╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╰╯
;;; Code:

(package-initialize)

(setf user-full-name "Thanos Apollo"
      user-mail-address "public@thanosapollo.org")

(setf copyright-names-regexp
      (format "%s <%s>" user-full-name user-mail-address))

(defvar is-constantine (string= (system-name) "constantine"))
(defvar is-hermes (string= (system-name) "hermes"))
(defvar is-phone (string= (system-name) "localhost"))
(defvar is-uranus (string= (system-name) "uranus"))

;; Font
(custom-set-faces
 (if is-hermes '(default ((t (:inherit nil :height 130 :family "Fira Mono"))))
   '(default ((t (:inherit nil :height 160 :family "Fira Mono")))))
 '(org-modern-symbol ((t (:inherit t :family "Iosevka"))))
 '(italic ((t (:inherit t :family "Iosevka")))))

;; Autoinsert
(auto-insert-mode 1)

(setf auto-insert-alist '((python-mode . "python.template"))
      auto-insert-directory (locate-user-emacs-file "insert"))


;; Set and load custom.el
(setf custom-file (locate-user-emacs-file "custom.el"))
(load custom-file 'noerror)

;; Imenu
;; Enable use-package support for imenu
;; NOTE: Load this before use-package!
(setf use-package-enable-imenu-support t)

(require 'imenu)
(setf imenu-auto-rescan t
      imenu-flatten 'prefix)

(global-set-key (kbd "C-c m") 'imenu)


;;;; Completions & Minibuffer

(use-package completion-preview
  :ensure t
  :config
  (global-completion-preview-mode)
  ;; Enable completion-preview in org-mode
  (push 'org-self-insert-command completion-preview-commands))

(use-package icomplete
  :ensure t
  :config
  (setf completion-styles '(basic flex)
	completion-auto-select t ;; Show completion on first call
	completion-auto-help 'visible ;; Display *Completions* upon first request
	completions-format 'one-column ;; Use only one column
	completions-sort 'historical ;; Order based on minibuffer history
	completions-max-height 20 ;; Limit completions to 15
	completion-ignore-case t)
  ;; Minibuffer completions
  (fido-vertical-mode 1)
  :bind (:map icomplete-fido-mode-map
	      ("TAB" . 'icomplete-force-complete)))

;; Search
(setf search-default-mode #'char-fold-to-regexp) ;; make it easier to search Greek chars

;; Registers & Misc nav
(setf register-use-preview t)

(global-set-key (kbd "C-x r d") 'bookmark-delete)
(global-set-key (kbd "C-x C-b") 'switch-to-prev-buffer)
(global-set-key (kbd "C-x M-b") 'ibuffer)

(defun switch-to-window-with-buffer (buffer-name)
  "Switch to a window displaying a buffer named BUFFER-NAME, if it exists."
  (let ((target-window nil))
    (walk-windows (lambda (w)
                    (when (string= (buffer-name (window-buffer w)) buffer-name)
                      (setq target-window w)))
                  nil t)
    (if target-window
        (select-window target-window)
      (message "No window is displaying buffer '%s'" buffer-name))))

(defun thanos/occur (regexp &optional nlines region)
  "Run `occur' and switch to *Occur* buffer."
  (interactive (nconc (occur-read-primary-args)
		      (and (use-region-p) (list (region-bounds)))))
  (let* ((start (and (caar region) (max (caar region) (point-min))))
         (end (and (cdar region) (min (cdar region) (point-max))))
         (in-region (or start end))
         (bufs (if (not in-region) (list (current-buffer))
                 (let ((ol (make-overlay
                            (or start (point-min))
                            (or end (point-max)))))
                   (overlay-put ol 'occur--orig-point (point))
                   (list ol)))))
    (occur-1 regexp nlines bufs))
  (switch-to-window-with-buffer "*Occur*")
  (forward-line))

(defun thanos/close-window (buffer-name)
  "Go to occurrence and quit the *Occur* window."
  (let ((occur-window (get-buffer-window buffer-name)))
    (when occur-window
      (quit-window nil occur-window))))

(defun thanos/occur-mode-goto-occurrence-close-occur-window ()
  "Go to occurrence and quit the *Occur* window."
  (interactive)
  (occur-mode-goto-occurrence)
  (thanos/close-window "*Occur*"))

(defun thanos/compile-goto-error ()
  "Go to compile error & close *grep* buffer."
  (interactive (list last-input-event))
  (compile-goto-error)
  (thanos/close-window "*grep*"))

(require 'grep)
(define-key occur-mode-map (kbd "RET")
	    'thanos/occur-mode-goto-occurrence-close-occur-window)

(define-key grep-mode-map (kbd "C-o") 'thanos/compile-goto-error)

(defvar-keymap thanos/search-map
  "g" #'grep-find
  "l" #'lgrep
  "f" #'find-name-dired
  "o" #'thanos/occur
  "C-o" #'occur)

(global-set-key (kbd "C-c s") thanos/search-map)

(setf browse-url-browser-function 'browse-url-generic
      browse-url-generic-program "icecat"
      backup-directory-alist '((".*" . "~/.Trash"))
      sentence-end-double-space t
      default-input-method "bulgarian-phonetic"
      gc-cons-threshold 5000000
      disabled-command-function nil ;; Enable all commands
      url-privacy-level 'high)

(setf calendar-date-style 'european)

(defun thanos/add-custom-keywords ()
  "Add custom warning keywords."
  (font-lock-add-keywords
   nil
   '(("\\<\\(FIXME\\):" 1 font-lock-warning-face t)
     ("\\<\\(TODO\\):" 1 font-lock-warning-face t)
     ("\\<\\(NOTE\\):" 1 font-lock-warning-face t))))

(setf tab-always-indent 'icomplete-force-complete)

(use-package flyspell
  :ensure t
  :config
  (setf ispell-alternate-dictionary
	(expand-file-name "~/.hunspell.d/en_med.dic")))

(use-package org
  :ensure t
  :config
  (setf org-directory "~/org/"
	org-agenda-files '("~/org/seminars.org" "~/org/lectures.org")
	org-default-notes-file (expand-file-name "notes.org" org-directory)
	org-ellipsis " ▼"
	org-log-done 'time
	org-hide-emphasis-markers nil ;;change to t to hide emphasis markers
	org-table-convert-region-max-lines 20000
	org-log-done 'time
	org-todo-keywords '((sequence "TODO(t)" "SEMINAR(s)" "LECTURE(l)" "DONE(d)")))

  (setf org-imenu-depth 3)

  (setf org-structure-template-alist
	'(("e" . "src emacs-lisp")
	  ("p" . "src python")
	  ("l" . "src lisp")
	  ("b" . "src bash")
	  ("q" . "QUOTE")))

  (require 'ox-latex)
  (add-to-list 'org-latex-classes
               '("article"
		 "\\documentclass[11pt,a4paper]{article}
\\usepackage[utf8]{inputenc}
\\usepackage[T1]{fontenc}
\\usepackage{fixltx2e}
\\usepackage{graphicx}
\\usepackage{longtable}
\\usepackage{float}
\\usepackage{wrapfig}
\\usepackage{rotating}
\\usepackage[normalem]{ulem}
\\usepackage{amsmath}
\\usepackage{textcomp}
\\usepackage{marvosym}
\\usepackage{wasysym}
\\usepackage{amssymb}
\\usepackage{hyperref}
\\usepackage{mathpazo}
\\usepackage{color}
\\usepackage{enumerate}
\\definecolor{bg}{rgb}{0.95,0.95,0.95}
\\tolerance=1000
      [NO-DEFAULT-PACKAGES]
      [PACKAGES]
      [EXTRA]
\\linespread{1.1}
\\hypersetup{pdfborder=0 0 0}"
		 ("\\section{%s}" . "\\section*{%s}")
		 ("\\subsection{%s}" . "\\subsection*{%s}")
		 ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
		 ("\\paragraph{%s}" . "\\paragraph*{%s}")))

  (push 'org-self-insert-command completion-preview-commands)
  :hook ((org-mode . (lambda () (display-line-numbers-mode -1) (flyspell-mode))))
  :bind (:map org-mode-map (("C-c l" . org-store-link)
			    ("C-c M-t" . org-todo)
			    ("C-c RET" . org-table-hline-and-move)
			    ("C-M-i" . completion-at-point))))

(use-package org-present
  :hook ((org-present-mode . (lambda ()
			       (org-present-big)
			       (org-display-inline-images)
			       (org-present-hide-cursor)
			       (org-present-read-only)))
	 (org-present-mode-quit . (lambda ()
				    (org-present-small)
				    (org-remove-inline-images)
				    (org-present-show-cursor)
				    (org-present-read-write)))))
;; (completion-styles-alist )
;; Export

(defun thanos/org-html-remove-curly-braces (text backend info)
  "Remove curly braces for superscripts and subscripts in HTML export."
  (when (org-export-derived-backend-p backend 'html 'latex)
    (replace-regexp-in-string
     "{\\([^}]*?\\)}" "\\1" text)))

(use-package emacs
  :ensure nil
  :config
  (if is-constantine
      (display-battery-mode 0)
    (display-battery-mode 1))

  ;; use ssh for tramp, faster for small files
  (setf tramp-default-method "ssh")

  (defun thanos/dired-gnosis-rename (&optional file)
    (interactive)
    (let ((file (or file (dired-get-filename)))
	  (name (read-string "File name: ")))
      (dired-rename-file file (expand-file-name name gnosis-images-dir) nil)))

  (savehist-mode)
  (save-place-mode 1)
  (recentf-mode 1)
  (electric-pair-mode 1)
  :bind (("M-<backspace>" . 'backward-kill-sexp)
	 ("C-c L" . 'display-line-numbers-mode)
	 ("C-z" . nil)
	 ("C-c r" . 'rename-visited-file)
	 ("C-x 7" . 'window-swap-states)
	 ("C-c t" . 'create-text-scratch))
  :hook ((emacs-lisp-mode . prettify-symbols-mode)
	 (emacs-lisp-mode . thanos/add-custom-keywords)
	 (lisp-mode . prettify-symbols-mode)
	 (lisp-mode . thanos/add-custom-keywords)
	 (scheme-mode . prettify-symbols-mode)
	 (scheme-mode . thanos/add-custom-keywords)))

(use-package dired
  :ensure nil
  :config
  (defun dired-watch-video ()
    "Watch play file with mpv."
    (interactive)
    (call-process-shell-command
     (format "mpv \"%s\"" (dired-get-filename)) nil 0))

  (defun dired-set-wallpaper ()
    "Set NAME as wallpaper using feh."
    (interactive)
    (thanos/wallpaper-set (dired-get-filename)))

  (defun dired-delete-files-except ()
    "Delete all files inside directory except match."
    (interactive)
    (let* ((directory (read-directory-name "Select directory: "))
	   (files (directory-files directory t))
	   (except-match (read-string "Except the ones that have: ")))
      (dolist (file files)
	(unless (or (string= "." (substring file -1))
		    (string= ".." (substring file -2))
		    (string-match except-match file))
	  (dired-delete-file file t)))))

  (defun dired-delete-files-match (&optional directory match)
    "Delete all files inside directory except match."
    (interactive)
    (let* ((directory (or directory (read-directory-name "Select directory: ")))
	   (files (directory-files directory t))
	   (match (or match (read-string "Delete files that match: "))))
      (dolist (file files)
	(when (string-match-p match file)
	  (dired-delete-file file t)))))

  (defun dired-rename-capitalize-file ()
    "Capitalize the base name of the file at point in a Dired buffer."
    (interactive)
    (let* ((file (dired-get-file-for-visit))
	   (new-file (capitalize (file-name-nondirectory file))))
      (if (string-prefix-p "." file)
	  (message "Skipping file starting with '.'")
	(progn
	  (rename-file file (concat (file-name-directory file) new-file))
	  (revert-buffer)
	  (message "Renamed %s to %s" file new-file)))))
  :bind ((:map dired-mode-map
	  ("b" . 'dired-up-directory)
	  ("v" . 'dired-watch-video)
	  ("z" . 'wdired-change-to-wdired-mode)
	  (";" . 'dired-set-wallpaper)
	  ("C-c d" . 'dired-delete-files-except)
	  ("C-c r" . 'dired-do-query-replace-regexp))))

(defun theme-invisible-dividers (_theme)
  "Make window dividers for THEME invisible."
  (let ((bg (face-background 'default)))
    (custom-set-faces
     `(fringe ((t :background ,bg :foreground ,bg)))
     `(window-divider ((t :background ,bg :foreground ,bg)))
     `(window-divider-first-pixel ((t :background ,bg :foreground ,bg)))
     `(window-divider-last-pixel ((t :background ,bg :foreground ,bg))))))

(add-hook 'enable-theme-functions #'theme-invisible-dividers)

;;;; Theming ;;;;
(setf inhibit-startup-message t
      initial-scratch-message nil)

(blink-cursor-mode -1)
(global-visual-line-mode 0)

(setf visible-bell nil
      display-line-numbers-type 'relative)

(column-number-mode)
(global-display-line-numbers-mode 1)

;; Transparency
(add-to-list 'default-frame-alist '(alpha-background . 85))

;; Theming

(defun thanos/terminal-theming ()
  "Customize theming when laucning Emacs as TUI."
  (unless (display-graphic-p (selected-frame))
    (set-face-background 'default "unspecified-bg" (selected-frame))
    (global-hl-line-mode 0)))

(add-hook 'window-setup-hook 'thanos/terminal-theming)

(when (equal is-phone nil)
  (scroll-bar-mode -1)
  (set-fringe-mode 10))

(tool-bar-mode -1)
(tooltip-mode -1)
(menu-bar-mode -1)

(defun org-insert-book ()
  "Insert org-link from ~/Library for book."
  (interactive)
  (let* ((book-path (read-file-name "Book: " "~/Library/")))
    (org-insert-link nil book-path (file-name-base book-path))))

(use-package org-modern
  :ensure t
  :config
  (setf org-modern-table nil
	org-modern-todo nil
	org-modern-tag nil
	org-modern-timestamp nil
	org-modern-star 'replace
	org-modern-list '((?+ . "•")
			  (?- . "•"))
	org-modern-replace-stars "☧")
  :hook ((org-mode . org-modern-mode)))

;; Create notes directory for org-roam
(unless (file-exists-p "~/Notes")
  (make-directory "~/Notes"))

(unless (or is-phone is-uranus)
  (use-package modus-themes
    :ensure t
    :config
    (setf modus-themes-italic-constructs nil
	  modus-themes-bold-constructs nil
	  modus-themes-mixed-fonts nil
	  modus-themes-variable-pitch-ui nil
	  modus-themes-custom-auto-reload t
	  modus-themes-disable-other-themes t
	  modus-themes-prompts '(semibold italic)
	  modus-themes-completions '((matches . (underline))
				     (selection . (semibold text-also underline))))
    ;; Palette overrides
    (setf modus-vivendi-palette-overrides
	  '((fg-line-number-active cyan-intense)
	    ;; (bg-main "#1d2021") ;;grubox-hard
	    ;; (bg-main "#191919") ;; 1337
	    ;; (bg-main "#1d1f21") ;; tomorrow night
	    ;; (bg-main "#151515") ;; jazz
	    ;; (bg-main "#0C0C0C") ;; random black
	    ;; (bg-main "#1b1d1e") ;; Iosevka
	    ;; (bg-main "#0D0E16") ;; Acario
	    ;; (bg-main "#1c1e1f") ;; molokai
	    ;; (bg-main "#171717") ;; badger
	    (fg-heading-1 red)
            (bg-line-number-inactive unspecified)
            (bg-line-number-active unspecified)
	    (bg-paren-match bg-magenta-intense)
            (underline-paren-match fg-main)
	    (underline-err red-intense)
            (underline-warning yellow-faint)
            (underline-note cyan-faint)
	    (string olive)
	    (border-mode-line-active unspecified)
            (border-mode-line-inactive unspecified)
	    (bg-mode-line-active bg-lavender)
	    (cursor indigo)
	    (prose-todo green-intense)
	    (prose-done bg-term-white)
	    (fg-prompt yellow-faint)
	    ,@modus-themes-preset-overrides-intense))
    ;; Headings
    (setf modus-themes-headings
	  '((1 . (ultrabold  1.35))
            (2 . (semibold  1.2))
            (agenda-date . (1.2))
            (agenda-structure . (variable-pitch light 1.8))
            (t . (1.15))))
    ;; Load modus
    (load-theme 'modus-vivendi t)))

(use-package which-key
  :ensure t
  :config
  (which-key-mode 1))

;; Python
(add-to-list 'auto-mode-alist '("\\.py\\'" . python-ts-mode))

;; Lisp

(use-package sly
  :init (setf inferior-lisp-program "sbcl")
  :ensure t)

(use-package vc
  :ensure t
  :config
  (defun vc-git--commit-hash-at-point ()
    "Return commit hash at point."
    (let ((hash (log-view-current-entry nil t)))
      (cadr hash)))

  (defun vc-git-reset-to-commit-at-point (&optional hash)
    "Reset the current branch to the commit at point in the vc log buffer."
    (let ((commit-hash (or (thing-at-point 'word t) hash)))
      (vc-git--reset commit-hash)))

  (defun vc-git--reset (commit-hash)
    "Reset to commit HASH."
    (if (and commit-hash (string-match-p "^[0-9a-f]\\{7,40\\}$" commit-hash))
        (if (yes-or-no-p (format "Reset current branch to commit %s?" commit-hash))
            (progn
              (let ((default-directory (vc-root-dir)))
                (vc-git-command nil 0 nil "reset" "--hard" commit-hash)
                (message "Reset to commit %s completed." commit-hash)))
          (message "Reset cancelled."))
      "Invalid commit hash"))

  (defun vc-git--select-commit ()
    (let* ((history-add-new-input nil)
	   (commit-strings (split-string (shell-command-to-string "git log --oneline") "\n" t))
	   (selected (completing-read "Select commit: " commit-strings nil t))
	   (selected-hash (car (split-string selected))))
      selected-hash))

  (defun vc-git-reset (&optional hash)
    (interactive)
    (vc-git--reset (or hash
		       (vc-git--commit-hash-at-point)
		       (vc-git--select-commit))))

  (defun vc-git-format-patches (num)
    "Format patches for NUM of last commits"
    (interactive (list (read-number "Number of commits: ")))
    (vc-git-command nil 0 nil "format-patch" (format "-%d" num))
    (message "Done."))

  (defun vc-git-reword-commit ()
    "Edit current commit message"
    (interactive)
    (vc-checkin nil 'git nil nil nil "")
    (vc-git-log-edit-toggle-amend))

  (defun thanos/vc-git-branch ()
    "Select git branch"
    (let ((branch (completing-read "Select branch: " (vc-git-branches) nil t)))
      branch))

  (defun vc-git-delete-branch ()
    "Select & delete git branch."
    (interactive)
    (let ((branch (thanos/vc-git-branch)))
      (vc-git-command nil 0 nil "branch" "-D" branch)
      (message "Branch `%s' deleted." branch)))
  :bind (:map
	 vc-prefix-map
	 ("b d" . 'vc-git-delete-branch)))

(use-package magit
  :defer t
  :config
  (setf magit-display-buffer-function #'magit-display-buffer-same-window-except-diff-v1)
  :bind ("C-x g" . magit))

(when (or is-phone is-uranus)
  (use-package corfu-terminal))

(defun insert-brackets (&optional arg)
  "Insert ARG brackets."
  (interactive "P")
  (insert-pair arg ?\[ ?\]))

(global-set-key (kbd "C-x M-[") 'insert-brackets)


(use-package pdf-tools
  :ensure nil
  :config
  (add-to-list 'auto-mode-alist '("\\.pdf\\'" . pdf-view-mode))
  :hook
  ((pdf-view-mode . (lambda ()
		      (display-line-numbers-mode 0)
		      ;; Guix package emacs-pdf-tools does not enable
		      ;; minor modes automatically
		      (pdf-tools-enable-minor-modes)))))

(use-package nov
  :ensure nil
  :config
  (add-to-list 'auto-mode-alist '("\\.epub\\'" . nov-mode)))

(use-package flycheck
  :ensure t
  :config
  (setf flycheck-emacs-lisp-load-path 'inherit)
  (global-flycheck-mode))

(use-package eat
  :ensure t
  :config
  (setf eshell-visual-commands nil
	eat-term-name "xterm-256color")
  :bind (("C-c V" . eat))
  :hook ((eshell-mode . eat-eshell-mode)
	 (eshell-mode . eat-eshell-visual-command-mode)
	 (eat-mode . (lambda () (visual-line-mode -1)))))

(use-package shell
  :ensure t
  :config
  (defun thanos/shell (n)
    "Create or switch to a shell buffer."
    (interactive "P")
    (let* ((num (if n (prefix-numeric-value n) nil))
           (buf-name (if num
			 (format "*shell<%d>*" num)
                       "*shell*")))
      (shell buf-name)))
  :bind (("C-c v" . thanos/shell)
	 :map shell-mode-map
	 ("C-l" . 'comint-clear-buffer))
  :hook ((shell-mode . (lambda () (display-line-numbers-mode -1)))))

(defvar thanos/aliases
  '((ll . "ls -lah")
    (clear . clear-scrollback)))

(defun thanos/set-eshell-aliases (aliases)
  "Set ALIASES as eshell aliases."
  ;; Remove aliases file
  (when (and eshell-aliases-file
             (file-exists-p eshell-aliases-file))
    (delete-file eshell-aliases-file))
  (mapc (lambda (alias)
          (let ((name (symbol-name (car alias)))
                (command (cdr alias)))
            (eshell/alias name
                          (cond
                           ((stringp command) command)
                           ((symbolp command) (symbol-name command))
                           (t (error "Unsupported alias command type"))))))
        aliases))

(defun eshell/o (file)
  "Open FILE."
  (find-file file))

(defun thanos/eshell-clear ()
  "Interactive call for clear-scrollback."
  (interactive)
  (eshell/clear-scrollback))

(defun thanos/eshell-preview-insert ()
  (interactive)
  (completion-preview-insert)
  (delete-char -1))

(use-package esh-mode
  :ensure nil
  :config
  (defun eshell-git-info ()
  "Return a string with git info."
  (when (eq (call-process "git" nil nil nil "rev-parse" "--is-inside-work-tree") 0)
    (let* ((branch-raw (shell-command-to-string "git rev-parse --abbrev-ref HEAD"))
           (branch (if (or (string-match-p "^fatal" branch-raw)
                           (string-match-p "^error" branch-raw))
                       "Unknown"
                     (string-trim branch-raw)))
           (dirty (not (string= "" (string-trim (shell-command-to-string "git status --porcelain")))))
           (dirty-info (if dirty " ✎" " ✔")))
      (concat (propertize "⎇ " 'face 'modus-themes-fg-green-warmer)
              (propertize branch 'face 'modus-themes-fg-magenta-warmer)
              (propertize dirty-info 'face (if dirty 'modus-themes-fg-red 'modus-themes-fg-green))))))

  (defun eshell-prompt-multiline ()
    "Eshell Git prompt inspired by spaceship-prompt."
    (let ((separator (propertize " | " 'face 'font-lock-comment-face))
          (hr (propertize (concat "\n" (make-string (/ (window-total-width) 2) ?─) "\n") 'face 'font-lock-comment-face))
          (dir (propertize (format "%s" (abbreviate-file-name (eshell/pwd))) 'face 'modus-themes-fg-yellow-warmer))
          (git-info (eshell-git-info))
          (time (propertize (format-time-string "%H:%M:%S") 'face 'font-lock-comment-face))
          (sign (if (= (user-uid) 0)
                    (propertize "\n#" 'face 'modus-themes-fg-blue-intense)
                  (propertize "\nλ" 'face 'modus-themes-fg-red-warmer))))
      ;; Build prompt
      (concat hr dir separator git-info separator time sign " ")))

  (setf eshell-prompt-function 'eshell-prompt-multiline
	eshell-highlight-prompt nil)
  :bind (("C-c e" . eshell)
	 :map eshell-mode-map
	 ("C-l" . 'thanos/eshell-clear)
	 ("<tab>" . 'thanos/eshell-preview-insert)
	 ("C-M-i" . 'completion-at-point))
  :hook ((eshell-mode . (lambda ()
			  (thanos/set-eshell-aliases thanos/aliases)
			  (display-line-numbers-mode -1)
			  (eshell-cmpl-mode -1)))))

;; Password-store
(use-package password-store
  :init (define-prefix-command 'thanos/pass)
  :defer t
  :config
  (setf password-store-password-length (+ 20 (random 20)))
  :bind (("C-c p" . 'thanos/pass)
	 :map thanos/pass
	 ("i" . 'password-store-insert)
	 ("e" . 'password-store-edit)
	 ("g" . 'password-store-generate)
	 ("c" . 'password-store-copy)
	 ("s" . 'smtp-get-pass)))

(use-package erc
  :ensure t
  :config
  (unless (expand-file-name "erc" user-emacs-directory)
    (make-directory (expand-file-name "erc" user-emacs-directory)))
  (setf erc-modules
	'(netsplit fill button match track completion readonly
		   networks ring autojoin noncommands irccontrols move-to-prompt stamp
		   menu list log notifications)
	erc-log-channels-directory (expand-file-name "erc" user-emacs-directory))

  (defun thanos/erc-login ()
    "Login to libera.chat"
    (interactive)
    (erc :server "uranus" :port 5555
	 :nick "thanosapollo"
	 :user "thanosapollo"
	 :password (password-store-get "znc/admin")))
  :bind (("C-c E" . 'erc-libera)
	 :map erc-mode-map
	 ("C-c RET" . 'erc-cmd-QUERY)))

(use-package sudo-edit
  :ensure t
  :config
  (setf sudo-edit-local-method "sudo"))

;; My packages
(when (or is-constantine is-hermes)
  (use-package yeetube
    :init (define-prefix-command 'thanos/yeetube-map)
    :vc t
    :load-path "~/Dev/emacs-lisp/yeetube"
    :ensure t
    :config
    (setf yeetube-results-limit 20
	  yeetube-mpv-disable-video t
	  yeetube-display-thumbnails t
	  yeetube-play-function #'yeetube-mpv-play)

    (defun yeetube-download-videos-ffmpeg ()
      "Download videos using ffmpeg."
      (interactive)
      (let ((url "")
	    (name "")
	    (download-counter 1)
	    (stored-contents nil))
	;; Read links and names until "q" is entered
	(while (not (string= url "q"))
	  (setf url (read-string "Enter URL (q to quit): "))
	  (unless (string= url "q")
	    (setf name
		  (read-string (format "Custom name (download counter: %d) " download-counter)))
	    (push (cons url name) stored-contents)
	    (setf download-counter (1+ download-counter))))
	;; Process the collected links and names
	(dolist (pair stored-contents)
	  (let ((url (car pair))
		(name (cdr pair)))
	    (async-shell-command
	     (format
	      "ffmpeg -protocol_whitelist file,crypto,data,https,tls,tcp -stats -i '%s' -codec copy '%s.mp4'"
	      url name))))))

    (defun yeetube-download-vimeo-videos ()
      "Download videos from vimeo services."
      (interactive)
      (let ((url "")
	    (name "")
	    (download-counter 1))
	(while (not (string= url "q"))
	  (setf url (read-string "Enter URL (q to quit): "))
	  (unless (string= url "q")
	    (setf name
		  (read-string (format "Custom name (download counter: %d) " download-counter)))
	    (setf download-counter (1+ download-counter))
	    (call-process-shell-command
	     (format
	      "yt-dlp '%s' -o '%s'"
	      (replace-regexp-in-string "\\.json" ".m3u8" url) name)
	     nil 0)))))
    
    :bind (("C-c y" . 'thanos/yeetube-map)
	   :map thanos/yeetube-map
	   ("s" . 'yeetube-search)
	   ("b" . 'yeetube-play-saved-video)
	   ("d" . 'yeetube-download-videos)
	   ("C-d" . 'yeetube-download-vimeo-videos)
	   ("p" . 'yeetube-mpv-toggle-pause)
	   ("v" . 'yeetube-mpv-toggle-video)
	   ("V" . 'yeetube-mpv-toggle-no-video-flag)
	   ("C-p" . 'yeetube-mpv-toggle-video)
	   ("k" . 'yeetube-remove-saved-video)))

  (use-package gnosis
    :load-path "~/Dev/emacs-lisp/gnosis"
    :ensure t
    :init (define-prefix-command 'thanos/gnosis-map)
    :config
    (setf gnosis-vc-auto-push t
	  gnosis-mcq-display-choices nil
	  gnosis-image-width (and is-hermes 150)
	  gnosis-image-height (and is-hermes 150))
    (gnosis-modeline-mode)
    (setf gnosis-custom-values
	  '((:deck "Bulgarian" (:amnesia 0.55 :proto (0 1 3)))
	    (:deck "Unking" (:amnesia 0.45))
	    (:tag "vocabulary" (:amnesia 0.65 :proto (0 1 3)))))
    :bind (("C-c SPC" . 'thanos/gnosis-map)
	   :map thanos/gnosis-map
	   ("a" . 'gnosis-add-note)
	   ("d" . 'gnosis-dashboard)))

  ;; Sync gnosis on startup
  (gnosis-vc-pull)

  (use-package org-gnosis
    :load-path "~/Dev/emacs-lisp/org-gnosis"
    :init
    (define-prefix-command 'thanos/notes-map)
    (define-prefix-command 'thanos/journal-map)
    :config
    (setf org-gnosis-dir "~/Notes"
	  org-gnosis-journal-templates
	  '(("Default"
	     "* Daily Notes\n\n* Goals\n+ [] Πρωινὴ Προσευχή\n+ [] Ἑσπερινή Προσευχή\n\n* Extras"))
	  org-gnosis-show-tags t)

    (defun thanos/org-open-at-point ()
      "Open Org link at point in the same window, if possible."
      (interactive)
      (let ((org-link-frame-setup '((file . find-file))))
	(org-open-at-point)))
    
    :bind (("C-c n" . thanos/notes-map)
	   ("C-c j" . thanos/journal-map)
	   :map thanos/notes-map
           ("f" . org-gnosis-find)
	   ("i" . org-gnosis-insert)
	   ("t" . org-gnosis-find-by-tag)
	   :map thanos/journal-map
	   ("j" . org-gnosis-journal)
	   ("f" . org-gnosis-journal-find)
	   ("i" . org-gnosis-journal-insert)
	   :map org-mode-map
	   ("C-c C-." . org-gnosis-insert-tag)
	   ("C-c i" . org-id-get-create)
	   ("C-c C-o" . thanos/org-open-at-point)))

  (use-package org-gnosis-ui
    :load-path "~/Dev/emacs-lisp/org-gnosis-ui/"
    :after org-gnosis
    :config
    (setf org-gnosis-ui-custom-theme
	  '((bg . "#1b1d1e")
	    (bg-alt . "#282a36")
	    (fg . "#ffffff")
	    (fg-alt . "#c6daff")
	    (red . "#ff5f5f")
	    (orange . "#f1fa8c")
	    (yellow . "#efef00")
	    (green . "#44df44")
	    (cyan . "#00eff0")
	    (blue . "#338fff")
	    (violet . "#9099d9") ;; indigo
	    (magenta . "#ff66ff"))))

  (use-package pcmpl-tailscale
    :vc t
    :load-path "~/Dev/emacs-lisp/pcmpl-tailscale"
    :ensure nil)

  (use-package greek-polytonic
    :vc t
    :ensure t
    :load-path "~/Dev/emacs-lisp/greek-polytonic"))


(use-package elfeed
       :defer t
       :vc (:url "https://github.com/skeeto/elfeed")
       :config
       (setf elfeed-search-filter "@1-week-ago +unread"
	     browse-url-browser-function #'browse-url-default-browser
	     elfeed-db-directory "~/.config/elfeed")
       ;; Feeds
       (setf elfeed-feeds
	     '(("https://hackaday.com/blog/feed/"
		hackaday linux)
	       ("https://thanosapollo.org/index.xml"
		thanos)
	       ("http://wikileaks.org/feed"
		wikileaks)
	       ("https://hackernoon.com/feed"
		hackernoon)
	       ("https://torrentfreak.com/feed"
		torrentfreak piracy)
	       ("https://www.science.org/action/showFeed?type=etoc&feed=rss&jc=sciimmunol"
		science)
	       ("http://localhost/?action=display&bridge=CssSelectorBridge&home_page=https%3A%2F%2Fwww.medscape.com%2Findex%2Flist_13470_0&url_selector=a.title&url_pattern=viewarticle%2F.*&content_selector=div.article__main-content&content_cleanup=&title_cleanup=+-+Index&limit=&format=Atom"
		medscape med)
	       ("http://localhost/?action=display&bridge=CssSelectorBridge&home_page=https%3A%2F%2Fwww.tovima.gr%2Flatest-news%2F&url_selector=a.columns&url_pattern=&content_selector=div.main-content&content_cleanup=div.wrap-facebook%2Cdiv.googlenews&title_cleanup=&limit=&format=Atom"
		tovima greek news)
	       ("http://localhost/?action=display&bridge=CssSelectorBridge&home_page=https%3A%2F%2Fwww.estianews.gr%2Feidiseis-arthra%2F&url_selector=h3.entry-title&url_pattern=&content_selector=div.col-lg-8&content_cleanup=&title_cleanup=&limit=&format=Atom"
		estia greek news)
	       ("http://localhost/?action=display&bridge=CssSelectorBridge&home_page=https%3A%2F%2Fwww.estianews.gr%2Fkentriko-thema%2F&url_selector=h3.entry-title&url_pattern=&content_selector=div.col-md-8&content_cleanup=&title_cleanup=&limit=&format=Atom"
		estia greek kyrio)
	       ("http://localhost/?action=display&bridge=CssSelectorBridge&home_page=https%3A%2F%2Fwww.estianews.gr%2Fapopseis%2F&url_selector=h3.entry-title&url_pattern=&content_selector=div.col-lg-8&content_cleanup=&title_cleanup=&limit=&format=Atom"
		estia greek opinions)
	       ("http://localhost/?action=display&bridge=CssSelectorBridge&home_page=https%3A%2F%2Fwww.reuters.com%2Fbusiness%2Fhealthcare-pharmaceuticals%2F&url_selector=a.basic-card__title__37xHl&url_pattern=&content_selector=div.article-body__wrapper__3IxHM&content_cleanup=svg.link__new-tab-symbol__3T19s%2C+div.toolbar__container__3kIkw%2C+div.article-body__row__dFOPA+article-body__element__2p5pI&title_cleanup=&limit=&format=Atom"
		reuters med news)
	       ("http://localhost/?action=display&bridge=CssSelectorBridge&home_page=https%3A%2F%2Fwww.kathimerini.gr%2Ftag%2Fellinotoyrkika%2F&url_selector=div.media-content&url_pattern=&content_selector=div.p-3&content_cleanup=&title_cleanup=&limit=&format=Atom"
		kath greek tk)
	       ("http://localhost/?action=display&bridge=CssSelectorBridge&home_page=https%3A%2F%2Fwww.reuters.com%2Ftechnology%2Fcybersecurity%2F&url_selector=a.media-story-card__headline__tFMEu%2C+a.media-story-card__heading__eqhp9&url_pattern=&content_selector=article.article__container__2MUeZ&content_cleanup=div.info-content__toolbar__3AkHm%2C+svg.link__new-tab-symbol__3T19s%2C+div.article-body__row__dFOPA%2C+div.read-next-tablet-up__container__3MpHN%2C+div.author-bio__multiple-authors__5YGrG%2C+div.article__read-next__Kjxdw&title_cleanup=&limit=&format=Atom" reuters cybersec news)
	       ("https://annas-blog.org/rss.xml" anna piracy)
	       ("http://localhost/?action=display&bridge=CssSelectorBridge&home_page=https%3A%2F%2Fwww.ethnos.gr%2Ftag%2F842%2Fellhnotoyrkika&url_selector=a.single-title&url_pattern=&content_selector=div.content-section&content_cleanup=div.article-related-posts%2C+div.ReadMore%2C+script&title_cleanup=&limit=&format=Atom" greek ethnos tk)
	       ("https://planet.emacslife.com/atom.xml" emacs emacslife)
	       ("https://localmonero.co/static/rss/the-monero-standard/feed.xml" monero)
	       ("https://devonzuegel.com/feed" devon)
	       ("https://www.addtoany.com/add_to/feed?linkurl=http%3A%2F%2Fwww.thelancet.com%2Frssfeed%2Flancet_online.xml&type=feed&linkname=The%20Lancet%20Online%20First&linknote=" lancet med)
	       ("https://www.propublica.org/feeds/propublica/main" probublica news)
	       ("http://tools.cdc.gov/podcasts/feed.asp?feedid=183" cdc med)
	       ("http://planet.lisp.org/rss20.xml" lisp planetlisp)
	       ("https://guix.gnu.org/feeds/blog.atom" guix)
	       ("https://protesilaos.com/master.xml" prot)
	       ("https://static.fsf.org/fsforg/rss/news.xml" gnu fsf)
	       ;; YouTube
	       ("https://www.youtube.com/feeds/videos.xml?channel_id=UCVls1GmFKf6WlTraIb_IaJg"
		distrotube yt linux)
	       ("https://www.youtube.com/feeds/videos.xml?channel_id=UCld68syR8Wi-GY_n4CaoJGA"
		brodie yt linux)
	       ("https://www.youtube.com/feeds/videos.xml?channel_id=UCAiiOTio8Yu69c3XnR7nQBQ"
		systemcrafters yt linux)
	       ("https://www.youtube.com/feeds/videos.xml?channel_id=UC6QYFutt9cluQ3uSM963_KQ"
		ninja yt med)
	       ("https://www.youtube.com/feeds/videos.xml?channel_id=UC1yNl2E66ZzKApQdRuTQ4tw"
		sabine yt physics)
	       ("https://www.aartaka.me.eu.org/"
		artyom blog lisp)
	       ("https://nyxt-browser.com/feed"
		nyxt lisp)))

       (defun elfeed-mpv (&optional use-generic-p)
	 "Play video link with mpv."
	 (interactive "P")
	 (let ((entries (elfeed-search-selected)))
	   (cl-loop for entry in entries
		    do (elfeed-untag entry 'unread)
		    when (elfeed-entry-link entry)
		    do (start-process-shell-command "elfeed-video" nil (format "mpv \"%s\"" it)))
	   (mapc #'elfeed-search-update-entry entries)
	   (unless (use-region-p) (forward-line))))
       
       :bind (("C-x f" . elfeed)
	      :map elfeed-search-mode-map
	      ("v" . 'elfeed-mpv)
	      ("U" . 'elfeed-update))
       :hook ((elfeed-searchacw-mode . (lambda () (display-line-numbers-mode 0)))))

(use-package transmission
  :vc (:url "https://github.com/holomorph/transmission")
  :defer t)

(use-package rainbow-delimiters
  :vc (:url "https://github.com/Fanael/rainbow-delimiters")
  :ensure t
  :hook ((emacs-lisp-mode . rainbow-delimiters-mode)
         (lisp-mode . rainbow-delimiters-mode)
	 (clojure-mode . rainbow-delimiters-mode)
         (scheme-mode . rainbow-delimiters-mode)))

;; AI tools
(use-package gptel
  :ensure t
  :config
  (setf gptel-api-key (password-store-get-field "openai/openai@thanosapollo.org" "api")
	gptel-default-mode 'org-mode
	gptel-directives '((default . "You are a large language model living in Emacs and a helpful assistant. Respond concisely.")
			   (programming . "You are a large language model and an expert emacs lisp hacker. Provide code and only code as output without any additional text, prompt or note.")
			   (epictetus . "You are Epictetus, the stoic philosopher from Nicopolis. Respond concisely as Epictetus.")
			   (med . "You are a medical professor. Respond concisely to your student in bullet points.")
			   (code-review . "You are an expert programmer within Emacs reviewing code. Respond concisely")
			   (writer . "You are an expert writer and FOSS enthusiast. Improve only the article sections provided as a hacker, do not add extra paragraphs.")))

  (setq-default gptel-model "llama3.2:latest")
  
  (setq gptel-backend (gptel-make-ollama "Ollama"
			:host (if is-constantine "localhost:11434" "constantine:11434")
			:stream t
			:models '("llama3.2:latest" "dolphin-phi" "dolphin-llama3:latest")))

  (gptel-make-anthropic "Claude"
    :stream t
    :key (password-store-get "claude/api1"))
  
  :bind (("C-c G" . 'gptel-send)
	 :map gptel-mode-map
	 ("C-c h" . 'gptel-menu)))

(use-package package-lint
  :defer t)

(use-package yaml-mode
  :defer t)

(defun thanos/notmuch-decrypt-script ()
  (let ((encrypted-script (expand-file-name "notmuch-hook.sh.gpg" "~/.scripts"))
	(script (expand-file-name "notmuch-hook.sh" "~/.scripts")))
    (unless (file-exists-p script)
      (epa-decrypt-file encrypted-script script)
      (set-file-modes script #o700))))

(defun thanos/notmuch-update--command (new-buffer)
  (let ((default-directory "~/"))
    (async-shell-command "mbsync -a; .scripts/notmuch-hook.sh" new-buffer)))

(defun thanos/notmuch-update ()
  (interactive)
  (let ((new-buffer (generate-new-buffer "*notmuch update*"))
	(height (max 5 (truncate (* (frame-height) 0.1)))))
    (split-window-vertically (- height))
    (other-window 1)
    (switch-to-buffer new-buffer)
    (thanos/notmuch-decrypt-script)
    (let ((proc (thanos/notmuch-update--command new-buffer)))
      (set-process-sentinel
       (get-buffer-process new-buffer)
       (lambda (process event)
         (when (memq (process-status process) '(exit signal))
           (delete-window (get-buffer-window (process-buffer process)))
           (kill-buffer (process-buffer process))))))))

(use-package notmuch
  :defer t
  :bind (("C-x m" . notmuch-hello)
	 :map notmuch-hello-mode-map
	 ("u" . notmuch-hello-update)
	 ("U" . thanos/notmuch-update)
	 :map notmuch-search-mode-map
	 ("u" . notmuch-refresh-all-buffers))
  :hook ((notmuch-hello-mode . (lambda () (display-line-numbers-mode 0)))
	 (notmuch-search-mode . (lambda () (display-line-numbers-mode 0) (emojify-mode)))))

(setf notmuch-archive-tags '("-inbox" "-unread" "+archived")
      notmuch-show-all-tags-list t
      notmuch-hello-sections
      '(notmuch-hello-insert-header notmuch-hello-insert-saved-searches
				    notmuch-hello-insert-alltags))

(setq notmuch-search-oldest-first nil)

(setf notmuch-tag-formats
      '(("unread" (propertize tag 'face 'notmuch-tag-unread))
	("emacs-pm" (propertize tag 'face '((t :foreground "#ff9580"))))))

(setf notmuch-identities '("public@thanosapollo.org" "public@thanosapollo.com"
			   "104111@students.mu-sofia.bg"))

(setf notmuch-saved-searches
      `((:name "Inbox" :query "tag:inbox" :sort-order newest-first :key ,(kbd "i"))
	(:name "Unread" :query "tag:unread" :sort-order newest-first :key ,(kbd "u"))
	(:name "Today's message" :query "tag:inbox date:today" :sort-order newest-first
	       :key ,(kbd "t"))
	(:name "sent" :query "tag:sent" :sort-order newest-first :key ,(kbd "s"))
	(:name "drafts" :query "tag:draft" :sort-order newest-first :key ,(kbd "d"))
	(:name "all mail" :query "*" :sort-order newest-first :key ,(kbd "a"))))

;; Update notmuch on startup
;; (thanos/notmuch-update)

;; smtpmail settings
(setf smtpmail-smtp-user (password-store-get-field "mailbox/thanosapollo" "user")
      smtpmail-smtp-server "smtp.mailbox.org"
      smtpmail-smtp-service 465
      smtpmail-stream-type 'ssl
      message-send-mail-function 'smtpmail-send-it
      message-signature "Thanos Apollo ☧\nhttps://thanosapollo.org")

;; autosign messages
(add-hook 'message-send-hook 'mml-secure-message-sign-pgpmime)
;; Signers
(setf mml-secure-openpgp-signers '("62B758D0F6719938BC09CECA339F736C3A720928"))


;; (use-package yasnippet
;;   :ensure nil
;;   :config
;;   (when is-constantine (add-to-list 'yas-snippet-dirs "~/Dev/guile/guix/etc/snippets/yas")))

(use-package emms
  :ensure t
  :init (define-prefix-command 'thanos/emms)
  :config
  (with-eval-after-load 'emms
    (emms-all)
    (setf emms-source-file-default-directory "/hdd/Music"
          emms-info-asynchronously t
          emms-show-format "♪ %s"
	  emms-player-list '(emms-player-mpv))
    (add-to-list 'emms-info-functions 'emms-info-native))
  (setf emms-player-mpv-parameters
	'("--quiet" "--really-quiet" "--no-audio-display" "--no-video"))
  :bind (("C-z" . thanos/emms)
	 :map thanos/emms
	 ("n" . 'emms-next)
	 ("p" . 'emms-previous)
	 ("SPC" . 'emms-pause)
	 ("e" . 'emms)
	 ("s" . 'emms-browser-search-by-names)
	 ("C-e" . 'emms-smart-browse)
	 :map emms-playlist-mode-map
	 (("A" . 'emms-add-directory-tree))
	 :map emms-browser-search-mode-map
	 (("s" . 'emms-browser-search-by-names))))

(use-package debbugs
  :ensure nil
  :config
  (require 'bug-reference)
  (add-hook 'prog-mode-hook #'bug-reference-prog-mode)
  (add-hook 'gnus-mode-hook #'bug-reference-mode)
  (add-hook 'erc-mode-hook #'bug-reference-mode)
  (add-hook 'gnus-summary-mode-hook #'bug-reference-mode)
  (add-hook 'gnus-article-mode-hook #'bug-reference-mode)

  ;; Change the default when run as 'M-x debbugs-gnu'.
  (setq debbugs-gnu-default-packages '("emacs" "guix"))

  ;; Show feature requests.
  (setq debbugs-gnu-default-severities
	'("serious" "important" "normal" "minor" "wishlist")))


;;; Random commands ;;;;
;;;;;;;;;;;;;;;;;;;;;;;;

;; Virtual machines
(defvar vm-directory "~/Virtual/")

(defun vm-create-image ()
  "Create qcow2 image."
  (interactive)
  (let ((name (format "%s%s.qcow2" vm-directory (read-string "Name: ")))
	(size (format "%s" (read-string "Size(G): "))))
    (shell-command
     (format "qemu-img create -f qcow2 %s %sG" name size))))


(defun vm-run ()
  "Spawn Virtual Machine with UEFI support."
  (interactive)
  (let ((memory (format "%sG" (read-string "Memory(G): ")))
        (cores (read-string "Cores: "))
        (image (read-file-name "Image: " vm-directory))
        (iso (if (y-or-n-p "Load iso? ")
                 (read-file-name "ISO: ")
               nil))
        (ovmf-path (string-trim
                    (shell-command-to-string
                     "echo $(guix build ovmf-x86-64)/share/firmware/ovmf_x64.bin"))))
    (async-shell-command
     (format "qemu-system-x86_64 -enable-kvm -m %s -smp %s -hda %s -vga virtio -device virtio-serial-pci -netdev user,id=vmnic,hostfwd=tcp::2222-:22 -device e1000,netdev=vmnic %s"
             memory cores image (if iso (concat "-cdrom " iso) "")))))

(use-package 0x0
  :ensure nil)

;; Misc Functions ;;
;;;;;;;;;;;;;;;;;;;;

(defun thanos/run-in-background (command)
  "Run COMMAND in the background."
  (let ((command-parts (split-string command "[ ]+")))
    (apply #'call-process `(,(car command-parts) nil 0 nil ,@(cdr command-parts)))))

(defmacro thanos/make-frame (name &rest body)
    "Create temporary frame as NAME.

Create a temporary frame to execute BODY, which will then be deleted."
    `(unwind-protect
	 (with-selected-frame
	     (make-frame '((name . ,name)
			   (fullscreen . 0)
			   (undecorated . t)
			   (minibuffer . only)
			   (width . 70)
			   (height . 15)))
	   ,@body
	   (delete-frame))))

(defun create-text-scratch ()
  "Create a scratch buffer."
  (interactive)
  (switch-to-buffer (get-buffer-create "*Text Scratch*"))
  (org-mode))

(defun create-scratch ()
  "Create scratch buffer."
  (interactive)
  (switch-to-buffer (get-buffer-create "*scratch*"))
  (emacs-lisp-mode))

(defvar-keymap thanos/create-map
  :doc "Create custom buffers"
  "t" #'create-text-scratch
  "e" #'create-scratch)

;; Theming

(defvar wallpapers-dir "~/wallpapers/"
  "Wallpaper directory.")

(defvar wallpaper-current nil
  "Current wallpaper.")

(defun thanos/load-theme (&optional theme)
  "Disable current theme and load a new THEME."
  (interactive)
  (let ((theme (or theme (intern (completing-read "Theme: " (custom-available-themes))))))
    (disable-theme (car custom-enabled-themes))
    (load-theme theme t)))

(defun thanos/wallpaper-set (image)
  "Set IMAGE as wallpaper, using feh."
  (let ((command "swaybg -o '*' -i"))
    ;; Kill previous swaybg
    (call-process-shell-command "kill -15 $(pgrep swaybg | tail -n 1)")
    ;; Set wallpaper
    (call-process-shell-command
     (format "%s %s -m stretch" command image)
     nil 0)))

(defun thanos/wallpaper-random ()
  "Set random wallpaper."
  (interactive)
  (let ((wallpapers (directory-files "~/wallpapers" nil "^[^.].*")))
    (thanos/wallpaper-set
     (format "%s%s" wallpapers-dir (nth (random (length wallpapers)) wallpapers)))))

(defun thanos/wallpaper-select ()
  "Set wallpaper."
  (interactive)
  (let ((wallpaper (format "%s%s" wallpapers-dir
			   (completing-read "Choose wallpaper: "
					    (directory-files wallpapers-dir nil "^[^.].*")))))
    (thanos/wallpaper-set wallpaper)
    (setf wallpaper-current wallpaper)))

(defun thanos/wallpaper-watcher (_symbol new-value _where _environment)
  "Watch for wallpaper changes."
  (with-temp-buffer (find-file (expand-file-name "wallpaper" user-emacs-directory))
		    (erase-buffer)
		    (setf wallpaper-current new-value)
		    (insert (pp-to-string wallpaper-current))
		    (save-buffer)
		    (kill-buffer)))

(defun thanos/load-wallpaper ()
  "Load saved wallpaper."
  (let ((wallpaper-path (expand-file-name "wallpaper" user-emacs-directory)))
    (if (file-exists-p wallpaper-path)
	(with-temp-buffer
	  (insert-file-contents wallpaper-path)
	  (goto-char (point-min))
	  (let ((contents (read (current-buffer))))
	    (setf wallpaper-current contents)))
      (write-region "nil" nil wallpaper-path))))

(defun thanos/wallpaper-startup (&optional image)
  "Set wallpaper IMAGE on startup."
  (thanos/load-wallpaper)
  (let ((image (or image wallpaper-current)))
    (thanos/wallpaper-set image)))

(add-variable-watcher 'wallpaper-current #'thanos/wallpaper-watcher)

;; Set wallpaper
(when (or is-constantine is-hermes)
  (thanos/wallpaper-startup))

(defvar-keymap thanos/applications-map
  :doc "Thanos commonly used programs"
  "t" #'thanos/load-theme
  "w" #'thanos/wallpaper-select
  "C-c" thanos/create-map)

(define-key global-map (kbd "C-c a") thanos/applications-map)

(defun thanos/iimage-mode-buffer (arg)
  "Display images if ARG is non-nil, undisplay them otherwise."
  (let ((image-path (cons default-directory iimage-mode-image-search-path))
        (edges (window-inside-pixel-edges (get-buffer-window)))
	file)
    (with-silent-modifications
      (save-excursion
        (dolist (pair iimage-mode-image-regex-alist)
          (goto-char (point-min))
          (while (re-search-forward (car pair) nil t)
            (when (and (setq file (match-string (cdr pair)))
                       (setq file (locate-file file image-path)))
              (if arg
                  (add-text-properties
                   (match-beginning 0) (match-end 0)
                   `(display
                     ,(create-image file nil nil
                                    :max-width 120
				    :max-height 120)
                     keymap ,image-map
                     modification-hooks
                     (iimage-modification-hook)))
                (remove-list-of-text-properties
                 (match-beginning 0) (match-end 0)
                 '(display modification-hooks))))))))))

(define-minor-mode thanos/iimage-mode nil
  :group 'iimage :lighter " iImg"
  (thanos/iimage-mode-buffer thanos/iimage-mode))

;; Password

(defun thanos/pass-autotype (&optional entry)
  "Autotype password ENTRY."
  (let* ((entry (or entry (completing-read "Select entry: " (password-store-list))))
	 (user (password-store-get-field entry "user"))
	 (pass (password-store-get entry)))
    (start-process-shell-command
     "wtype" nil
     (format "sleep 0.3 && wtype %s -P tab %s"
	     (shell-quote-argument (if user user "thanosapollo"))
	     (shell-quote-argument pass)))))

(defun thanos/pass-launcher ()
  "Launch Emacs as a front-end for pass."
  (interactive)
  (thanos/make-frame
   "thanos/pass-launcher"
   (let* ((choice (completing-read "Choose an action: "
				   '("AUTO" "COPY PASS" "EDIT" "GENERATE")))
	  (action (pcase choice
		    ("AUTO" #'thanos/pass-autotype)
		    ("COPY PASS" #'password-store-copy)
		    ("EDIT" #'password-store-edit)
		    ("GENERATE" #'password-store-generate))))
     (funcall action (completing-read "Search: " (password-store-list))))))

(defun smtp-get-pass ()
  "Get password for smtp."
  (interactive)
  (password-store-copy-field "fastmail.com/thanosapollo@fastmail.com" "smtp"))

(defun org-table-insert-numbers (str end)
  "Used to insert numbers for verses in org tables."
  (interactive "nStart: \nnEnd: ")
  (dotimes (i (1+ (- end str)))
    (insert (format "%s" (+ i str)))
    (org-table-hline-and-move)))

(require 'server)
(unless (server-running-p)
  (server-start))

;;; init.el ends here