;; This file is distributed with GNU Go, a Go program.
;;
;; Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005. 2006
;; 2007, 2008 and 2009 by the Free Software Foundation.
;;
;; This program is free software; you can redistribute it and/
;; modify it under the terms of the GNU General Public License
;; as published by the Free Software Foundation - version 3
;; 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 in file COPYIN
;; for more details.
;;
;; You should have received a copy of the GNU General Public
;; License along with this program; if not, write to the Free
;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02111, USA.


;; GNU Emacs mode for editing pattern database files.
;;
;; Put this file to emacs/site-lisp directory and add
;;
;;	(require 'gnugo-db)
;;
;; to your ~/.emacs file. If you want gnugo-db-mode to be selected
;; automatically for every .db file, add these lines also:
;;
;;	(setq auto-mode-alist
;;	      (append
;;	       auto-mode-alist
;;	       '(("\\.db\\'" . gnugo-db-mode))))


(defvar gnugo-db-mode-map nil)
(unless gnugo-db-mode-map
  (setq gnugo-db-mode-map (make-sparse-keymap))
  (define-key gnugo-db-mode-map "\C-c\C-p" 'gnugo-db-insert-pattern)
  (define-key gnugo-db-mode-map "\C-c\C-c"
    'gnugo-db-copy-main-diagram-to-constraint))

(defvar gnugo-db-mode-abbrev-table nil)
(define-abbrev-table 'gnugo-db-mode-abbrev-table ())

(defvar gnugo-db-mode-syntax-table nil)
(unless gnugo-db-mode-syntax-table
  (setq gnugo-db-mode-syntax-table (make-syntax-table))
  (modify-syntax-entry ?\# "<" gnugo-db-mode-syntax-table)
  (modify-syntax-entry ?\n ">#" gnugo-db-mode-syntax-table))

(defvar gnugo-db-font-lock-keywords (list "Pattern"
					  "goal_elements"
					  "callback_data"
					  "attribute_map"))


(defun gnugo-db-mode()
  "Major mode for editing pattern database files."
  (interactive)
  (kill-all-local-variables)
  (use-local-map gnugo-db-mode-map)
  (setq local-abbrev-table gnugo-db-mode-abbrev-table)
  (set-syntax-table gnugo-db-mode-syntax-table)
  (set (make-local-variable 'paragraph-start) "Pattern")
  (set (make-local-variable 'paragraph-separate) paragraph-start)
  (set (make-local-variable 'comment-start) "# ")
  (set (make-local-variable 'comment-end) "")
  (set (make-local-variable 'comment-start-skip) "#+ *")
  (setq font-lock-defaults '(gnugo-db-font-lock-keywords nil nil ((?_ . "w"))))
  (set (make-local-variable 'indent-line-function) 'gnugo-db-indent-line)
  (set (make-local-variable 'indent-region-function) 'gnugo-db-indent-region)
  (setq mode-name "GNU Go pattern database")
  (setq major-mode 'gnugo-db-mode))


(defun gnugo-db-indent-line(&optional indenting-region)
  "Indents a line of a constraint or main diagram line with comment."
  (let ((return-point (point)))
    (beginning-of-line)
    (let ((line-beginning (point))
	  (first-char (char-after)))
      (unless (= first-char ?\;)
	(forward-line -1)
	(when (= (char-after) ?\;)
	  (setq first-char ?\;)))

      (let* ((column)
	     (indentation
	      (if (= first-char ?\;)
		  (progn
		    (while (and (= (char-after) ?\;)
				(= (forward-line -1) 0)))
		    (let ((paren-stack ()))
		      (while (search-forward-regexp "[][()]" line-beginning t)
			(let ((char (char-before)))
			  (if (memq char '(?\( ?\[))
			      (push (list char (current-column)) paren-stack)
			    (let ((pop-paren (cond ((= char ?\)) ?\()
						   ((= char ?\]) ?\[))))
			      (while (not (= (car (pop paren-stack)) pop-paren))
				())))))
		      (goto-char line-beginning)
		      (setq column (if paren-stack
				       (cadr (car paren-stack))
				     2)))
		    (concat ";"
			    (make-string (/ column tab-width) ?\t)
			    (make-string (if (< column tab-width)
					     (1- column)
					   (% column tab-width))
					 ? )))

		(goto-char line-beginning)
		(if (memq first-char '(?- ?+ ?| ?. ?X ?O ?x ?o
					  ?, ?! ?* ?? ?Y ?Q))
		    (progn
		      (let ((diagram-width 0))
			(while (not (memq (char-after) '(?  ?\t ?\n nil)))
			  (setq diagram-width (1+ diagram-width))
			  (forward-char))
			(if (< diagram-width 8)
			    (progn (setq column 12)
				   "\t    ")
			  (setq column (+ diagram-width 4))
			  "    ")))
		  nil))))

	(when indentation
	  (let ((indentation-point (point))
		(indentation-length (length indentation))
		(matched 0))
	    (while (and (< matched indentation-length)
			(eq (char-after) (aref indentation matched)))
	      (setq matched (1+ matched))
	      (forward-char))
	    (while (memq (char-after) '(?  ?\t))
	      (forward-char))
	    (unless (or (= (current-column) column)
			(and indenting-region (memq (char-after) '(?\n nil))))
	      (setq return-point (+ return-point
				    indentation-length
				    (- indentation-point (point))))
	      (delete-region (+ indentation-point matched) (point))
	      (when (< matched indentation-length)
		(insert (substring indentation matched))))
	    (when (< return-point (point))
	      (setq return-point (point)))))))

    (goto-char return-point)))


(defun gnugo-db-indent-region(start end)
  "Indents a region.  Indents in the same way as `gnugo-db-indent-line'."
  (interactive "r")
  (save-excursion
    (setq end (copy-marker end))
    (goto-char start)
    (while (< (point) end)
      (or (and (bolp) (eolp))
	  (gnugo-db-indent-line t))
      (forward-line))
    (move-marker end nil)))


(defun gnugo-db-insert-pattern()
  "Inserts a new pattern after the current one. Tries to pick up a
suitable name by incrementing numeric part of the previous pattern
name.

This function heavily depends on correctness of the current pattern."
  (interactive)
  (let ((first-name "")
	(middle-name "")
	(last-name ""))
    (end-of-line)
    (if (re-search-backward "^Pattern " 0 t)
	(progn
	  (forward-char 8)
	  (when (looking-at "\\([^0-9]+\\)\\([0-9]*\\)\\(.*\\)")
	    (setq first-name (match-string-no-properties 1)
		  middle-name (match-string-no-properties 2)
		  last-name (match-string-no-properties 3)))
	  (re-search-forward "^:" (1+ (buffer-size)) t)
	  (backward-char)
	  (forward-line 2)
	  (unless (memq (char-after) '(?\n ?  ?\t))
	    (re-search-forward "^[;>]" (1+ (buffer-size)) t)
	    (backward-char)
	    (while (looking-at "[;>]")
	      (forward-line))
	    (forward-line)
	    (when (looking-at "[;>]")
	      (while (looking-at "[;>]")
		(forward-line))
	      (forward-line)))
	  (when (= (forward-line) 1)
	    (end-of-line)
	    (insert "\n")))
      (re-search-forward "^Pattern " (1+ (buffer-size)) t)
      (beginning-of-line))

    (insert "Pattern \n")
    (let ((move-to-point (1- (point))))
      (unless (string= first-name "")
	(let ((pattern-name
	       (if (string= last-name "")
		   (concat first-name
			   (number-to-string (1+ (string-to-number middle-name))))
		 (concat first-name middle-name
			 (char-to-string (1+ (string-to-char last-name)))))))
	  (when (string= last-name "")
	    (when (save-excursion
		    (re-search-forward "^Pattern " (1+ (buffer-size)) t)
		    (or (looking-at pattern-name)
			(looking-at (concat first-name middle-name))))
	      (setq pattern-name (concat first-name middle-name "a"))))
	  (backward-char)
	  (insert pattern-name)
	  (forward-char)))
      (insert "\n")
      (unless (string= first-name "")
	(setq move-to-point (point)))
      (insert "\n\n:\n\n\n")
      (goto-char move-to-point))))


(defun gnugo-db-copy-main-diagram-to-constraint()
  "Copies pattern diagram to constraint and inserts a dummy constraint line"
  (interactive)
  (let ((start-point (point)))
    (end-of-line)
    (unless (re-search-backward "^Pattern " 0 t)
      (re-search-forward "^Pattern" (1+ (buffer-size)) t)
      (beginning-of-line))

    (forward-line)
    (while (not (looking-at "[-+|.XOxo,!*?YQ]"))
      (forward-line))

    (let ((diagram-beginning (point)))
      (while (looking-at "[-+|.XOxo,!*?YQ]")
	(forward-line))

      (let ((diagram (buffer-substring diagram-beginning  (point))))
	(re-search-forward "^:" (1+ (buffer-size)) t)
	(backward-char)
	(forward-line)
	(while (looking-at "#")
	  (forward-line))
	(when (memq (char-after) '(?\n ?  ?\t))
	  (forward-line))

	(when (looking-at "[-+|.XOxo,!*?YQ;>]")
	  (goto-char start-point)
	  (error "Pattern already seems to have a constraint"))

	(let ((constraint-diagram-beginning (point)))
	  (insert diagram)
	  (let ((constraint-diagram-end (point)))
	    (goto-char constraint-diagram-beginning)
	    (while (not (= (point) constraint-diagram-end))
	      (while (not (memq (char-after) '(?\n ?  ?\t)))
		(forward-char))
	      (unless (= (char-after) ?\n)
		(let ((diagram-line-end (point)))
		  (end-of-line)
		  (setq constraint-diagram-end
			(- constraint-diagram-end (- (point) diagram-line-end)))
		  (delete-region diagram-line-end (point))))
	      (forward-char))

	    (insert "\n; \n\n")
	    (goto-char constraint-diagram-beginning)))))))


(provide 'gnugo-db)
