;;; System

(add-to-list 'load-path (expand-file-name "~/.emacs.d/site-lisp"))

;; Platform specific configuration
(cond ((equal system-type 'darwin)
       (setenv "PATH" (concat (getenv "PATH") ":/usr/local/bin"))
       (setq exec-path (append exec-path '("/usr/local/bin")))
       (setq dired-listing-switches "-al")
       (global-set-key [home] 'move-beginning-of-line)
       (global-set-key [end] 'move-end-of-line))
      ((or (equal system-type 'gnu/linux) (equal system-type 'gnu/kfreebsd))
       (setq dired-listing-switches "-al --time-style=long-iso")))

(setq-default
 gc-cons-threshold              10000000
 backup-by-copying              t
 backup-directory-alist         `((".*" . ,temporary-file-directory))
 auto-save-file-name-transforms `((".*" ,temporary-file-directory t))
 scheme-program-name            "mzscheme"
 browse-url-generic-program     (executable-find "sensible-browser")
 browse-url-browser-function    'browse-url-generic)

;;; Packages

(require 'package)
(add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/"))
(package-initialize)
(unless (package-installed-p 'use-package)
  (progn
    (package-refresh-contents)
    (package-install 'use-package)))

(require 'use-package)

(setq use-package-always-ensure t)
(use-package ace-window)
(use-package clang-format :commands (c-mode c++-mode))
(use-package counsel :after (ivy) :config (counsel-mode))
(use-package counsel-gtags :commands ggtags-mode)
(use-package counsel-projectile :after (counsel projectile))
(use-package diminish)
(use-package dired+)
(use-package dired-rainbow :after (dired+))
(use-package flyspell :commands flyspell-mode)
(use-package flyspell-correct-ivy :after (flyspell ivy) :commands flyspell-mode)
(use-package ggtags :commands ggtags-mode)
(use-package ivy :config (ivy-mode))
(use-package llvm-mode :commands llvm-mode)
(use-package magit :commands magit-status :bind ("C-c m" . magit-status) :config (define-key magit-mode-map (kbd "C-<tab>") nil))
(use-package markdown-mode :commands markdown-mode)
(use-package pdf-tools :magic ("%PDF" . pdf-view-mode) :config (pdf-tools-install))
(use-package projectile :config (projectile-global-mode))
(use-package rainbow-delimiters :commands prog-mode)
(use-package smart-tabs-mode :commands (c-mode c++-mode))
(use-package spaceline)
(use-package swiper :commands swiper)
(use-package tramp)

(require 'spaceline-config)

;;; Basic editor configuration

(defalias 'yes-or-no-p 'y-or-n-p)
(delete-selection-mode 1)
(winner-mode 1)
(setq-default tab-width        4)
(setq-default indent-tabs-mode nil)
(setq-default scroll-margin    3)
(spaceline-toggle-buffer-size-off)
(spaceline-toggle-hud-off)
(spaceline-toggle-buffer-encoding-abbrev-off)
(diminish 'abbrev-mode "")
(diminish 'auto-fill-function "↵")
(diminish 'auto-revert-mode "↻")
(diminish 'compilation-in-progress "⚙")
(diminish 'counsel-mode "")
(diminish 'flyspell-mode "✓")
(diminish 'ggtags-mode "☚")
(diminish 'ivy-mode "")
(setq spaceline-minor-modes-separator " ")

(spaceline-define-segment version-control
  "Version control information."
  (when vc-mode
    (powerline-raw
     (s-trim (concat vc-mode
                     (when (buffer-file-name)
                       (pcase (vc-state (buffer-file-name))
                         (`up-to-date " ")
                         (`edited " *")
                         (`added " +")
                         (`unregistered " ?")
                         (`removed " -")
                         (`needs-merge " !")
                         (`needs-update " ^")
                         (`ignored " I")
                         (_ " ?"))))))))

(spaceline-define-segment buffer-position
  "Buffer position in percent"
  (format "%3d%%%%" (/ (* 100 (- (line-number-at-pos) 1))
                       (max 1 (count-lines (point-min) (point-max))))))

;;; Variables

(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.
 '(auto-revert-interval 1)
 '(backup-by-copying t)
 '(backup-directory-alist (\` ((".*" \, temporary-file-directory))))
 '(browse-url-firefox-new-window-is-tab t)
 '(column-number-mode t)
 '(compilation-scroll-output t)
 '(cursor-in-non-selected-windows (quote hollow))
 '(custom-enabled-themes (quote (drobillized)))
 '(custom-safe-themes
   (quote
    ("c2616934f29a24eded08ab10db3615ba9fa6b91ce0427dd53700d43f94705f42" default)))
 '(delete-old-versions t)
 '(dired-omit-files "^\\.?#\\|^\\.$\\|^\\.\\.\\|^\\..*$")
 '(diredp-hide-details-initially-flag t)
 '(diredp-wrap-around-flag nil)
 '(erc-autojoin-channels-alist
   (quote
    ((".*\\.freenode.net" "#ardour" "#ardour-dev" "#ingen" "#lv2" "#moddevices" "#lad"))))
 '(erc-autojoin-mode t)
 '(erc-join-buffer (quote bury))
 '(erc-log-channels-directory "~/.erc/logs")
 '(erc-log-insert-log-on-open t)
 '(erc-log-mode t)
 '(erc-mode-line-format "%t")
 '(erc-prompt ">")
 '(erc-track-exclude-server-buffer t)
 '(erc-track-exclude-types
   (quote
    ("JOIN" "NICK" "PART" "QUIT" "MODE" "333" "353" "324" "329" "332" "477")))
 '(fill-column 79)
 '(font-lock-maximum-decoration t)
 '(frame-background-mode (quote dark))
 '(fringe-mode 0 nil (fringe))
 '(gc-cons-threshold 10000000)
 '(grep-find-command
   "find . -type f -not -name \"*.svn-base\" -print0 | xargs -0 grep -I -n -s -F ")
 '(indicate-empty-lines t)
 '(inhibit-startup-screen t)
 '(ivy-magic-tilde nil)
 '(jabber-account-list
   (quote
    (("david.e.robillard@gmail.com"
      (:network-server . "talk.google.com")
      (:port . 5223)
      (:connection-type . ssl)))))
 '(jabber-chat-buffer-show-avatar nil)
 '(jabber-chat-header-line-format
   (quote
    (""
     (:eval
      (jabber-jid-displayname jabber-chatting-with))
     "	"
     (:eval
      (let
          ((buddy
            (jabber-jid-symbol jabber-chatting-with)))
        (propertize
         (or
          (cdr
           (assoc
            (get buddy
                 (quote show))
            jabber-presence-strings))
          (get buddy
               (quote show)))
         (quote face)
         (or
          (cdr
           (assoc
            (get buddy
                 (quote show))
            jabber-presence-faces))
          (quote jabber-roster-user-online)))))
     "	"
     (:eval
      (jabber-fix-status
       (get
        (jabber-jid-symbol jabber-chatting-with)
        (quote status))))
     "	" jabber-events-message "	" jabber-chatstates-message)))
 '(jabber-roster-line-format " %c %-25n %u %-8s  %S")
 '(jshint-mode-node-program "nodejs")
 '(magit-auto-revert-mode nil)
 '(magit-diff-refine-hunk (quote all))
 '(menu-bar-mode nil)
 '(ns-use-srgb-colorspace nil)
 '(package-selected-packages
   (quote
    (rainbow-delimiters auctex clang-format counsel-gtags counsel-projectile dired+ dired-rainbow flyspell-correct-ivy ggtags ivy magit markdown-mode n3-mode pdf-tools projectile spaceline use-package)))
 '(pdf-view-midnight-colors (quote ("#C3D1D1" . "#141414")))
 '(powerline-default-separator (quote arrow))
 '(projectile-completion-system (quote ivy))
 '(projectile-enable-caching t)
 '(projectile-mode-line nil)
 '(projectile-use-git-grep t)
 '(quack-pretty-lambda-p t)
 '(quack-run-scheme-always-prompts-p nil)
 '(savehist-mode t)
 '(scroll-bar-mode nil)
 '(scroll-conservatively 5)
 '(send-mail-function (quote sendmail-send-it))
 '(show-paren-mode t)
 '(speedbar-use-images nil)
 '(split-window-preferred-function (quote split-window-horizontally))
 '(tool-bar-mode nil)
 '(version-control t)
 '(visible-bell nil))

(setq ring-bell-function
      (lambda ()
        (lexical-let ((fg (face-foreground 'mode-line)))
          (set-face-foreground 'mode-line "#F2804F")
          (run-with-idle-timer 0.1 nil (lambda () (set-face-foreground 'mode-line fg))))))

;;; Colours

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

;;; Windows

(defun compile-autoclose (buffer string)
  "Close compilation window on success"
  (cond ((and (string-match "compilation" (buffer-name buffer))
              (string-match "finished" string))
         (bury-buffer "*compilation*")
         (winner-undo)
         (message "Build successful."))
        (t
         (message "Compilation exited abnormally: %s" string))))

(defun rotate-windows ()
  "Rotate windows"
  (interactive)
  (if (> (count-windows) 1)
      (let ((i 0)
            (num-windows (count-windows)))
        (while  (< i (- num-windows 1))
          (let* ((w1 (elt (window-list) i))
                 (w2 (elt (window-list) (% (+ i 1) num-windows)))
                 (b1 (window-buffer w1))
                 (b2 (window-buffer w2))
                 (s1 (window-start w1))
                 (s2 (window-start w2)))
            (set-window-buffer w1 b2)
            (set-window-buffer w2 b1)
            (set-window-start w1 s2)
            (set-window-start w2 s1)
            (setq i (+ i 1)))))))

(setq split-height-threshold       nil)
(setq split-width-threshold        0)
(setq compilation-finish-functions 'compile-autoclose)

;; Remove slow Maven regexp that makes the compilation buffer clunky
(setq compilation-error-regexp-alist
      (delete 'maven compilation-error-regexp-alist))

;;; Key bindings

(global-set-key (kbd "C-<left>")  'previous-buffer)
(global-set-key (kbd "C-<right>") 'next-buffer)
(global-set-key (kbd "C-<tab>")   'ace-window)
(global-set-key (kbd "C-b")       'compile)
(global-set-key (kbd "C-m")       'newline-and-indent)
(global-set-key (kbd "C-n")       'next-error)
(global-set-key (kbd "C-p")       'previous-error)
(global-set-key (kbd "C-s")       'swiper)

(global-set-key (kbd "C-c <") 'first-error)
(global-set-key (kbd "C-c =") 'set-variable)
(global-set-key (kbd "C-c I") 'clang-format-region)
(global-set-key (kbd "C-c R") 'replace-regexp)
(global-set-key (kbd "C-c a") 'align)
(global-set-key (kbd "C-c c") 'comment-region)
(global-set-key (kbd "C-c d") 'svn-status-show-svn-diff)
(global-set-key (kbd "C-c e") 'erc-track-switch-buffer)
(global-set-key (kbd "C-c f") 'ff-find-other-file)
(global-set-key (kbd "C-c g") 'grep-find)
(global-set-key (kbd "C-c i") 'clang-format-buffer)
(global-set-key (kbd "C-c m") 'magit-status)
(global-set-key (kbd "C-c r") 'replace-string)
(global-set-key (kbd "C-c s") 'sort-lines)
(global-set-key (kbd "C-c t") 'toggle-truncate-lines)
(global-set-key (kbd "C-c u") 'uncomment-region)
(global-set-key (kbd "C-c w") 'rotate-windows)

;;; File modes

(add-hook 'prog-mode-hook #'rainbow-delimiters-mode)

;; C/C++

(defconst my-c-style
  '((c-block-comment-prefix            . "   ")
    (c-comment-only-line-offset        . 0)
    (c-auto-align-backslashes          . nil)
    (c-label-minimum-indentation       . 0)
    (c-hungry-delete-key               . t)
    (c-indent-comments-syntactically-p . nil)
    (c-hanging-braces-alist . ((brace-list-open after)
                               (defun-open after)
                               (defun-close before after)
                               (class-open after)
                               (class-close before after)
                               (namespace-open after)
                               (inline-open after)
                               (inline-close before after)
                               (block-open after)
                               (block-close . c-snug-do-while)
                               (extern-lang-open after)
                               (extern-lang-close after)
                               (statement-case-open after)
                               (substatement-open after)))
    (c-hanging-colons-alist . ((case-label)
                               (label after)
                               (access-label after)
                               (member-init-intro before)
                               (inher-intro)))
    (c-hanging-semi&comma-criteria . (c-semi&comma-no-newlines-for-oneline-inliners
                                      c-semi&comma-inside-parenlist
                                      c-semi&comma-no-newlines-before-nonblanks))
    (c-cleanup-list . (brace-catch-brace
                       brace-else-brace
                       brace-elseif-brace
                       compact-empty-funcall
                       defun-close-semi
                       empty-defun-braces
                       list-close-comma
                       one-liner-defun
                       scope-operator))
    (c-offsets-alist . ((func-decl-cont       . ++)
                        (member-init-intro    . +)
                        (inher-intro          . ++)
                        (comment-intro        . 0)
                        (arglist-close        . c-lineup-arglist)
                        (topmost-intro        . 0)
                        (block-open           . 0)
                        (inline-open          . 0)
                        (substatement-open    . 0)
                        (label                . /)
                        (case-label           . 0)
                        (statement-case-open  . +)
                        (statement-case-intro . +)
                        (access-label         . -)
                        (innamespace          . 0)
                        (inextern-lang        . 0)
                        (extern-lang-open     . 0)
                        (label                . -)))))

(c-add-style "drobilla" my-c-style)

(defun my-c-mode-common-hook ()
  "C mode for people with taste."
  (require 'smarttabs)
  (auto-fill-mode 1)
  (abbrev-mode -1)
  (set-fill-column 79)
  (smart-tabs-mode-enable)
  (setq tab-width 4)
  (setq indent-tabs-mode t)
  (setq truncate-lines t)
  (setq show-trailing-whitespace t)
  (smart-tabs-advice c-indent-line c-basic-offset)
  (c-set-style "drobilla"))

(add-hook 'c-mode-common-hook   'my-c-mode-common-hook)
(add-hook 'c++-mode-common-hook 'my-c-mode-common-hook)

;; Python

(add-hook 'python-mode-hook
          (lambda ()
            (setq tab-width 4)
            (setq indent-tabs-mode nil)
            (setq show-trailing-whitespace)))

;; PDF

(defun my-doc-view-hook ()
  (auto-revert-mode)
  (blink-cursor-mode -1))

(add-hook 'doc-view-mode-hook 'my-doc-view-hook)
(add-hook 'pdf-view-mode-hook 'my-doc-view-hook)

(spaceline-define-segment line-column
  "The current line and column numbers."
  (if (eq major-mode 'pdf-view-mode)
      ;; Show page number and count for PDF documents
      (concat (number-to-string (pdf-view-current-page))
              "/"
              (number-to-string (pdf-cache-number-of-pages)))
    mode-line-position
    "%l:%c"))

;; Associations

(add-to-list 'auto-mode-alist '("\\.pl\\'" . prolog-mode))
(add-to-list 'auto-mode-alist '("\\.n3"    . n3-mode))
(add-to-list 'auto-mode-alist '("\\.ttl"   . n3-mode))
(add-to-list 'auto-mode-alist '("\\.owl"   . n3-mode))
(add-to-list 'auto-mode-alist '("\\.ll"    . llvm-mode))
(add-to-list 'auto-mode-alist '("\\.md"    . markdown-mode))
(add-to-list 'auto-mode-alist '("\\.pdf"   . pdf-view-mode))

;;; Launch

(spaceline-spacemacs-theme)
(server-start)