;; -*- lexical-binding: t; -*-
(setq gc-cons-threshold (* 100 1000 1000))
(add-hook 'after-init-hook #'(lambda ()
                               ;; restore after startup
                               (setq gc-cons-threshold 800000)))

(defun my/copy-buffer-file-path ()
  "Copy the current buffer's file path to kill ring.
For regular files, copies the full path.
For Dired buffers, copies the directory path.
Shows a message if no path is available."
  (interactive)
  (let ((path (cond
               (buffer-file-truename
                buffer-file-truename)
               (dired-directory
                dired-directory)
               (t nil))))
    (if path
        (progn
          (kill-new path)
          (message "Copied: %s" path))
      (message "No file path available"))))

(defun my/dired-open-current-file-directory ()
  "Open the current file's directory in Dired, if possible."
  (interactive)
  (if (buffer-file-name)
      (dired-jump)
    (call-interactively 'dired)))

(defun my/duplicate-line-or-region (arg)
  (interactive "p")
  (let (beg end (origin (point)))
    (if (and mark-active (> (point) (mark)))
        (exchange-point-and-mark))
    (setq beg (line-beginning-position))
    (if mark-active
        (exchange-point-and-mark))
    (setq end (line-end-position))
    (let ((region (buffer-substring-no-properties beg end)))
      (dotimes (i arg)
        (goto-char end)
        (newline)
        (insert region)
        (setq end (point)))
      (goto-char (+ origin (* (length region) arg) arg)))))

(defun my/mhtml-forward (arg)
  (interactive "P")
  (pcase (get-text-property (point) 'mhtml-submode)
    ('nil (sgml-skip-tag-forward 1))
    (submode (forward-sexp))))

(defun my/create-new-file ()
  "Create a new file with prompt for filename using :>."
  (interactive)
  (let ((filename (read-string "Filename: ")))
    (shell-command (concat ":> " filename))
    (find-file filename)))

(custom-set-variables
 ;; custom-set-variables was added by Custom.
 ;; If you edit it by hand, you could mess it up, so be careful.
 ;; Your init file should contain only one such instance.
 ;; If there is more than one, they won't work right.
 '(custom-safe-themes
   '("19d62171e83f2d4d6f7c31fc0a6f437e8cec4543234f0548bad5d49be8e344cd"
     "b99e334a4019a2caa71e1d6445fc346c6f074a05fcbb989800ecbe54474ae1b0"
     default))
 '(initial-frame-alist '((fullscreen . maximized)))
 '(package-selected-packages
   '(docker-compose-mode dockerfile-mode doom-modeline doom-themes
                         ef-themes elpy exec-path-from-shell
                         gruber-darker-theme highlight-indent-guides:
                         highlight-indentation: json-mode lsp-dart
                         magit marginalia meson-mode modus-themes
                         move-text orderless org-superstar prettier-js
                         projectile tide vala-mode verb vertico
                         vue-mode zig-mode))
 '(warning-suppress-log-types
   '((files missing-lexbind-cookie
            "~/.emacs.d/elpa/gruber-darker-theme-20231026.2031/gruber-darker-theme.el"))))

(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(package-initialize)

(use-package dired
  :config
  (put 'dired-find-alternate-file 'disabled nil)
  (add-hook 'dired-mode-hook
          (lambda ()
            (define-key dired-mode-map (kbd "RET")
              (lambda () (interactive) (find-alternate-file (dired-get-file-for-visit))))))
  (setq delete-by-moving-to-trash t)
  (setq dired-dwim-target t))

(use-package marginalia
  :ensure t)

(use-package vertico
  :ensure t
  :defer t
  :config
  (add-hook 'rfn-eshadow-update-overlay-hook 'vertico-directory-tidy))

(use-package move-text
  :ensure t
  :defer t
  :config
  (global-set-key (kbd "<M-up>") 'move-text-up)
  (global-set-key (kbd "<M-down>") 'move-text-down))

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

(require 'treesit)

(use-package docker-compose-mode
  :ensure t
  :defer t)

(use-package zig-mode
  :ensure t
  :defer t)

(use-package dockerfile-mode
  :ensure t
  :defer t
  :mode ("Dockerfile\\'" "\\.dockerfile\\'")
  :commands dockerfile-mode)

(use-package flycheck
  :ensure t
  :defer t
  :hook (prog-mode . flycheck-mode))

(vertico-mode 1)

(marginalia-mode 1)

(tool-bar-mode -1)

(scroll-bar-mode -1)

;;"M+1Code Nerd Font"
;;"Lekton Nerd Font"
;; Agave Nerd Font
;; EnvyCodeR Nerd Font
;; Binchotan_Sharp
;; Ubuntu Mono
(set-face-attribute 'default nil :font "Binchotan_Sharp" :height 140)
;;(set-face-attribute 'default nil :height 110)

;;(set-fontset-font t nil (font-spec :size 20 :name "VictorMono Nerd Font Mono"))
;;(set-face-attribute 'default nil :font "VictorMono Nerd Font Mono" :height 110)

;;(setq x-antialiasing-factor nil)

;; Example how to add a new dir to PATH
;; (add-to-list 'exec-path "/home/tar/.local/bin")

(set-default-coding-systems 'utf-8)

(setq ring-bell-function 'ignore)

(setq inhibit-startup-message t)

(setq backup-directory-alist '(("." . "~/.saves")))

(setq auto-save-default nil)

(setq-default indent-tabs-mode nil)

(setq show-paren-delay 0)

(setq max-mini-window-height 1)

(setq native-comp-async-report-warnings-errors 'silent)

(delete-selection-mode 1)

(show-paren-mode 1)

(add-hook 'before-save-hook 'whitespace-cleanup)

(setq-default frame-title-format
              '(:eval
                (format "%s"
                        (cond
                         (buffer-file-truename buffer-file-truename )
                         (dired-directory (concat "[ " dired-directory " ]"))
                         (t "GNU Emacs 👾")))))

(use-package elpy
  :ensure t
  :defer t
  :commands (elpy-enable)
  :hook (python-mode . elpy-enable)
  :config
  (setq elpy-modules (delq 'elpy-module-flymake elpy-modules))
  (setq elpy-rpc-timeout 5))

;; (add-hook 'python-mode-hook
;;           (lambda () (setq gud-pdb-command-name "python3 -m pdb")))

(use-package yasnippet
  :hook (prog-mode . yas-minor-mode)
  :config
  (setq yas-snippet-dirs '("~/.emacs.d/snippets"))
  (yas-reload-all))


(use-package meson-mode
  :ensure t
  :defer t)

(use-package vala-mode
  :ensure t
  :defer t
  :mode (("\\.vala\\'" . vala-mode)
         ("\\.vapi\\'" . vala-mode))
  :config
  (add-to-list 'file-coding-system-alist '("\\.vala\\'" . utf-8))
  (add-to-list 'file-coding-system-alist '("\\.vapi\\'" . utf-8)))

(use-package lsp-dart
  :ensure t
  :defer t
  :after lsp-mode
  :config
  (setq lsp-dart-flutter-widget-guides nil)
  (setq lsp-dart-closing-labels nil))

(use-package dart-mode
  :ensure t
  :defer t)

(use-package verb
  :ensure t
  :defer t)

(use-package move-text
  :ensure t
  :config
  (global-set-key (kbd "<M-up>") 'move-text-up)
  (global-set-key (kbd "<M-down>") 'move-text-down))

(use-package org
  :mode ("\\.org\\'" . org-mode)
  :config (define-key org-mode-map (kbd "C-c C-r") verb-command-map))

(use-package org-superstar
  :ensure t
  :defer t
  :hook (org-mode . org-superstar-mode))

(defface my-org-face
  '((t :family "Liberation Sans" :height 120))
  "Face for Org mode.")

(defun my-org-font-setup ()
  (face-remap-add-relative 'default 'my-org-face))

(add-hook 'org-mode-hook 'my-org-font-setup)

;; my keybindings

(defun revert-buffer-no-confirm ()
    (interactive)
    (revert-buffer :ignore-auto :noconfirm))

(defun kill-buffer-no-confirm ()
    (interactive)
    (kill-buffer nil))

(defun my/list-buffers-please ()
  (interactive)
  (list-buffers)
  (other-window 1)
  (delete-other-windows))

(defun my/toggle-buffer ()
  (interactive)
  (switch-to-buffer (other-buffer)))

(defun make-json-great-again ()
  (interactive)
  (when (string-match-p ".json\\'" (buffer-name))
    (mark-whole-buffer)
    (json-pretty-print (region-beginning) (region-end))))

(global-unset-key (kbd "C-d"))
(global-unset-key (kbd "C-z"))
(global-unset-key (kbd "C-v"))
(global-unset-key (kbd "C-f"))
(global-unset-key (kbd "C-b"))
(global-unset-key (kbd "C-n"))
(global-unset-key (kbd "C-l"))
(global-unset-key (kbd "C-p"))
(global-unset-key (kbd "M-v"))

(global-set-key (kbd "<f1>") 'find-file)
(global-set-key (kbd "<f5>") 'revert-buffer-no-confirm)
(global-set-key (kbd "C-<tab>") 'my/toggle-buffer)
(global-set-key (kbd "<f12>") 'kill-buffer-no-confirm)
(global-set-key (kbd "C-b") 'my/list-buffers-please)
(global-set-key (kbd "C-n") 'my/create-new-file)
(global-set-key (kbd "C-d") 'my/duplicate-line-or-region)
(global-set-key (kbd "C-x d") 'my/dired-open-current-file-directory)
(global-set-key (kbd "C-p") 'my/copy-buffer-file-path)

(add-hook 'elpy-mode-hook
          (lambda () (local-set-key [f2] 'elpy-refactor-rename)))

(add-hook 'elpy-mode-hook
          (lambda () (local-set-key [f7] 'elpy-goto-definition)))

(add-hook 'typescript-mode-hook
          (lambda () (local-set-key [f2] 'tide-rename-symbol)))

(add-hook 'typescript-mode-hook
          (lambda () (local-set-key [f7] 'tide-references)))

(add-hook 'hs-minor-mode-hook
          (lambda () (local-set-key (kbd "C-<escape>") 'hs-toggle-hiding)))

(add-hook 'mhtml-mode-hook
          (lambda () (local-set-key (kbd "C-f") 'prettier-js)))

(add-hook 'typescript-ts-mode-hook
          (lambda () (local-set-key (kbd "C-f") 'prettier-js)))

(add-hook 'js-mode-hook
          (lambda () (local-set-key (kbd "C-f") 'make-json-great-again)))

(add-hook 'lsp-mode-hook
          (lambda () (local-set-key (kbd "C-f") 'lsp-format-buffer)))

(add-hook 'elpy-mode-hook
          (lambda () (local-set-key (kbd "C-f") 'elpy-black-fix-code))) ;elpy-format-code

;; (add-hook 'prog-mode-hook
;;           (lambda () (local-set-key (kbd "C-;") 'comment-or-uncomment-region)))

;; (define-key map (kbd "M-.") 'elpy-goto-assignment
;; (define-key map (kbd "C-x 4 M-.") 'elpy-goto-assignment-other-window)

;; stuff for folding html

(add-to-list 'hs-special-modes-alist
             '(mhtml-mode
               "{\\|<[^/>]+?"
               "}\\|</[^/>]*[^/]>"
               "<!--"
               my/mhtml-forward
               nil))

(add-hook 'prog-mode-hook 'hs-minor-mode)

;; theming starts here:

(use-package doom-modeline
  :ensure t
  :hook (after-init . doom-modeline-mode))

(use-package doom-themes
  :ensure t)

(use-package gruber-darker-theme
  :ensure t)

(use-package modus-themes
  :ensure t)

;; (modus-themes-select 'modus-operandi-tinted)
(setq modus-themes-to-toggle '(modus-operandi-tinted modus-vivendi-tinted))

(use-package ef-themes
  :ensure t
  :init
  (modus-themes-include-derivatives-mode 1)
  :config
  (setq modus-themes-mixed-fonts t)
  (setq modus-themes-italic-constructs t)
  (setq ef-themes-to-toggle '(ef-elea-light ef-elea-dark))
  (setq ef-themes-headings ; read the manual's entry or the doc string
        '((0 . (variable-pitch light 1.9))
          (1 . (variable-pitch light 1.8))
          (2 . (variable-pitch regular 1.7))
          (3 . (variable-pitch regular 1.6))
          (4 . (variable-pitch regular 1.5))
          (5 . (variable-pitch 1.4)) ; absence of weight means `bold'
          (6 . (variable-pitch 1.3))
          (7 . (variable-pitch 1.2))
          (t . (variable-pitch 1.1))))
  (setq ef-themes-variable-pitch-ui t))


(defun my-setup-display-based-on-type ()
  "Load different themes and disable menu bar for console mode."
  (if (display-graphic-p)
      (progn
        ;;(modus-themes-load-theme 'ef-elea-light)
        (modus-themes-load-theme 'modus-operandi-tinted)
        ;;(load-theme 'doom-monokai-pro)
        (menu-bar-mode 1))
    (progn
      (load-theme 'gruber-darker t)
      (menu-bar-mode -1))))

(add-hook 'after-init-hook 'my-setup-display-based-on-type)

;; theming stops here

(use-package magit
  :ensure t
  :defer t
  :bind (("C-x g" . magit-status))
  :commands (magit-status magit-dispatch magit-file-dispatch)
  :config
  (setq magit-auto-revert-mode nil))

;; M-x nerd-icons-install-fonts
(use-package nerd-icons
  :ensure t)

(use-package prettier-js
  :ensure t)

(use-package orderless
  :ensure t
  :custom
  (completion-styles '(orderless basic))
  (completion-category-overrides '((file (styles basic partial-completion)))))

(use-package json-mode
  :ensure t
  :mode "\\.json\\'"
  :defer t
  :commands json-mode)

(use-package lsp-mode
  :ensure t
  :defer t
  :config
  (setq lsp-signature-render-documentation nil))

(use-package dap-mode
  :ensure t
  :defer t
  ;;:custom (lsp-enable-dap-auto-configure nil)
  :commands dap-debug)

(use-package vue-mode
  :ensure t
  :defer t
  :mode "\\.vue\\'"
  :hook (vue-mode . lsp-deferred))

;; __________________________________________________
;; typescript setup starts here
(defun setup-tide-mode ()
  (interactive)
  (tide-setup)
  (flycheck-mode +1)
  (setq flycheck-check-syntax-automatically '(save mode-enabled))
  (eldoc-mode +1)
  (tide-hl-identifier-mode +1))

(use-package tide
  :ensure t
  :after (flycheck)
  :hook ((typescript-ts-mode . setup-tide-mode)
         (tsx-ts-mode . setup-tide-mode)
         (typescript-ts-mode . tide-hl-identifier-mode)
         (before-save . tide-format-before-save))
  :config
  (add-to-list 'auto-mode-alist '("\\.ts\\'" . typescript-ts-mode))
  (add-to-list 'auto-mode-alist '("\\.tsx\\'" . tsx-ts-mode)))

;;----------------------------------------
;; dotnet starts here

(use-package dap-netcore
  :after dap-mode
  :defer t
  :commands (dap-netcore-setup)
  :init
  (add-hook 'csharp-mode-hook
            (lambda ()
              (require 'dap-netcore)
              (dap-netcore-setup))))


;;(setq dap-auto-configure-features '(sessions locals controls tooltip))

(defun setup-lsp-keys ()
  (interactive)
  (local-set-key [f7] 'lsp-find-implementation)
  (local-set-key [f2] 'lsp-rename))

(add-hook 'lsp-mode-hook
          (lambda () (setup-lsp-keys)))

(defun setup-dotnet ()
  (interactive)
  (exec-path-from-shell-copy-env "DOTNET_ROOT")
  (lsp-deferred))

(add-to-list 'auto-mode-alist '("\\.csproj\\'" . xml-mode))

(add-hook 'csharp-mode-hook
          (lambda () (setup-dotnet)))

(defun find-project-root ()
  (interactive)
  (if (ignore-errors (eproject-root))
      (eproject-root)
    (or (find-git-repo (buffer-file-name)) (file-name-directory (buffer-file-name)))))

(defun find-git-repo (dir)
  (if (string= "/" dir)
      nil
    (if (file-exists-p (expand-file-name "../.git/" dir))
        dir
      (find-git-repo (expand-file-name "../" dir)))))


(defun file-path-to-namespace ()
  (interactive)
  (let (
        (root (find-project-root))
        (base (file-name-nondirectory buffer-file-name))
        )
    (substring (replace-regexp-in-string "/" "\." (substring buffer-file-name (length root) (* -1 (length base))) t t) 0 -1)
    )
  )


(custom-set-faces
 ;; custom-set-faces was added by Custom.
 ;; If you edit it by hand, you could mess it up, so be careful.
 ;; Your init file should contain only one such instance.
 ;; If there is more than one, they won't work right.
 )
