;;; jao-afio.el --- workspaces in just one frame  -*- lexical-binding: t; -*-

;; Copyright (C) 2020, 2021  jao

;; Author: jao <mail@jao.io>
;; Keywords: frames

;; 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/>.

;;; Code:

(defvar jao-open-doc-fun 'find-file)
(defvar jao-afio-mail-function 'gnus)
(defvar jao-afio-use-w3m nil)
(defvar jao-afio-notmuch-in-web nil)
(defvar jao-afio-switch-hook nil)

(defvar jao-afio--configs '(?c ?w ?g ?p ?s))
(defvar jao-afio--current-config (car jao-afio--configs))
(defvar jao-afio--locker nil)
(defvar jao-afio-fallback-fun nil)

(defun jao-afio--check-frame-p ()
  (assoc 'afio (frame-parameters)))

(defun jao-afio--init (&optional f)
  (interactive)
  (when (and (frame-live-p jao-afio--locker)
             (not (eql f jao-afio--locker)))
    (if jao-afio-fallback-fun
        (funcall jao-afio-fallback-fun)
      (error "Another frame is using afio")))
  (setq jao-afio--locker f)
  (modify-frame-parameters f '((afio . t)))
  (setq jao-afio--current-config ?c)
  (mapc (lambda (r) (set-register r nil)) jao-afio--configs)
  (window-configuration-to-register ?c))

(defun jao-afio--steal ()
  (interactive)
  (setq jao-afio--locker nil)
  (jao-afio--init (window-frame (get-buffer-window (current-buffer)))))

(defun jao-afio--check-frame ()
  (unless (jao-afio--check-frame-p)
    (or (when jao-afio-fallback-fun
          (funcall jao-afio-fallback-fun)
          t)
        (when (y-or-n-p "Another frame is using afio. Steal? ")
          (jao-afio--steal)
          t)
        (error "Aborted"))))

(defun jao-afio--next-frame ()
  (interactive)
  (jao-afio--check-frame)
  (let* ((cur (member jao-afio--current-config jao-afio--configs))
         (next (or (cadr cur) (car jao-afio--configs))))
    (jao-afio--goto-frame next)))

;;;###autoload
(defun jao-afio-open-pdf-session ()
  (interactive)
  (let ((jao-doc-view-inhibit-session-save t))
    (dolist (doc (jao-doc-view-session))
      (when (and (file-exists-p doc) (y-or-n-p (format "Open %s? " doc)))
        (jao-open-doc doc)))))

(defun jao-afio-open-doc ()
  (interactive)
  (delete-other-windows)
  (split-window-right)
  (let ((docs (remove-if-not (lambda (b)
                               (with-current-buffer b (jao-doc-view--is-pdf)))
                             (buffer-list))))
    (if (car docs)
        (progn (switch-to-buffer (car docs))
               (switch-to-buffer-other-window (or (cadr docs) (car docs))))
      (when (and (jao-doc-view-session) (y-or-n-p "Load saved session? "))
        (jao-afio-open-pdf-session)))))

(declare w3m "w3m")
(declare w3m-alive-p "w3m")
(declare w3m-previous-buffer "w3m")
(declare notmuch "notmuch")

;;;###autoload
(defun jao-afio-open-www ()
  (interactive)
  (require 'jao-eww-session)
  (if (< (frame-width) 160)
      (if jao-afio-use-w3m (w3m) (jao-eww-session-load))
    (delete-other-windows)
    (split-window-right)
    (if jao-afio-use-w3m
        (w3m)
      (jao-eww-session-load)
      (let ((b (current-buffer)))
        (other-window 1)
        (if jao-afio-notmuch-in-web
            (notmuch)
          (switch-to-buffer (car (jao-eww-session--list-buffers b))))
        (other-window 1)))))

;;;###autoload
(defun jao-afio-open-gnus ()
  (interactive)
  (delete-other-windows)
  (org-agenda-list)
  (calendar)
  (find-file (expand-file-name "inbox.org" org-directory))
  (gnus)
  (jao-gnus--set-summary-line))

;;;###autoload
(defun jao-afio-open-mail ()
  (interactive)
  (if (or (null jao-afio-mail-function) (eq 'gnus jao-afio-mail-function))
      (jao-afio-open-gnus)
    (calendar)
    (jao-trisect)
    (other-window 2)
    (delete-window)
    (other-window 1)
    (funcall jao-afio-mail-function)
    (other-window 1)
    (find-file (expand-file-name "inbox.org" org-directory))
    (split-window-below (/ (window-height) 3))
    (other-window 1)
    (org-agenda-list)
    (split-window-below -9)
    (other-window 1)
    (switch-to-buffer "*Calendar*")
    (other-window 1)))

(defun jao-afio--goto-frame (next &optional reset)
  (when (or reset (not (eq next jao-afio--current-config)))
    (let ((next-cfg (when (not reset) (get-register next))))
      (window-configuration-to-register jao-afio--current-config)
      (setq jao-afio--current-config next)
      (if next-cfg
          (jump-to-register next)
        (delete-other-windows)
        (cl-case next
          (?w (jao-afio-open-www))
          (?g (jao-afio-open-mail))
          (?p (jao-afio-open-doc))
          (?s (delete-other-windows))))
      (run-hooks 'jao-afio-switch-hook))))

(defun jao-afio--goto-main (&optional reset)
  (interactive "P")
  (jao-afio--check-frame)
  (jao-afio--goto-frame ?c reset))

(defun jao-afio--goto-scratch (&optional reset)
  (interactive "P")
  (jao-afio--check-frame)
  (jao-afio--goto-frame ?s reset))

(defun jao-afio--goto-mail (&optional reset)
  (interactive "P")
  (jao-afio--check-frame)
  (jao-afio--goto-frame ?g reset))

(defun jao-afio--goto-docs (&optional reset)
  (interactive "P")
  (jao-afio--check-frame)
  (jao-afio--goto-frame ?p reset))

(defun jao-afio--goto-www (&optional reset)
  (interactive "P")
  (if (jao-afio--check-frame-p)
      (jao-afio--goto-frame ?w reset)
    (when (and jao-afio-use-w3m (w3m-alive-p))
      (pop-to-buffer (w3m-alive-p)))))

(defun jao-afio--try-init (&optional f)
  (ignore-errors (jao-afio--init f))
  t)

(defun jao-afio--goto-www-buffer (buf &rest _)
  (jao-afio--goto-www)
  (jao-first-window)
  (switch-to-buffer buf nil t))

(defun jao-afio--goto-pdf-buffer (buf &rest _)
  (if (jao-afio--check-frame-p)
      (progn (jao-afio--goto-docs)
             (jao-first-window)
             (switch-to-buffer buf nil t))
    (pop-to-buffer buf)))

(defun jao-afio-goto-scratch (&optional one-win)
  (jao-afio--goto-scratch)
  (when one-win (delete-other-windows)))

(defun jao-afio-current-frame ()
  (cl-case jao-afio--current-config
    (?c "Main")
    (?s "Scratch")
    (?g "Mail")
    (?p "Docs")
    (?w "Web")))

(defun jao-afio-current-no ()
  (cl-case jao-afio--current-config
    (?c "1")
    (?s "0")
    (?g "2")
    (?p "4")
    (?w "3")))

;;;###autoload
(defun jao-afio-setup (&optional fallback-fun init-p)
  (global-set-key "\C-cf" 'jao-afio--goto-main)
  (global-set-key "\C-cg" 'jao-afio--goto-mail)
  (global-set-key "\C-cw" 'jao-afio--goto-www)
  (global-set-key "\C-cz" 'jao-afio--goto-docs)
  (setq jao-afio-fallback-fun fallback-fun)
  (add-hook (if init-p 'after-init-hook 'after-make-frame-functions)
            'jao-afio--try-init))

(provide 'jao-afio)
;;; jao-afio.el ends here