; -*- emacs-lisp -*-
;

(message "checking for local setup and loading..")
(if (file-exists-p "/local/skel/all.emacs")
    (load "/local/skel/all.emacs" nil t t))

(message "loading .emacs-standard")
(load "~/.emacs-standard")

(cw/emacs-minimum-version 24
 (require 'package)
 (add-to-list 'package-archives
	      '("melpa" . "https://melpa.org/packages/"))
 (package-initialize))

(setq vc-follow-symlinks t
      custom-file (expand-file-name "~/.emacs-custom"))

(cw/requiring-forms nil
  (message "checking for local setup and loading..")
  (if (file-exists-p "/local/skel/all.emacs")
      (load "/local/skel/all.emacs" nil t t))
  (if (file-exists-p custom-file)
      (load custom-file nil t t)))

(if window-system (progn
		    (add-to-list 'default-frame-alist '(foreground-color . "black"))
		    (add-to-list 'default-frame-alist '(background-color . "white"))))

(require 'jka-compr)
(auto-compression-mode t)

(cw/for-emacs
 (if (string-match "[Uu][Tt][Ff]-?8" (or (getenv "LC_ALL")
                                         (getenv "LANG")
                                         (format "")))
     (progn
       (set-language-environment "UTF-8") ; UTF-8 mode
       (if (not window-system)
           (progn
             (set-keyboard-coding-system 'utf-8)
             (set-terminal-coding-system 'utf-8))))
   (set-language-environment "Latin-1")))

(cw/emacs-minimum-version 30
  (when (eq window-system 'x)
    (setq x-gtk-use-native-input t)))

;(prefer-coding-system "utf-8")

(add-to-list 'Info-default-directory-list (expand-file-name "~/usr/info"))

(add-to-list 'load-path (expand-file-name "~/data/emacs/lisp"))
(add-to-list 'load-path (expand-file-name "~/svn/elisp"))

(cw/requiring-package (loudbel)
  (loudbel-global-mode 1)
  (add-hook 'message-setup-hook
	    (lambda ()  (set (make-local-variable 'completion-ignore-case) t))))

(setq default-major-mode 'text-mode
      mail-user-agent 'gnus-user-agent
      read-mail-command 'gnus
      visible-bell t)

(add-hook 'text-mode-hook 'turn-on-auto-fill)
(add-hook 'debian-changelog-mode-hook 'turn-on-auto-fill)

(fset 'yes-or-no-p 'y-or-n-p)

(global-set-key "\M-n" 'browse-url-at-point)
;(global-set-key (kbd "C-x C-c")
;#'(lambda () (interactive)
;    (if (and window-system (string-match "yiwaz\\|thosu" system-name))
;        (message "you must be kidding")
;      (save-buffers-kill-emacs))))

(global-set-key [(control backspace)] 'undo)
(global-set-key [(control return)] 'find-tag)

(setq browse-url-browser-function 'browse-url-generic
      browse-url-new-window-p t
      browse-url-mozilla-program (or (executable-find "firefox-3.0") "firefox")
      browse-url-generic-program "chromium")

;; File modes

(autoload 'css-mode "css-mode")

(add-to-list 'auto-mode-alist '("\\.css$" . css-mode))
(add-to-list 'auto-mode-alist '("\\.jl$" . lisp-mode))
(add-to-list 'auto-mode-alist '("\\.sls$" . yaml-mode))

;; Set up autoloading and auto-mode
(autoload 'ps-mode "ps-mode"
  "Major mode for editing PostScript" t)
(setq auto-mode-alist
      (append
       '(("\\.[eE]?[pP][sS]$" . ps-mode))
       auto-mode-alist))

;; Various programming settings and languages
; K&R
(setq perl-indent-level 8
      perl-continued-statement-offset 8
      perl-continued-brace-offset 0
      perl-brace-offset -8
      perl-brace-imaginary-offset 0
      perl-label-offset -8)

(setq next-line-add-newlines nil
      compilation-window-height 10
      diff-switches "-u"
      calendar-week-start-day 1
      ; from https://github.com/unhammer/calendar-norway.el/blob/master/calendar-norway.el
      ; «ons. 2. mai 2012»
      calendar-date-display-form '((if dayname
				       (concat dayname ", "))
				   day ". " monthname " " year)
      calendar-time-display-form '(24-hours ":" minutes)
      calendar-day-name-array ["søndag" "mandag" "tirsdag" "onsdag" "torsdag" "fredag" "lørdag"]
      calendar-month-name-array ["januar" "februar" "mars" "april" "mai" "juni"
				 "juli" "august" "september" "oktober" "november" "desember"])

(with-eval-after-load 'calendar
  (calendar-set-date-style 'european))

(setq py-honor-comment-indentation nil)

(add-hook 'sgml-mode-hook '(lambda()(local-set-key "\C-c>" 'sgml-insert-end-tag)))
;(add-hook 'emacs-lisp-mode-hook '(lambda()(local-set-key "\C-j" 'find-function)))
;(require 'template)
;(add-hook 'c-mode-common-hook 'tmpl-init)
;(add-hook 'c-mode-common-hook 'auto-insert-file-header)
(add-hook 'c-mode-common-hook (lambda() (local-set-key "\M-\C-x" 'compile)))
(add-hook 'c-mode-common-hook (lambda() (local-set-key "\M-\C-z" 'next-error)))

(cw/requiring-package (auto-header))

(setq header-full-name "Tollef Fog Heen"
      header-email-address "tfheen@err.no"
      add-log-mailing-address "tfheen@err.no"
      debian-changelog-mailing-address "tfheen@debian.org"
      tags-table-list '("~/usr/include/"))

(setq sgml-local-catalogs (list "~/usr/lib/sgml/sgml.catalog"))

;; Find-file hooks
; Stolen from Jan Ingvoldstad
(add-hook 'find-file-hooks
	  '(lambda ()
	  ;; Invoke proper modes when we don't know file extensions
	  (cond ((looking-at "#!.*/perl") (perl-mode))
		((looking-at "#!.*/tclsh") (tcl-mode))
		((looking-at "#!.*/wish") (tcl-mode))
		((looking-at "#!.*/make") (makefile-mode)))))

;; Visual fluff.

(cw/for-emacs
 (if (functionp 'toggle-scroll-bar)
     (progn
       (toggle-scroll-bar -1)
       (setq scrollbars-visible-p nil))))

(setq scroll-bar-mode nil)
; (set-default-font "fixed")

;      show-paren-style 'expression)

; Highlight matching/nonmatching parens
;(if (not xemacsp)
;    (show-paren-mode t)) ; show-paren-mode takes too much cpu
(cw/for-emacs
 (global-font-lock-mode t)
 (font-lock-mode 1))

;(cw/requiring-package
;    (font-lock)
;  (set-face-foreground (find-face font-lock-string-face) "Blue"))

(cw/requiring-package (tramp)
  (setq tramp-auto-save-directory (expand-file-name "~/tmp")
        tramp-default-method "rsync"))

;;         (setq tramp-multi-file-name-structure (quote ("\\`/r:\\(\\([a-z0-9]+\\)\\)?\\(\\(%s\\)+\\):\\(.*\\)\\'" 2 3 -1))
;;                             tramp-make-tramp-file-format "/r:%m/%u@%h:%p"
;;                             tramp-file-name-regexp "\\`/r:"
;;                             tramp-make-multi-tramp-file-format (quote ("/r:%m" "/%m:%u@%h" ":%p"))
;;                             tramp-make-tramp-file-user-nil-format "/r:%m/%h:%p"
;;                             tramp-file-name-structure '("\\`/r:\\(\\([a-zA-Z0-9]+\\)/\\)?\\(\\([-a-zA-Z0-9_#/:]+\\)@\\)?\\([-a-zA-Z0-9_#/:@.]+\\):\\(.*\\)\\'" 2 4 5 6)))


(cw/for-emacs-21-and-later
 (blink-cursor-mode 0)
 (if window-system
     (tooltip-mode 0))
 (if window-system
     (tool-bar-mode 0))
 (global-set-key [home] 'beginning-of-buffer)
 (global-set-key [end] 'end-of-buffer)
 (menu-bar-mode 0)
 (setq cursor-in-non-selected-windows nil))

(add-hook 'after-change-major-mode-hook (lambda() (electric-indent-mode -1)))

(cw/requiring-package (go-mode)
  (message "Golang-mode")
  (add-hook 'before-save-hook 'gofmt-before-save))

(global-set-key [C-tab] 'dabbrev-expand)
(global-set-key "\M- " 'hippie-expand)


;(add-hook 'diary-hook 'appt-make-list)
;(add-hook 'diary-hook 'appt-make-list)
;(diary 0)

; Mode line
(setq display-time-format "%H:%M"
      display-time-string-forms '(24-hours ":" minutes))
(display-time)

(cw/for-emacs-22-and-later
 (setq inhibit-splash-screen t)
 (setq inhibit-startup-echo-area-message "tfheen")
)

(cw/for-emacs-23-and-later
 (setq line-move-visual nil))

(setq show-trailing-whitespace t)
;(setq-default indent-tabs-mode nil)
(cw/for-emacs-21-and-later
 (define-coding-system-alias 'iso885915 'iso-8859-15)
 (define-coding-system-alias 'utf8 'utf-8)
 (define-coding-system-alias 'iso-8859-15 'iso-8859-1))

(setq add-log-full-name "Tollef Fog Heen"
      add-log-time-format 'tfheen-dpkg-changelog-time)

(defun tfheen-dpkg-changelog-time ()
  (let* ((time (decode-time))
        (sec (first time))
        (minute (second time))
        (hour (third time))
        (day (fourth time))
        (month (fifth time))
        (year (sixth time))
        (dow (seventh time))
        (dst (eighth time))
        (zone (second (current-time-zone)))
        (daynames '("Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun"))
        (monthnames '("Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"))
        (dayname nil)
        (monthname nil))
    (setq dayname (nth (- dow 1) daynames)
          monthname (nth (- month 1) monthnames))
    (format "%s %s %d %2d:%2d:%2d %s %d" dayname monthname day hour minute sec zone year)))

(setq mouse-yank-at-point t)

;: WL setup

(setq elmo-imap4-default-stream-type 'ssl
      elmo-imap4-default-server "mail.raw.no"
      elmo-imap4-default-port 993)
;      wl-folder-access-subscribe-alist '(("^%" . (t . "."))))
(setq elmo-imap4-default-authenticate-type 'clear
      elmo-default-authenticate-type 'clear)

(put 'downcase-region 'disabled nil)

(defun linux-c-mode ()
  "C mode with adjusted defaults for use with the Linux kernel."
  (interactive)
  (c-mode)
  (c-set-style "K&R")
  (setq tab-width 8)
  (setq indent-tabs-mode t)
  (setq c-basic-offset 8))

(cw/for-emacs-22-and-later
 (setq org-return-follows-link t
       org-todo-keywords '((sequence "TODO" "WAITING" "|" "DONE(!)" "CANCELED"))
       org-agenda-include-diary t
       org-agenda-include-all-todo t
       org-tags-column (* -1 (- (frame-width) 10))
       org-agenda-align-tags-to-column (- (frame-width) 10)
       org-hide-leading-stars nil
       org-directory "~/Sync/notes"
       org-agenda-files (mapcar 'expand-file-name (list "~/Sync/notes/todo.org"  "~/Sync/notes/calendar.org"))
       org-default-notes-file (concat org-directory "/todo.org")
       org-stuck-projects '("+LEVEL=2-neverstuck/-DONE" ("TODO" "NEXT" "NEXTACTION" "") ("APPOINTMENT") "")
       org-clock-out-when-done nil
       org-clock-persist t
       org-clock-into-drawer t)
 (add-hook 'org-mode-hook (lambda() (local-set-key (kbd "M-i") 'tfheen-org-insert-checkbox)))
 (add-hook 'org-mode-hook (lambda() (auto-revert-mode t)))

(setq org-capture-templates
      '(("t" "Todo" entry (file+headline "~/Sync/notes/todo.org" "Mind sweep/INBOX")
         "* TODO %?\n  %i\n  %a")
        ("c" "Cognite daily questions" entry (file "~/Sync/notes/cognite/notes.org")
         "* %u Morning / strategic / standup
** strategic thinking (gtd, https://www.lenareinhard.com/articles/how-to-lead-strategically-every-day)
  - areas of focus?
  - What's the most important thing for us?
  - What are we not doing to accomplish it?
  - How can I help my team draw connections between their work and strategy?
  - How am I investing in capabilities we need to meet our strategic goals?

** three things to be grateful for

%?

** standup
*** done

*** to do

*** help/support
")
	("d" "Daily journal" entry (file "~/Sync/notes/journal.org")
         "* %u Daily journal
- three things to be grateful for

%?

")))

 (org-clock-persistence-insinuate)
 (cw/requiring-package (org-bullets)
   (add-hook 'org-mode-hook (lambda () (org-bullets-mode 1)))))

(add-to-list 'auto-mode-alist '("\\.org$" . org-mode))
(global-set-key (kbd "C-c a") 'org-agenda)
(setq org-agenda-custom-commands
      '(("w" todo
	 #("WAITING" 0 7
	   (face org-warning)))
	("h" tags-todo "@hjemme")
	("j" tags-todo "@jobb")
	("n" "Agenda and all TODOs"
	 ((agenda "")
	  (alltodo "")))))

(add-to-list 'window-size-change-functions
	     (lambda (frame) (setq org-tags-column (* -1 (- (frame-width) 10))
				   org-agenda-align-tags-to-column (- (frame-width) 10))))

(cw/for-host "aexonyam"
  (setq gnus-home-directory "~/.emacs.d/gnus"
	gnus-init-file "~/.gnus"
	message-directory (concat gnus-home-directory "/Mail")))

(cw/for-emacs-23-and-later
 (cw/for-os 'darwin
	    (cw/requiring-package (package)
	      (add-to-list 'package-archives
			   '("marmalade" . "http://marmalade-repo.org/packages/") t))))

(cw/for-emacs-23-and-later
 (cw/not-for-os 'darwin
	       ;; Firefox-like zooming of fonts.
	       (setq tfheen-base-size 10
		     tfheen-base-font "DejaVu Sans Mono"
		     tfheen-current-size tfheen-base-size)
	       (defun font-zoom-increase-font-size ()
		 (interactive)
		 (setq tfheen-current-size (+ tfheen-current-size 2))
		 (let ((font-name (format "%s-%d" tfheen-base-font tfheen-current-size)))
		   (set-frame-font font-name)))
	       
	       (defun font-zoom-decrease-font-size ()
		 (interactive)
		 (setq tfheen-current-size (- tfheen-current-size 2))
		 (let ((font-name (format "%s-%d" tfheen-base-font tfheen-current-size)))
		   (set-frame-font font-name)))
	       
	       (defun font-zoom-reset-font-size ()
		 (interactive)
		 (let ((font-name (format "%s-%d" tfheen-base-font tfheen-base-size)))
		   (set-frame-font font-name)))
	       
	       (define-key global-map (read-kbd-macro "C--") 'font-zoom-decrease-font-size)
	       (define-key global-map (read-kbd-macro "C-+") 'font-zoom-increase-font-size)
	       (define-key global-map (read-kbd-macro "C-=") 'font-zoom-reset-font-size)
	       (font-zoom-reset-font-size)))

(defun minutes-to-hours (minutes)
 (when minutes
   (/ (* 1.0 minutes) 60)))

(defun minutes-to-seconds (minutes)
  (* 60 minutes))

(defun weekend-p (time)
  (> (string-to-number (format-time-string "%u" time)) 5))

(defun org-dblock-write:timebalance (params)
  "Display day-by-day summary, giving a balance"
  (let* ((ts (plist-get params :tstart))
         (te (plist-get params :tend))
	 (ins (make-marker))
         (start (time-to-seconds
                 (apply 'encode-time (org-parse-time-string ts))))
         (end (time-to-seconds
               (apply 'encode-time (org-parse-time-string te))))
	 tbl sum)

    (setq params (plist-put params :tstart nil))
    (setq params (plist-put params :end nil))
    (move-marker ins (point))
    (while (<= start end)
      (save-excursion
	(let ((sum 0))
	  (org-clock-sum start (+ start 86400))
	  (goto-char (point-min))
	  (setq st t)
	  (while (or (and (bobp) (prog1 st (setq st nil))
			  (get-text-property (point) :org-clock-minutes)
			  (setq p (point-min)))
		     (setq p (next-single-property-change (point) :org-clock-minutes)))
	    (goto-char p)
	    (message (format "pre: %s %d %d %s" (format-time-string "%Y-%m-%d" (seconds-to-time start)) sum start (point)))
	    (if (= 1 (car (org-heading-components)))
		(setq sum (+ sum (or (get-text-property p :org-clock-minutes) 0)))))
	  (push (list start sum) tbl)))
      (setq start (+ 86400 start)))
    (setq tbl (nreverse tbl))
    (goto-char ins)
    (setq sum 0)
    (while tbl
      (let* ((line (car tbl))
	     (ts (seconds-to-time (first line)))
	     (minutes (second line))
	     (hours (minutes-to-hours minutes)))
	(setq sum (+ sum hours))
	(if (not (weekend-p ts))
	    (setq sum (- sum 7.5)))
	(if (> minutes 0)
	    (insert-before-markers
	     (format-time-string "%Y-%m-%d" ts)
	     (format " %f %f\n" hours sum))))
	(setq tbl (cdr tbl)))))

(defun tfheen-round-invoice-hours (hours)
  (when hours
    (/ (ceiling (* hours 4)) 4.0)))

(defun tfheen-round-invoice-minutes (minutes)
  (when minutes
    (* 15 (ceiling minutes 15))))

(defun org-dblock-write:invoicerounding (params)
  "Grab hours in a format suitable for an invoice, rounding all times to a full hour"
  (let* ((ts (plist-get params :tstart))
         (te (plist-get params :tend))
	 (ins (make-marker))
         (start (time-to-seconds
                 (apply 'encode-time (org-parse-time-string ts))))
         (end (time-to-seconds
               (apply 'encode-time (org-parse-time-string te))))
	 tbl sum)

    (setq params (plist-put params :tstart nil))
    (setq params (plist-put params :end nil))
    (move-marker ins (point))
    (while (<= start end)
      (save-excursion
	(let ((sum 0))
	  (org-clock-sum start (+ start 86400))
	  (goto-char (point-min))
	  (setq st t)
	  (while (or (and (bobp) (prog1 st (setq st nil))
			  (get-text-property (point) :org-clock-minutes)
			  (setq p (point-min)))
		     (setq p (next-single-property-change (point) :org-clock-minutes)))
	    (goto-char p)
	    (if (= 1 (car (org-heading-components)))
		(setq sum (+ sum (or (get-text-property p :org-clock-minutes) 0)))))
	  (push (list start sum) tbl)))
      (setq start (+ 86400 start)))
    (setq tbl (nreverse tbl))
    (goto-char ins)
    (setq sum 0)
    (while tbl
      (let* ((line (car tbl))
	     (ts (seconds-to-time (first line)))
	     (minutes (second line))
	     (hours (tfheen-round-invoice-hours (minutes-to-hours minutes))))
	(setq sum (+ sum hours))
	(if (> minutes 0)
	    (insert-before-markers
	     (format-time-string "%Y-%m-%d" ts)
            (format " %.2f\n" hours))))
      (setq tbl (cdr tbl)))
    (insert-before-markers "---------------\n")
    (insert-before-markers (format "Sum %11.2f" sum))))

(defun org-dblock-write:gtimelog (params)
  "Display day-by-day time reports in gtimelog format."
  (let* ((ts (plist-get params :tstart))
         (te (plist-get params :tend))
	 (ins (make-marker))
         (maxlevel (plist-get params :maxlevel))
         (start (time-to-seconds
                 (apply 'encode-time (org-parse-time-string ts))))
         (end (time-to-seconds
               (apply 'encode-time (org-parse-time-string te))))
	 tbl day-numbers heading1-label)

    (setq params (plist-put params :tstart nil))
    (setq params (plist-put params :end nil))
    (move-marker ins (point))
    (while (<= start end)
      (save-excursion
	(org-clock-sum start (+ start 86400))
	(goto-char (point-min))
	(setq st t)
	(while (or (and (bobp) (prog1 st (setq st nil))
			(get-text-property (point) :org-clock-minutes)
			(setq p (point-min)))
		   (setq p (next-single-property-change (point) :org-clock-minutes)))
	  (goto-char p)
	  (when (setq time (tfheen-round-invoice-minutes (get-text-property p :org-clock-minutes)))
	    (save-excursion
	      (message "time '%s'" (get-text-property p :org-clock-minutes))
	      (beginning-of-line 1)
	      (when (and (looking-at "^\\(\\*+\\)[ \t]+\\(.*?\\)\\([ \t]+:[[:alnum:]_@:]+:\\)?[ \t]*$")
			 (setq level (org-reduced-level
				      (- (match-end 1) (match-beginning 1))))
			 (<= level maxlevel))
		(if (= level 1)
		    (setq heading1-label (match-string 2))
		  (push (list
		       start
		       (concat heading1-label ": " (match-string 2))
		       time) tbl)))))))
      (setq start (+ 86400 start)))
    (setq tbl (nreverse tbl))
    (goto-char ins)
    (setq ts 0)
    (while tbl
      (let ((line (car tbl)))
	(if (< ts (car line))
	    ; New day
	    (progn
	      (insert-before-markers
	       (format-time-string "%Y-%m-%d %H:%M %z"
				   (seconds-to-time (+ 21600 (first line))))
	       ": Arrive\n")
	      (setq ts (+ 21600 (first line)))))

	(insert-before-markers
	 (format-time-string "%Y-%m-%d %H:%M %z"
			     (seconds-to-time (+ ts (* 60 (third line)))))
	 ": "
	 (format "%s" (second line))
	 "\n")
	(setq ts (+ ts (* 60 (third line))))
	(setq tbl (cdr tbl))))))

;(defadvice current-time (after tfheen-floor-current-time activate disable)
;  (setq ad-return-value (seconds-to-time (* 900 (floor (time-to-seconds ad-return-value) 900)))))
;(defadvice org-clock-in (around tfheen-round-clock activate)
;  (ad-enable-advice 'current-time 'after 'tfheen-floor-current-time)
;  (ad-activate 'current-time)
;  ad-do-it
;  (ad-disable-advice 'current-time 'after 'tfheen-floor-current-time)
;  (ad-activate 'current-time))

;(defadvice current-time (after tfheen-ceiling-current-time activate disable)
;  (setq ad-return-value (seconds-to-time (* 900 (ceiling (time-to-seconds ad-return-value) 900)))))
;(defadvice org-clock-out (around tfheen-round-clock activate)
;  (ad-enable-advice 'current-time 'after 'tfheen-ceiling-current-time)
;  (ad-activate 'current-time)
;  ad-do-it
;  (ad-disable-advice 'current-time 'after 'tfheen-ceiling-current-time)
;  (ad-activate 'current-time))

(defun unfill-paragraph ()
  (interactive)
  (let ((fill-column (point-max)))
    (fill-paragraph nil)))

(defun unfill-region ()
  (interactive)
  (let ((fill-column (point-max)))
    (fill-region (region-beginning) (region-end) nil)))

(define-key global-map "\M-Q" 'unfill-paragraph)
(define-key global-map "\C-\M-q" 'unfill-region)

(setq twittering-use-native-retweet t
      twittering-use-icon-storage t
      twittering-icon-mode t)

(setq tfheen-twittering-filter-blacklist
      '((text . "#oslo #planking")
	(text . "#WEBELIEBE")
	(text . "#SurrenderYourSay")
	(text . "I just earned the.*Level.*badge on .untappd")
       (user-screen-name . "\\(Oslo_HitHot\\|Map_Game\\|anbud1\\)")))

(defun tfheen-twitter-filter-search ()
  (interactive)
  (let ((non-matching-statuses ()))
    (dolist (status twittering-new-tweets-statuses)
      (if (eq (cadr (assoc 'source-spec status)) 'search)
	  (let ((matched-tweets 0))
	    (dolist (item tfheen-twittering-filter-blacklist)
	      (let* ((type (car item))
		     (pat (cdr item)))
		(if (string-match pat (cdr (assoc type status)))
		    (setq matched-tweets (+ 1 matched-tweets)))))
	    (if (= 0 matched-tweets)
		(setq non-matching-statuses (append non-matching-statuses `(,status)))))
	(setq non-matching-statuses (append non-matching-statuses `(,status)))))
    (setq new-statuses non-matching-statuses)))

(add-hook 'twittering-new-tweets-hook 'tfheen-twitter-filter-search)

(setq twittering-initial-timeline-spec-string
      '(":home"
	":replies"
	":direct_messages"))

(define-key org-mode-map [home] 'beginning-of-buffer)
(define-key org-mode-map [end]  'end-of-buffer)

(defun tfheen/weekday-p ()
  (let ((wday (nth 6 (decode-time))))
    (and (< wday 6)
	 (> wday 0))))

(defun tfheen/working-p ()
  (let ((hour (nth 2 (decode-time))))
    (and (tfheen/weekday-p)
	 (and (>= hour 7) (<= hour 15)))))

(defun tfheen/org-auto-exclude-function (tag)
  "Automatic task exclusion in the agenda with / RET"
  (and (cond
	((string= tag "@farm")
         t)
        (t
         (if (tfheen/working-p)
             (setq tag "@hjemme")
           (setq tag "@jobb"))
         (unless (member (concat "-" tag) org-agenda-filter)
           tag)))
       (concat "-" tag)))

(setq org-agenda-auto-exclude-function 'tfheen/org-auto-exclude-function)
(setq org-time-clocksum-format (quote (:hours "%d" :require-hours t :minutes ":%02d" :require-minutes t)))

(load (expand-file-name (concat user-emacs-directory "secrets")) t t)
(make-directory (expand-file-name "~/.emacs-autosave/") t)
(setq auto-save-file-name-transforms '((".*" "~/.emacs-autosave/" t)))


(defun tfheen/days-in-month (year month)
  (let* ((days nil)
	 (day 1)
	 (end-time (encode-time 1 1 0 day (+ month 1) year))
	 (time (encode-time 1 1 0 day month year)))
	  (while (time-less-p time end-time)
	    (push day days)
	    (setq day (+ day 1)
		  time (encode-time 1 1 0 day month year)))
	  (nreverse days)))

(defun tfheen/random-element-from-list (list)
  (nth (random (length list)) list))

(defun tfheen/sort-hour-list (a b)
  (< (car a) (car b)))

(defun tfheen/generate-hours (year month hours min-hours max-hours)
  (let ((days (tfheen/days-in-month year month))
	 (sheet nil))
    (while (> hours 0)
      (let ((day (tfheen/random-element-from-list days))
	    (day-hours))
	(if (> hours min-hours)
	    (setq day-hours (+ min-hours (random (- (min max-hours hours) min-hours))))
	  (setq day-hours hours))
	(message "%s %s" day day-hours)
	(push (cons day day-hours) sheet)
	(setq hours (- hours day-hours)
	      days (remove day days))))
    (sort sheet 'tfheen/sort-hour-list)))

(defun tfheen/format-timesheet (year month hours min-hours max-hours)
  (dolist (elem (tfheen/generate-hours year month hours min-hours max-hours))
    (let* ((time-format "%Y-%m-%d %a %H:%M")
	   (day (car elem))
	   (day-hours (cdr elem))
	   (start (encode-time 0 0 8 day month year))
	   (start-string (format-time-string time-format start))
	   (end (seconds-to-time (+ (* day-hours 3600) (time-to-seconds start))))
	   (end-string (format-time-string time-format end)))
      (insert (format "CLOCK: [%s]--[%s] => %2d:00\n" start-string end-string day-hours)))))

(cw/requiring-package (uniquify)
  (setq uniquify-buffer-name-style 'post-forward-angle-brackets))

(setq ido-enable-flex-matching t)
(setq ido-everywhere t)
(ido-mode 1)

(setq frame-title-format "%b %f")

(eval-after-load 'mailcap
  '(setcdr
    (assoc "application" mailcap-mime-data)
    (remove '("pdf"
	      (viewer . doc-view-mode)
	      (type . "application/pdf")
	      (test eq window-system 'x))
	    (cdr (assoc "application" mailcap-mime-data)))))

(if (not window-system)
    nil
  (require 'server)
  (if (server-running-p) nil (server-start)))

(setq message-default-headers (concat "Bcc: tfheen+outgoing" (char-to-string 64) "err.no"))

(cw/requiring-package (atomic-chrome)
  (atomic-chrome-start-server)
  (setq atomic-chrome-buffer-open-style 'frame))

(cw/requiring-package (real-auto-save)
  (add-hook 'org-mode-hook 'real-auto-save-mode))

(cw/requiring-package (notmuch)
  (define-key notmuch-show-mode-map "d"
    (lambda ()
      "toggle spam tag for message"
        (interactive)
        (if (member "spam" (notmuch-show-get-tags))
            (notmuch-show-tag (list "-spam"))
	  (notmuch-show-tag (list "+spam")))))
  (define-key notmuch-search-mode-map "d"
    (lambda (&optional beg end)
      "toggle spam tag for search"
      (interactive (notmuch-search-interactive-region))
        (if (member "spam" (notmuch-search-get-tags))
            (notmuch-search-tag (list "-spam") beg end)
	  (notmuch-search-tag (list "+spam") beg end))))
  (define-key notmuch-tree-mode-map "d"
    (lambda ()
      "toggle spam tag for tree/thread"
      (interactive)
        (if (member "spam" (notmuch-tree-get-tags))
            (notmuch-tree-tag (list "-spam"))
	  (notmuch-tree-tag (list "+spam")))))
  (setq notmuch-show-logo nil))

(add-hook 'before-save-hook 'time-stamp)

(defun ensc/mailcap-mime-data-filter (filter)
  ""
  (mapcar (lambda(major)
        (append (list (car major))
            (remove nil
                (mapcar (lambda(minor)
                      (when (funcall filter (car major) (car minor) (cdr minor))
                    minor))
                    (cdr major)))))
      mailcap-mime-data))

(defun ensc/no-pdf-doc-view-filter (major minor spec)
  (if (and (string= major "application")
       (string= minor "pdf")
       (member '(viewer . doc-view-mode) spec))
      nil
    t))

(eval-after-load 'mailcap
  '(progn
     (setq mailcap-mime-data
       (ensc/mailcap-mime-data-filter 'ensc/no-pdf-doc-view-filter))))

(defun tfheen-save-org-files ()
  (interactive)
  (save-excursion
    (dolist (buf (buffer-list))
      (set-buffer buf)
      (if (and (buffer-file-name) (buffer-modified-p) (string-match "/Sync/.*\\(org$\\|org_done$\\)" (buffer-file-name)))
          (basic-save-buffer)))))

(defun tfheen-org-update-org-ts-expand-auto ()
  "Switch to todo.org, update the VISIBILITY drawer for today"
  (interactive)
  (save-excursion
    (dolist (buf (buffer-list))
      (set-buffer buf)
      (if (and (buffer-file-name)
	       (string-match "/Sync/.*todo.org$" (buffer-file-name))
	       (functionp 'org-timestamp-from-time)
	       (verify-visited-file-modtime))
	  (tfheen-org-expand-today-automatically)))))

(defun tfheen-org-expand-today-automatically ()
  "Mark today's first entry as expand-automatically, so it looks more ergonomic"
  (interactive)
  (let ((look-for-time (org-timestamp-format (org-timestamp-from-time (current-time))
					     (car org-time-stamp-formats))))
    (save-excursion
      (goto-char (point-min))
      (search-forward look-for-time nil 'noerr)
      (if (not (= (point) (point-max)))
	  (org-set-property "VISIBILITY" "all")))))

(defun t/splay (n splay)
  (+ n (random splay)))

(run-at-time "00:01" (t/splay 86400 100) 'tfheen-org-update-org-ts-expand-auto)
(run-with-timer (t/splay 600 100) (t/splay 600 100) 'tfheen-org-update-org-ts-expand-auto)
(run-with-idle-timer (t/splay 300 100) (t/splay 600 300) 'tfheen-save-org-files)
(define-key special-event-map [sigusr1] #'tfheen-save-org-files)

(defun tfheen-org-maybe-move-point ()
  "Look for today's timestamp and move to it, if it exists"
  (interactive)
  (let ((look-for-time (org-timestamp-format (org-timestamp-from-time (current-time))
					     (car org-time-stamp-formats))))
    (if (save-excursion
	  (goto-char (point-min))
	  (search-forward look-for-time nil 'noerr)
	  (not (= (point) (point-max))))
	(progn
	  (goto-char (point-min))
	  (search-forward look-for-time nil 'noerr)
	  (forward-char)
	  (while (org-at-drawer-p)
	    (forward-char))))))

(add-hook 'org-mode-hook 'tfheen-org-maybe-move-point)

(defun tfheen-org-remove-blank-lines-in-checklist ()
  "Make today's checklist be a bit more ordered and spaced"
  (interactive)
  (let ((look-for-time (org-timestamp-format (org-timestamp-from-time (current-time))
					     (car org-time-stamp-formats))))
    (save-excursion
      (goto-char (point-min))
      (search-forward look-for-time nil 'noerr)
      (if (not (= (point) (point-max)))
	  (progn
	    (forward-char)
	    (if (org-at-drawer-p) (org-flag-drawer t))
	    (while (not (org-at-item-p))
	      (forward-line))
	    ; Sort list, to make insertion of "\n" useful
	    (org-sort-list nil ?X)
	    ;; Skip any checked boxes
	    (if (looking-at "^- \\[X")
		(progn
		  (while (looking-at "^- \\[X")
		    (progn
		      (org-end-of-item)
		      (if (looking-back "\n\n")
			  (delete-backward-char 1))))
		  (if (looking-at "\n- \\[X") (kill-line))
		  (while (looking-at "^- \\[X")
		    (progn
		      (org-end-of-item)
		      (if (looking-back "\n\n")
			  (delete-backward-char 1))))
		  (if (looking-at "- \\[ ") (open-line 1)))))))))

(defun tfheen-org-update-org-fix-checklist ()
  "Switch to todo.org, clean up checklist"
  (interactive)
  (save-excursion
    (dolist (buf (buffer-list))
      (set-buffer buf)
      (if (and (buffer-file-name)
	       (not (file-locked-p (buffer-file-name)))
	       (string-match "/Sync/.*todo.org$" (buffer-file-name))
	       (functionp 'org-timestamp-from-time)
	       (verify-visited-file-modtime))
	  (tfheen-org-remove-blank-lines-in-checklist)))))

(run-with-idle-timer 60 t 'tfheen-org-update-org-fix-checklist)

(defun tfheen-org-insert-checkbox ()
    "org-insert-item, or if that fails, start a new checklist"
  (interactive)
  (if (not (org-insert-item t))
      (progn
	(save-excursion
	  (beginning-of-line)
	  (if (looking-at "\\*")
	    (open-line 1))
	  (insert "- [ ] "))
	(end-of-line)
	(if (not (looking-at "\n\n"))
	    (open-line 1)))))

(global-set-key (kbd "C-c r") #'org-capture)

(setq large-file-warning-threshold (* 100 1024 1024))

(defun org-summary-todo (n-done n-not-done)
  "Switch entry to DONE when all subentries are done, to TODO otherwise."
  (let (org-log-done org-todo-log-states)   ; turn off logging
    (org-todo (if (= n-not-done 0) "DONE" "TODO"))))
(add-hook 'org-after-todo-statistics-hook #'org-summary-todo)

(use-package corfu
   :hook (after-init . global-corfu-mode)
   :custom
   (corfu-cycle t) ; cycle around to first entry after reaching the last
   (corfu-preview-current nil) ; don't expand text at point until I press return
   (corfu-on-exact-match 'insert) ; complete if there is only a single candidate
   (setq completion-styles '(flex partial-completion basic))

;;   (corfu-quit-no-match t)
;;   (corfu-quit-at-boundary t)
;;   :config
;;   (setq corfu-popupinfo-delay '(1.25 . 0.5))
;;   (corfu-popupinfo-mode 1) ; shows documentation next to completions

;;   ;; sort by input history
;;   (with-eval-after-load 'savehist
;;     (corfu-history-mode 1)
;;     (add-to-list 'savehist-additional-variables 'corfu-history))
   )
