;;;;
;;;;;; Version Control / Ediff
;;;;

(defadvice ediff-window-display-p (after eaw-ediff-window-display-p)
  "Force window display instead of frame display"
  (setq ad-return-value nil))
(ad-activate 'ediff-window-display-p)

;; ediff should use a smaller diff
(setq ediff-diff-options "-d")

;; ediff should split horizontally
(setq ediff-split-window-function 'split-window-horizontally)

;; ediff shouldn't create a new frame
(setq ediff-window-setup-function 'ediff-setup-windows-plain)

;; perforce integration
(setq p4-use-p4config-exclusively t
      p4-follow-symlinks t
      p4-verbose nil
      p4-window-config-stack-size 1
      p4-default-diff-options "-ddwupU8")

(defun eaw-p4-changes ()
  (interactive)
  (p4-file-change-log "changes"
                      (p4-make-list-from-string
                       (concat "-s pending -c " (p4-current-client)))))

(defun eaw-p4-interchanges (args)
  (interactive "sp4 interchanges ")
  (p4-file-change-log "interchanges"
                      (p4-make-list-from-string
                       (concat "-l " args))))

(defun eaw-p4-changes-comp (string predicate action)
  (let (line list)
    (if (eq (compare-strings string 0 (length string)
                             "default" 0 (length string)) t)
        (setq list (cons "default" list)))
    (with-temp-buffer
      (p4-exec-p4 (current-buffer) (list "changes" "-s" "pending" "-c" (p4-current-client)) t)
      (while (setq line (p4-read-depot-output (current-buffer) "^Change \\([0-9]\+\\) .*$"))
        (if (eq (compare-strings string 0 (length string)
                                 line 0 (length string)) t)
            (setq list (cons line list)))))
    list))

(defun eaw-p4-opened (arg)
  (interactive "P")
  (let (args)
    (unless current-prefix-arg
      (setq args (list "-c" (completing-read "P4 change (default: default): " 'eaw-p4-changes-comp
                                             nil t nil nil "default"))))
    (p4-opened-internal args)))

(defun eaw-p4-submit (arg)
  (interactive "P")
  (if current-prefix-arg
      (p4-submit)
    (let* ((change (completing-read "P4 submit change (default: default): " 'eaw-p4-changes-comp
                                    nil t nil nil "default"))
           (submit-buf-name (concat "*P4 Submit " change "*"))
           args)
      (setq args (if (not (string= "default" change)) (list change)))
      (p4-save-opened-files)
      (if (or (not (and p4-check-empty-diffs (p4-empty-diff-p)))
              (yes-or-no-p "File with empty diff opened for edit. Submit anyway? "))
          (p4-async-process-command "change" "Description:\n\t"
                                    submit-buf-name "submit" args)))))

(defun eaw-p4-ediff-all-opened (change)
  "Use ediff to compare all opened files in a changeset
with their previous versions."
  (interactive (list (completing-read "P4 change (default: default): "
                                      'eaw-p4-changes-comp
                                      nil t nil nil "default")))
  (with-temp-buffer
    (p4-exec-p4 (current-buffer) (list "opened" "-c" change) t)
    (let (line display-eregistry (default-directory (expand-file-name "~/")))
      (while (setq line (p4-read-depot-output (current-buffer)))
        (if (and (string-match "^\\(.*\\)#[[:digit:]]+ - \\([^ ]+\\) .*$" line)
                 (or (string= (match-string 2 line) "edit")
                     (string= (match-string 2 line) "integrate")))
            (let* ((depot (match-string 1 line))
                   (file (cdar (p4-map-depot-files (list depot))))
                   (depot-buffer (get-buffer-create depot))
                   (file-buffer (find-file-noselect file)))
              (message file)
              (setq display-eregistry t)
              ;; can't just use p4-ediff because it doesn't use unique buffer names,
              ;; and can't use rename-uniquely because it operates on the current buffer
              (p4-exec-p4 depot-buffer
                          (list "print" "-q" (concat depot "#have")) t)
              (ediff-buffers depot-buffer file-buffer
                             `((lambda ()
                                 (make-local-variable 'ediff-startup-hook)
                                 (add-hook 'ediff-startup-hook 'ediff-suspend)
                                 (make-local-variable 'ediff-cleanup-hook)
                                 (add-hook 'ediff-cleanup-hook
                                           '(lambda ()
                                              (kill-buffer ,depot-buffer)))))))))
      (if (not display-eregistry)
          (if (string= change "default")
              (message "No edits made in default change")
            (message "No edits made in change %s" change))
        (eregistry)
        (delete-other-windows)))))

(defun eaw-p4-reopen-change (change)
  (interactive (list (completing-read "P4 change (default: default): "
                                      'eaw-p4-changes-comp
                                      nil t nil nil "default")))
  (if (p4-buffer-file-name-2)
      (let ((p4-output-buffer-name "*P4 reopen*"))
        (p4-noinput-buffer-action "reopen" t nil (list "-c" change (p4-buffer-file-name-2)))
        (p4-check-mode)
        (p4-update-opened-list)
        (if (get-buffer p4-output-buffer-name)
            (progn
              (with-current-buffer p4-output-buffer-name
                (message "%s" (buffer-substring (point-min) (- (point-max) 1))))
              (kill-buffer p4-output-buffer-name))))))

(defun eaw-p4-login ()
  (interactive)
  (let (buffer)
    (setq buffer (make-comint "p4 login" p4-executable nil "login"))
    (set-buffer buffer)
    (comint-mode)
    (display-buffer buffer)))

(defun eaw-p4-logout ()
  (interactive)
  (p4-noinput-buffer-action "logout" t nil nil))

(defun p4-go (config)
  (interactive
   (list (read-file-name "P4 Config file: "
                         (concat (getenv "HOME") "/etc/perforce/*-")
                         ""
                         t)))
  (if (string= (getenv "P4CONFIG") (expand-file-name config))
      t
    (p4-set-client-config (expand-file-name config))
    (setenv "WORKSPACE" (p4-get-client-root (p4-current-client)))
    t))

(defun eaw-p4-sync-sentinel (proc evt)
  (if (not (eq (process-status proc) 'exit))
      (message "sentinel event for %s" (process-name proc))
    (p4-refresh-files-in-buffers)
    (let* ((buffer (process-buffer proc))
           (bufname (buffer-name buffer)))
      (with-current-buffer buffer
        (rename-uniquely)
        (goto-char (point-max))
        (sort-lines nil (region-beginning) (region-end)))
      (let ((p4-output-buffer-name (buffer-name buffer)))
        (p4-make-depot-list-buffer bufname))
      (with-current-buffer buffer
        (toggle-read-only -1)
        (save-excursion
          (goto-char (point-max))
          (insert "\n --- COMPLETE --- \n\n"))))))

(if (executable-find "p4")
    (progn
      (require 'p4)
      (require 'p4v)
      (set-face-foreground 'p4-diff-ins-face "green")
      (set-face-background 'p4-diff-head-face 'unspecified)
      (set-face-foreground 'p4-diff-head-face "yellow")
      (set-face-background 'p4-diff-file-face "blue")
      (define-key p4-prefix-map "A" 'eaw-p4-ediff-all-opened)
      (define-key p4-prefix-map "b" 'p4-go)
      (define-key p4-prefix-map "C" 'p4-change)
      (define-key p4-prefix-map "c" 'eaw-p4-changes)
      (define-key p4-prefix-map "D" 'p4-ediff2)
      (define-key p4-prefix-map "d" 'p4-describe)
      (define-key p4-prefix-map "E" 'eaw-p4-reopen-change)
      (define-key p4-prefix-map "K" 'eaw-p4-logout)
      (define-key p4-prefix-map "k" 'eaw-p4-login)
      (define-key p4-prefix-map "o" 'eaw-p4-opened)
      (define-key p4-prefix-map "S" 'p4-go)
      (define-key p4-prefix-map "S" 'p4-shelve)
      (define-key p4-prefix-map "s" 'eaw-p4-submit)
      (define-key p4-prefix-map "v" 'p4v-mode)
      (define-key p4-prefix-map "\C-c" 'p4-client)

      (defun eaw-p4-proj-change-hook ()
        (if (boundp 'eaw-proj-p4-map)
            (dolist (dir eaw-proj-p4-map)
              (setq dirname (car dir))
              (if (or (and (stringp dirname)
                           (eq t (compare-strings nonsrc 0 (length dirname) dirname 0 (length dirname))))
                      (eq dirname t))
                  (progn
                    (p4-go (concat (getenv "HOME" "/etc/perforce/" (cdr dir)))
                           (return)))))))
      (add-hook 'eaw-proj-change-hook 'eaw-p4-proj-change-hook)

      (defp4cmd eaw-p4-get ()
        "sync"
        "To synchronise the local view with the depot, type \\[p4-get].\n"
        (interactive)
        (let (args buffer process (p4-output-buffer-name (concat "*P4 Get: (" (p4-current-client) ")*")))
          (if current-prefix-arg
              (setq args (p4-make-list-from-string (p4-read-arg-string "p4 get: "))))
          (setq buffer (get-buffer-create p4-output-buffer-name))
          (with-current-buffer buffer
            (toggle-read-only -1)
            (goto-char (point-max))
            (set-mark (point-max)))
          (display-buffer buffer)
          (setq process (apply 'start-process "p4-get" buffer "p4" "get" args))
          (set-process-sentinel process 'eaw-p4-sync-sentinel)))

      (defalias 'p4-get 'eaw-p4-get)

      ;; The p4 shelve command
      (defp4cmd p4-shelve (change)
        "shelve" "To edit the change specification, type \\[p4-shelve].\n"
        (interactive "P")
        (let* ((change (completing-read "P4 shelve change (default: default): "
                                        'eaw-p4-changes-comp
                                        nil t nil nil "default"))
               args)
          (p4-save-opened-files)
          (if (or (not (and p4-check-empty-diffs (p4-empty-diff-p)))
                  (yes-or-no-p "File with empty diff opened for edit. Shelve anyway? "))
              (if (string= "default" change)
                  (p4-async-process-command "change" "Description:\n\t" "*P4 Shelve*" "shelve" nil)
                (let ((p4-output-buffer-name (concat "*P4 Shelve " change "*")))
                  (p4-noinput-buffer-action "shelve" nil nil (list "-r" "-c" change)))))))

      (defadvice p4-activate-print-buffer (after p4-print-mode)
        "Based on the name of the file in the depot, set the proper major mode"
        (with-current-buffer (get-buffer buffer-name)
          (let ((buffer-file-name buffer-name))
            (set-auto-mode))))
      (ad-activate 'p4-activate-print-buffer)

      (defadvice p4-edit (around eaw-p4-edit)
        (let ((p4-output-buffer-name "*P4 edit*"))
          ad-do-it
          (with-current-buffer p4-output-buffer-name
            (message "%s" (buffer-substring (point-min) (- (point-max) 1))))
          (if (get-buffer p4-output-buffer-name)
              (kill-buffer p4-output-buffer-name))))
      (ad-activate 'p4-edit)

      (defadvice p4-reopen (around eaw-p4-reopen)
        (let ((p4-output-buffer-name "*P4 reopen*"))
          ad-do-it
          (with-current-buffer p4-output-buffer-name
            (message "%s" (buffer-substring (point-min) (- (point-max) 1))))
          (kill-buffer p4-output-buffer-name)))
      (ad-activate 'p4-reopen)

      (defadvice p4-revert (around eaw-p4-revert)
        (let ((p4-output-buffer-name "*P4 revert*"))
          ad-do-it
          (with-current-buffer p4-output-buffer-name
            (message "%s" (buffer-substring (point-min) (- (point-max) 1))))
          (kill-buffer p4-output-buffer-name)))
      (ad-activate 'p4-revert)

      (defadvice p4-add (around eaw-p4-add)
        (let ((p4-output-buffer-name "*P4 add*"))
          ad-do-it
          (with-current-buffer p4-output-buffer-name
            (message "%s" (buffer-substring (point-min) (- (point-max) 1))))
          (kill-buffer p4-output-buffer-name)))
      (ad-activate 'p4-add)

      (defadvice p4-delete (around eaw-p4-delete)
        (let ((p4-output-buffer-name "*P4 delete*"))
          ad-do-it
          (with-current-buffer p4-output-buffer-name
            (message "%s" (buffer-substring (point-min) (- (point-max) 1))))
          (kill-buffer p4-output-buffer-name)))
      (ad-activate 'p4-delete)

      (defadvice p4-ediff (around eaw-p4-ediff)
        (let ((p4-output-buffer-name (concat "*P4 print " (p4-buffer-file-name) "#have*")))
          ad-do-it))
      (ad-activate 'p4-ediff)

      (defadvice p4-noinput-buffer-action (after eaw-auto-pop-window)
        "Automatically undo the changes to the window configuration
that p4 makes"
        (if (ad-get-arg 2)
            (progn
              (p4-pop-window-config 1)
              (display-buffer p4-output-buffer-name))))
      (ad-activate 'p4-noinput-buffer-action)

      (defadvice p4-get-client-root (around eaw-p4-get-client-root)
        (let ((p4-output-buffer-name "*P4 client*"))
          ad-do-it
          (kill-buffer p4-output-buffer-name)))
      (ad-activate 'p4-get-client-root)))

;; magit
(autoload 'magit-status "magit" nil t)
(defvar eaw-magit-prefix-map
  (let ((map (make-sparse-keymap)))
    (define-key map "o" 'magit-status)
    map)
  "The Prefix for Magit Commands.")
(define-key global-map "\C-xj" eaw-magit-prefix-map)
(setq magit-repository-directories `((,eaw-src-home . 2)))
(setq magit-buffer-name-format "*%M%v: %t*")
(setq magit-delete-by-moving-to-trash nil)

;; debian does this for me, mac does not
(autoload 'svn-status "psvn" nil t)

;; it's crazy that it steals C-x C-j for dired
(add-hook 'svn-status-mode-hook '(lambda () (define-key svn-status-mode-map (kbd "C-x C-j") nil)))

;; remove temporary files
(setq svn-status-ediff-delete-temporary-files t)

;; give a warning when following symlinks, don't ask
(setq vc-follow-symlinks nil)

;; C-x v - to use ediff to diff
(defun ediff-current-buffer-revision ()
  (interactive)
  (ediff-load-version-control)
  (if (buffer-file-name)
      (funcall (intern
        (format "ediff-%S-internal" ediff-version-control-package)) "" ""
        `((lambda ()
            (make-local-variable 'ediff-cleanup-hook)
            (add-hook 'ediff-cleanup-hook '(lambda () (kill-buffer ediff-buffer-A))))))))
(define-key vc-prefix-map "-" 'ediff-current-buffer-revision)
(autoload 'ediff-load-version-control "ediff" nil t)

(provide 'ew-vc)