;;; 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:

;; Disable package.el
(setq package-enable-at-startup nil
      package-archives nil)

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

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

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

;; Font
(custom-set-faces
 (if is-hermes '(default ((t (:inherit nil :height 130 :family "Jetbrains Mono"))))
   '(default ((t (:inherit nil :height 140 :family "Jetbrains Mono")))))
 '(variable-pitch ((t (:inherit t :height 140 :family "Iosevka Aile"))))
 '(org-modern-symbol ((t (:inherit t :family "Iosevka Aile")))))

;; Autoinsert
(auto-insert-mode 1)

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

(add-to-list 'completion-styles 'initials t)

(setf tab-always-indent 'complete)

;; xref
(setf xref-show-xrefs-function #'consult-xref
      xref-show-definitions-function #'consult-xref)

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

;; Enable use-package support for imenu
(setf use-package-enable-imenu-support t)

;; Install straight.el
(defvar bootstrap-version)

(let ((bootstrap-file
       (expand-file-name
        "straight/repos/straight.el/bootstrap.el"
        (or (bound-and-true-p straight-base-dir)
            user-emacs-directory)))
      (bootstrap-version 7))
  (unless (file-exists-p bootstrap-file)
    (with-current-buffer
        (url-retrieve-synchronously
         "https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el"
         'silent 'inhibit-cookies)
      (goto-char (point-max))
      (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))

(require 'straight)

(setf straight-use-package-by-default t)

(setf straight-recipe-overrides
      '((transmission :type git :host nil :repo "git@thanosapollo.org:/var/git/transmission.git")))

(setf browse-url-browser-function 'browse-url-generic
      browse-url-generic-program "librewolf"
      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)

(setq 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))))

(use-package emacs
  :ensure nil
  :config

  (if is-zeus
      (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
  :straight nil
  :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
(global-visual-line-mode)

(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)

(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-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}")))
  :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))))

(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-star 'replace
	org-modern-list '((?+ . "•")
			  (?- . "•")))
  :hook ((org-mode . org-modern-mode)))

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

(use-package org-roam
  :defer t
  :init
  (define-prefix-command 'thanos/notes-map)
  :config
  (setf org-roam-directory "~/Notes"
	org-roam-dailies-directory "daily/")

  (org-roam-db-autosync-enable)

  (setf org-roam-node-display-template
	(concat "${title:50} "(propertize "${tags:30}" 'face 'org-tag)))

  (setf org-roam-db-node-include-function
	(lambda ()
	  (not (or (member "journal" (org-get-tags))
		   (member "dailies" (org-get-tags))))))
  ;; Templates
  (setf org-roam-capture-templates
	'(("d" "default" plain
	   "%?"
	   :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n#+startup: overview\n")
	   :unnarrowed t)
	  ("p" "MUS" plain "* Goals\n\n%?\n\n* Tasks\n\n** TODO Add initial tasks\n\n* Dates\n\n"
	   :if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n#+filetags: MUS")
	   :unnarrowed t))
	org-roam-dailies-capture-templates
	'(("d" "default" entry
	   "* %?"
	   :target (file+head "%<%Y-%m-%d>.org"
                              "#+title: %<%Y-%m-%d>\n#+filetags: :journal:\n"))
	  ("j" "journal" plain
	   "\n* Daily Notes\n\n* Goals\n+ []\n\n* Extras %?"
	   :target (file+head "%<%Y-%m-%d>.org"
			      "#+title: %<%Y-%m-%d>\n#+filetags: :journal:\n"))))

  (defun org-roam-ref-add-book ()
    "Insert org-link from Library."
    (interactive)
    (let ((book (format "file:%s" (read-file-name "Book: " (if is-zeus "/hdd/Library/" "~/Library/")))))
      book))

  (defun org-roam-sync-notes ()
    "Sync org-oram notes"
    (interactive)
    (let ((git (executable-find "git"))
	  (default-directory org-roam-directory))
      (message "Synching org-roam notes %s" org-roam-directory)
      (unless git
	(error "Git not found, please install `git'"))
      (unless (file-exists-p (expand-file-name ".git" gnosis-dir))
	(message "Creating git repository")
	(vc-create-repo 'Git))
      (shell-command "git pull")
      (shell-command (format "%s %s" git "add ."))
      (shell-command (format "%s %s %s" git "commit -m" (shell-quote-argument "Update org-roam notes")))
      (vc-git-push nil))
    (funcall 'org-roam-db-sync))
  :bind (("C-c n" . thanos/notes-map)
	 :map thanos/notes-map
	 ("t" . org-roam-buffer-toggle)
         ("f" . org-roam-node-find)
         ("i" . org-roam-node-insert)
	 ("d" . org-roam-dailies-goto-today)
	 ("D" . org-roam-dailies-goto-date)
	 :map org-mode-map
	 ("C-c C-." . org-roam-tag-add)
	 ("C-c i" . org-id-get-create)))

(use-package org-roam-ui
  :defer t)

(use-package modus-themes
  :straight 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 '(italic)
	modus-themes-completions '((matches . (extrabold))
				   (selection . (semibold italic text-also underline)))
	modus-themes-org-blocks 'tinted-background)
  ;; Palette overrides
  (setf modus-themes-common-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 "#171717") ;; badger
	  ;; (overline-heading-1 gold)
	  (fg-heading-1 red-warmer)
	  ;; (bg-heading-1 bg-blue-nuanced)
          (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 "#86B187")
	  (border-mode-line-active unspecified)
          (border-mode-line-inactive unspecified)
	  (bg-mode-line-active "#433F4f") ;; subtle lavender
	  (bg-mode-line-inactive "#1D1D1D")
	  ;; set fg from badger theme
          (fg-mode-line-active "#F6F3E8")
	  (bg-hl-line bg-dim)
	  (cursor slate)
	  (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.3))
          (agenda-structure . (variable-pitch light 1.8))
          (t . (1.15))))
  ;; Load modus
  (load-theme 'modus-vivendi t))

(use-package vertico
  :ensure t
  :config
  (vertico-mode))

(use-package marginalia
  :ensure t
  :config
  (marginalia-mode))

(use-package consult
  :ensure t
  :init (define-prefix-command 'thanos/search)
  :bind (("C-x r d" . 'bookmark-delete)
	 ("C-x r C-r" . 'bookmark-rename)
	 ("C-x r C-j" . 'consult-register)
	 ("C-x r SPC" . 'consult-register-store)
	 ("C-x r b" . 'consult-bookmark)
	 ("C-c m" . 'consult-imenu)
	 ("C-x b" . 'consult-buffer)
	 ("C-x C-b" . 'switch-to-prev-buffer)
         ("M-y" . 'consult-yank-from-kill-ring)
	 ("C-M-s" . 'consult-line)
         ("M-s" . 'thanos/search)
	 :map thanos/search
	 ("M-f" . 'consult-find)
	 ("g" . 'consult-grep)
	 ("i" . 'consult-info)
	 ("l" . 'consult-locate)
	 :map project-prefix-map
	 ("b" . 'consult-project-buffer)))

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

(use-package elfeed
  :defer t
  :config
  (setf elfeed-search-filter "@1-week-ago +unread -hackernoon"
	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%2Fmedfac.mu-sofia.com%2Fen%2Fnews%2F&url_selector=div.news-card&url_pattern=%2F*&content_selector=article.richtext-area&content_cleanup=&title_cleanup=&limit=&format=Atom" musofia 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.tovima.gr%2Fcategory%2Fscience%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 science news)
	  ("http://localhost/?action=display&bridge=CssSelectorBridge&home_page=https%3A%2F%2Fwww.reuters.com%2Fworld%2F&url_selector=a.media-story-card__headline__tFMEu&url_pattern=&content_selector=article.article__container__2MUeZ&content_cleanup=div.info-content__toolbar__3AkHm%2C+div.article-body__row__dFOPA%2C+div.article__read-next__Kjxdw&title_cleanup=&limit=&format=Atom" reuters world news)
	  ("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.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)
	  ("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://rss-bridge.thanosapollo.org/?action=display&bridge=CssSelectorBridge&home_page=https%3A%2F%2Fwww.wired.com%2Fmost-recent%2F&url_selector=a.SummaryItemHedLink-civMjp&url_pattern=&content_selector=article.main-content&content_cleanup=div.ActionBarWrapperContent-lasBkU&title_cleanup=&limit=&format=Atom" wired tech news)
	  ("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)
	  ;; 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)))))

;; Python
(use-package python-mode
  :defer t
  :config
  (add-to-list 'auto-mode-alist '("\\.py\\'" . python-mode)))

(use-package pyenv
  :defer t)

(use-package elpy
  :straight nil
  :ensure nil
  :init
  (elpy-enable))

;; Clojure
(use-package cider
  :defer t)

(use-package clojure-mode
  :defer t)

(use-package rainbow-delimiters
  :defer t
  :hook ((emacs-lisp-mode . rainbow-delimiters-mode)
         (lisp-mode . rainbow-delimiters-mode)
	 (clojure-mode . rainbow-delimiters-mode)
         (scheme-mode . rainbow-delimiters-mode)))

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

(use-package helpful
  :defer t
  :bind (("C-h f" . 'helpful-callable)
	 ("C-h v" . 'helpful-variable)
	 ("C-h k" . 'helpful-key)
	 ("C-h x" . 'helpful-command)
	 ("C-h ." . 'helpful-at-point)
	 ("C-h F" . 'helpful-function)
	 ("C-h C-k" . 'helpful-kill-buffers)
	 ("C-h a" . 'apropos)
	 ("C-h C-m" . 'info-apropos)))

(use-package ox-hugo
  :ensure t
  :config
  (setf org-hugo-section "post"))

(use-package json-mode
  :defer t
  :config
  (add-to-list 'auto-mode-alist '("\\.json'" . json-mode)))

(defun project-magit ()
  "Run magit-status in the current project's root."
  (interactive)
  (magit-status-setup-buffer (project-root (project-current t))))

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

(use-package magit-todos
  :after magit
  :config
  (magit-todos-mode 1)
  (setf magit-todos-keywords 'hl-todo-keyword-faces))

(use-package corfu
  :ensure t
  :config
  (global-corfu-mode)
  (corfu-popupinfo-mode)
  (setf corfu-auto t
	corfu-auto-delay 0.1
	corfu-auto-prefix 2
	corfu-cycle t
	corfu-popupinfo-delay 0.3
	corfu-quit-at-boundary 'separator
	corfu-quit-no-match t
        corfu-preselect 'first
	corfu-preview-current t
	corfu-echo-mode t)
  (setf indent-tabs-mode nil))

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

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


(use-package orderless
  :init (add-to-list 'completion-styles 'initials t)
  :ensure t
  :config
  (setf completion-category-overrides '((file (style basic partial-completion)))
	completion-styles '(orderless)
	completion-cycle-threshold 2))

(use-package pdf-tools
  :straight nil
  :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 markdown-mode
  :straight nil
  :defer t
  :config
  (setq markdown-header-scaling t
        markdown-command "multimarkdown")
  (add-to-list 'auto-mode-alist '("\\.md\\'" . gfm-mode))
  :hook ((markdown-mode . flyspell-mode)))

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

(use-package eshell-syntax-highlighting
  :defer t)

(use-package emojify
  :ensure t
  :hook (erc-mode . emojify-mode)
  :commands emojify-mode)

(use-package flycheck-package
  :straight t
  :ensure t
  :after flycheck)

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

;; Shells
(use-package vterm
  :straight nil
  :ensure nil
  :bind (("C-c v" . vterm)
	 :map vterm-mode-map
	 ("M-&" . 'async-shell-command)
	 ("C-c C-y" . 'vterm-copy-mode))
  :hook
  ((vterm-mode . (lambda () (display-line-numbers-mode -1)))))

(use-package shell
  :defer t
  :bind (("C-c V" . shell)
	 :map shell-mode-map
	 ("C-l" . 'comint-clear-buffer))
  :hook ((shell-mode . (lambda () (display-line-numbers-mode -1)))))

(defvar thanos/aliases
  '((g . magit)
    (gl . magit-log)
    (gc . magit-clone)
    (d . dired)
    (o . find-file)
    (oo . find-file-other-window)
    (ll . (lambda () (eshell/ls '-lha)))
    (eshell/clear . eshell/clear-scrollback)))

(defun thanos/set-eshell-aliases (aliases)
  "Set ALIASES as eshell aliases."
  (mapc (lambda (alias)
	  (defalias (car alias) (cdr alias)))
	aliases))

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

(use-package eshell
  :config
  (setf eshell-highlight-prompt t)
  (eshell-syntax-highlighting-global-mode 1)
  :bind (("C-c e" . eshell)
	 :map eshell-mode-map
	 ("C-l" . 'thanos/eshell-clear))
  :hook ((eshell-mode . (lambda ()
			  (thanos/set-eshell-aliases thanos/aliases)
			  (display-line-numbers-mode -1)))))

(use-package eshell-git-prompt
  :straight nil
  :config
  (defun eshell-git-prompt-multiline ()
  "Eshell Git prompt inspired by spaceship-prompt."
  (let (separator hr dir git git-dirty time sign command)
    (setq separator (with-face " | " 'eshell-git-prompt-multiline-secondary-face))
    (setq hr (with-face (concat "\n" (make-string (/ (window-total-width) 2) ?─) "\n") 'eshell-git-prompt-multiline-secondary-face))
    (setq dir
          (concat
           (with-face "" 'eshell-git-prompt-directory-face)
           (concat  (abbreviate-file-name (eshell/pwd)))))
    (setq git
          (concat (with-face "⎇" 'eshell-git-prompt-exit-success-face)
                  (concat (eshell-git-prompt--branch-name))))
    (setq git-dirty
          (when (eshell-git-prompt--branch-name)
            (if (eshell-git-prompt--collect-status)
                (with-face " ✎" 'eshell-git-prompt-modified-face)
              (with-face " ✔" 'eshell-git-prompt-exit-success-face))))
    (setq time (with-face (format-time-string "%I:%M:%S %p") 'eshell-git-prompt-multiline-secondary-face))
    (setq sign
          (if (= (user-uid) 0)
              (with-face "\n#" 'eshell-git-prompt-multiline-sign-face)
            (with-face "\nλ" 'eshell-git-prompt-multiline-sign-face)))
    (setq command (with-face " " 'eshell-git-prompt-multiline-command-face))

    ;; Build prompt
    (eshell-git-prompt---str-read-only
     (concat hr dir separator git git-dirty separator time sign command))))
  
  (eshell-git-prompt-use-theme 'multiline))

;; Chat
(use-package jabber
  :defer t
  :config
  (defun jabber-buffers-formats (&optional buffer-formats)
    "Return jabber BUFFER-FORMATS without the format specifiers.

By default, returns all jabber related buffers format."
    (let ((buffer-formats (or buffer-formats '(jabber-chat-buffer-format
					       jabber-browse-buffer-format
					       jabber-roster-buffer
					       jabber-groupchat-buffer-format
					       jabber-muc-private-buffer-format))))
      (cl-loop for var in buffer-formats
               for str = (symbol-value var)
               for formatted-str = (when (stringp str)
				     ;; Remove format specifier and
				     ;; the following 2 char, or 1 if
				     ;; there is not second.
                                     (replace-regexp-in-string "%.?" "" str))
               collect formatted-str)))

  (defun jabber-switch-to-buffer ()
    "Prompt the user to select a buffer whose name matches a list of strings."
    (interactive)
    (let* ((string-list (jabber-buffers-formats))
           (buffers-matching-strings
            (cl-loop for buf in (buffer-list)
                     when (cl-loop for str in string-list
                                   thereis (string-match-p str (buffer-name buf)))
                     collect (buffer-name buf))))
      (if buffers-matching-strings
	  (switch-to-buffer
	   (completing-read "Select jabber buffer: " buffers-matching-strings)))
      (error "No jabber buffer found")))
  :bind (:map jabber-global-keymap
	      ("C-b" . 'jabber-switch-to-buffer)))

(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
	'(sasl 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 ()
    (interactive)
    "Login to libera.chat"
    (erc-tls :server "irc.libera.chat" :port 6697
	     :nick "thanosapollo"
	     :user "thanosapollo"
	     :password (password-store-get "liberachat/thanos_apollo")))
  :bind (("C-c E" . 'erc-libera)
	 :map erc-mode-map
	 ("C-c RET" . 'erc-cmd-QUERY)))

(use-package transmission
  :defer t)

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

(use-package dabbrev
  :defer t
  :config
  (setf dabbrev-ignored-buffer-regexps '("\\.\\(?:pdf\\|jpe?g\\|png\\)\\'")))

(use-package xref
  :defer t
  :config
  (setf xref-show-xrefs-function #'consult-xref
	xref-show-definitions-function #'consult-xref))

;; My packages
(when (or is-zeus is-hermes)
  (use-package yeetube
    :init (define-prefix-command 'thanos/yeetube-map)
    :straight (yeeutube :local-repo "~/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
    :straight (gnosis :local-repo "~/Dev/emacs-lisp/gnosis")
    :ensure t
    :init (define-prefix-command 'thanos/gnosis-map)
    :config
    (setf gnosis-vc-auto-push nil
	  gnosis-mcq-display-choices nil
	  gnosis-image-width (and is-hermes 150)
	  gnosis-image-height (and is-hermes 150))
    (gnosis-modeline-mode)
    :bind (("C-M-r" . 'gnosis-dashboard)))

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

  (use-package pcmpl-tailscale
    :straight (pcmpl-tailscale :local-repo "~/Dev/emacs-lisp/pcmpl-tailscale")
    :ensure nil))

;; Emacs dev

(use-package package-lint
  :defer t)

;; AI tools
(use-package gptel
  :defer 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 a careful programmer. 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 within Emacs. Respond concisely.")
			   (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.1:latest")
  
  (setq gptel-backend (gptel-make-ollama "Ollama"
			:host (if is-zeus "localhost:11434" "zeus:11434")
			:stream t
			:models '("llama3.1:latest" "dolphin-llama3:latest" "zephyr:latest"
				  "mistral:latest" "mixtral:8x22b" "neural-chat:latest"
				  "dolphin-mixtral:latest" "phi")))
  :bind (("C-c g" . 'gptel-send)
	 :map gptel-mode-map
	 ("C-c h" . 'gptel-menu)))


;; 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 package-lint
  :defer t)

(use-package yaml-mode
  :defer t)

(when (or is-zeus is-hermes)
  (use-package notmuch
    :ensure t
    :commands notmuch-hello
    :bind (("C-x m" . notmuch-hello)
	   :map notmuch-hello-mode-map
	   ("U" . thanos/notmuch-update)
	   ("u" . notmuch-hello-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))))
    :config
    (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 ()
      (interactive)
      (let ((default-directory "~/")
	    (new-buffer (generate-new-buffer "*notmuch update*"))
	    (height (max 5 (truncate (* (frame-height) 0.1))))
	    (command "mbsync -a; ./.scripts/notmuch-hook.sh"))
	(split-window-vertically (- height))
	(other-window 1)
	(switch-to-buffer new-buffer)
	(thanos/notmuch-decrypt-script)
	(let ((proc (async-shell-command 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))))))))

    (setf notmuch-archive-tags '("-inbox" "-unread" "+archived")
	  notmuch-hello-sections '(notmuch-hello-insert-header notmuch-hello-insert-saved-searches notmuch-hello-insert-recent-searches notmuch-hello-insert-alltags notmuch-hello-insert-footer))

    (setq notmuch-search-oldest-first nil)
    
    (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 "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"))))))

;; smtpmail settings
(setf smtpmail-smtp-user "public@thanosapollo.org"
      smtpmail-smtp-server "smtp.forwardemail.net"
      smtpmail-smtp-service 465
      smtpmail-stream-type 'ssl
      message-send-mail-function 'smtpmail-send-it
      message-signature "Thanos Apollo\n https://thanosapollo.org")

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

(use-package debbugs
  :ensure nil)


;;; 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."
  (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)))
    (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
  :straight nil
  :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
(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"))

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

;;; init.el ends here