;;; liece-filter.el --- Process filters for IRC process.
;; Copyright (C) 1998-2000 Daiki Ueno

;; Author: Daiki Ueno <ueno@unixuser.org>
;; Created: 1998-09-28
;; Revised: 1998-11-25
;; Keywords: IRC, liece

;; This file is part of Liece.

;; 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 2, 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 GNU Emacs; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.


;;; Commentary:
;; 

;;; Code:

(require 'liece-inlines)
(require 'liece-misc)
(require 'liece-intl)
(require 'liece-handler)

(defvar liece-current-function nil)

(defun* liece-handle-message (prefix message line)
  (let ((hook (intern (concat "liece-" message "-hook")))
	(after-hook (intern (concat "liece-after-" message "-hook")))
	(number (car (read-from-string message)))
	function)
    (if (run-hook-with-args-until-success hook prefix line)
	(return-from liece-handle-message))
    (if (not (numberp number))
	(setq function (liece-handler-find-function message '(prefix line) "generic"))
      (let ((base (format "%03d" (- number (mod number 100)))))
	(require (intern (concat "liece-" base)) nil 'noerror)
	(setq function (liece-handler-find-function message '(prefix line) base))
	(or function
	    (setq function
		  (let ((default (concat "liece-handle-" base "-messages")))
		    `(lambda (prefix line)
		       (funcall (intern ,default) ,message prefix line)))))))
    (if function
	(funcall function prefix line))
    (run-hook-with-args after-hook prefix line)))

(defun liece-parse-user-at-host ()
  (let ((cookie
	 (and (stringp liece-user-at-host)
	      (> (length liece-user-at-host) 2)
	      (string-to-char liece-user-at-host))))
    (cond
     ((null cookie)
      (setq liece-user-at-host-type 'invalid))
     ((or (eq cookie ?^) (eq cookie ?=))
      (setq liece-user-at-host (substring liece-user-at-host 1)
	    liece-user-at-host-type 'fake))
     ((or (eq cookie ?~) (eq cookie ?-))
      (setq liece-user-at-host (substring liece-user-at-host 1)
	    liece-user-at-host-type 'not-verified))
     ((eq cookie ?+)
      (setq liece-user-at-host (substring liece-user-at-host 1)
	    liece-user-at-host-type 'ok))
     (t (setq liece-user-at-host-type 'ok)))))

(defun liece-parse-line (line)
  (let (prefix message)
    (when (or (string-match "^\\(:[^! ]*\\)!\\([^ ]*\\) +\\([^ ]+\\) +:?"
			    line)
	      (string-match "^\\(:[^ ]*\\)?\\(\\) *\\([^ ]+\\) +:?"
			    line)
	      (string-match "^\\(:[^! \t]*\\)!\\([^ \t]*\\) +\\([^ \t]+\\) +:?"
			    line)
	      (string-match "^\\(:[^ ]*\\)?\\(\\) *\\([^ \t]+\\) +:?"
			    line))
      (setq prefix (if (match-beginning 1)
		       (substring (match-string 1 line) 1))
	    liece-user-at-host (match-string 2 line)
	    message (downcase (match-string 3 line))
	    line (liece-coding-decode-charset-string
		  (substring line (match-end 0))))
      
      (liece-parse-user-at-host)
      (setq liece-current-function (list prefix message))
      (liece-handle-message prefix message line)
      (setq liece-current-function '("" "")))))

(defun liece-filter (process output)
  "Filter function for IRC server process."
  (with-current-buffer (process-buffer process)
    (goto-char (point-max))
    (insert (liece-convert-received-input output))
    (goto-char (point-min))
    (while (progn (end-of-line) (and (not (eobp)) (eq (char-after) ?\n)))
      (if (eq (char-after (1- (point))) ?\r) ; cut off preceding LF
	  (delete-region (1- (point)) (point)))
      (liece-parse-line (buffer-substring (point-min) (point)))
      (delete-region (point-min) (progn (beginning-of-line 2) (point))))))

(defun liece-sentinel (proc status)
  "Sentinel function for Liece process."
  (cond
   ((or (not liece-server-process) (liece-server-opened)))
   ((not (or liece-reconnect-automagic liece-reconnect-with-password))
    (if (process-id proc)
	(liece-sentinel-error proc status)
      (liece-message (_ "Connection closed. (%s)")
		      (substring status 0 (1- (length status)))))
    (liece-close-server))
   (liece-reconnect-with-password
    (liece))
   (t
    (condition-case nil
	(progn
	  (set-process-filter liece-server-process nil)
	  (set-process-sentinel liece-server-process nil))
      (wrong-type-argument nil))
    (setq liece-server-process nil)
    (liece))))

(defun liece-sentinel-error (proc status)
  (if (not (string-match "^exited abnormally with code \\([0-9]+\\)" status))
      (liece-message (_ "Connection closed. (%s)")
		      (substring status 0 (1- (length status))))
    (let ((status (string-to-int (match-string 1 status))))
      (cond
       ((= 99 status) ;; unsupported command
        (liece-message (_ "Please use a newer \"%s\".") liece-dcc-program))
       ((= 98 status) ;; bad argment number
        (liece-message (_ "Please use a newer \"%s\".") liece-dcc-program))
       ((= 97 status)
        (liece-message (_ "Cannot connect to IRC server.")))
       (t
        (liece-message (_ "Server connection closed.")))))))

(provide 'liece-filter)

;;; liece-filter.el ends here
