;;; see file irchat-copyright.el for change log and copyright info

(require 'irchat-inlines)
(require 'irchat-globals)
(require 'irchat-pj-action)

(defsubst irchat-scan-channels (str)
  (setq irchat-channel-alist (cons (list str) irchat-channel-alist)))

(defsubst irchat-user-on-this-channel (nick chan)
  "return T if NICK is on channel CHAN"
  (if (null nick) nil
    (let ((n (intern nick)))
      (string-list-ci-memberp chan (get n 'chnl)))))

(defun irchat-greet-user (nick chan)
  (let ((n (intern nick)))
    ;; modified by simm@irc.fan.gr.jp, Mon, 20 Dec 1999 21:35:11 +0900
    (funcall irchat-pj-sound-join-function)
    (message "IRCHAT: %s has entered! (%s)" nick
	     (if (string= chan "0") "on no channel yet"
	       (concat "on channel " (irchat-chan-virtual chan))))
    (if (get n 'irchat-greeting)
	(irchat-send "PRIVMSG %s :%s" nick (get n 'irchat-greeting)))
    (put n 'irchat-waited-for nil)
    (put n 'irchat-greeting nil)))


(defun irchat-change-nick-of (old new)
  (let ((pair (assoc old irchat-nick-alist)))
    (if pair
	(setcar pair new)
      (setq irchat-nick-alist (cons (cons new nil) irchat-nick-alist)))
    (if (null new) ;; maybe quit
	(put (intern old) 'chnl nil))))

(defun irchat-Command-debug-user ()
  "for debugging."
  (interactive)
  (let (rest str chan oper (nick (read-string (format "NICK: "))))
    (setq rest (get (intern nick) 'chnl))
    (setq str (format "%s =" nick))
    (while rest
      (setq chan (car rest))
      (setq rest (cdr rest))
      (setq oper (cdr (assoc nick (get (intern chan) 'nicka))))
      (setq str (format "%s %s%s" str (or oper "")
			(irchat-chan-virtual chan))))
    (irchat-insert0 (format "%s (shared channels)\n" str))
    (irchat-insert0 (format "%s = [%s] to %s last seen\n" nick
			    (irchat-convert-seconds2
			     (get (intern nick) 'lasttime))
			    (irchat-chan-virtual
			     (or (get (intern nick) 'lastchan) "(not yet)"))))
    (irchat-insert0 (format "%s = <%s>\n" nick
			    (or (get (intern nick) 'userhost) "not yet")))))


(defsubst irchat-remove-from-channel (nick chan)
  "Remove users info from his channel"
  (let (chans (u (intern nick)))
    (setq chans (string-list-ci-delete chan (get u 'chnl)))
    (put u 'chnl chans)
    (if (null chans)  ;; no more shared chanel
	nil)))

(defun irchat-add-to-channel (nicks chan)
  "Update our copy of NICKS on channel CHAN."
  (let ((nicka (get (intern chan) 'nicka)))
    (while (string-match "^\\([@+]?\\)\\([^ ]+\\) ?\\(.*\\)" nicks)
      (let ((oper (matching-substring nicks 1))
	    (nick (matching-substring nicks 2))
	    chans)
	(setq nicks (matching-substring nicks 3))
	(setq nicka (cons (cons nick oper) nicka))
	(setq chans (get (intern nick) 'chnl))
	(if (not (string-list-ci-memberp chan chans))
	    (put (intern nick) 'chnl (nconc chans (list chan))))
	(if (get (intern nick) 'irchat-waited-for)
	    (irchat-greet-user nick chan))
	(if (null (assoc nick irchat-nick-alist))
	    (setq irchat-nick-alist (cons (list nick) irchat-nick-alist)))))
    (put (intern chan) 'nicka nicka)))

(defun irchat-channel-operator (nick chan)
  (cdr (assoc nick (get (intern chan) 'nicka))))

(defun irchat-channel-operator-set (nick chan oper)
  (let ((nicka (assoc nick (get (intern chan) 'nicka))))
    (and nicka
	 (setcdr nicka oper))))

(defun irchat-channel-newnick-set (nick chan newnick)
  (let ((nicka (assoc nick (get (intern chan) 'nicka))))
    (and nicka
	 (setcar nicka newnick))))

(defun irchat-user-last-privmsg (nick)
  (get (intern nick) 'lastchan))

(defun irchat-user-last-privmsg-time (nick)
  (let ((time (get (intern nick) 'lasttime)))
    (if time (- (irchat-current-time) time) nil)))

;; begin: add by simm@irc.fan.gr.jp, Tue, 20 Jul 1999
(defsubst irchat-pj-make-verbose-nick (nick userhost flag)
  (if flag
      (concat nick "(" userhost ")")
    nick))
;; end


(defun irchat-handle-error (prefix msg)
  (if (not irchat-no-configure-windows)
      (irchat-insert-allchan (format "ERROR: %s\n" msg) nil))
  (setq irchat-fatal-error-message msg)
  (message "IRC ERROR: %s" msg))

(defun irchat-handle-482 (prefix me chan msg)
  (message "IRCHAT: You are not a channel operator on %s"
	   (irchat-chan-virtual chan)))

(defun irchat-handle-464 (prefix me msg)
  (irchat-insert0 (format 
       "*** Password incorrect from %s. Try again with password.\n" prefix))
  (setq irchat-reconnect-with-password t))

(defun irchat-handle-nick (prefix userhost nick)
  (irchat-insert (concat
                  (irchat-pj-make-verbose-nick prefix userhost irchat-pj-handle-nick-verbose)
                  " is now known as " nick "\n")
		 (get (intern prefix) 'chnl))
  (irchat-change-nick-of prefix nick)
  (put (intern nick) 'chnl (get (intern prefix) 'chnl))
  (put (intern prefix) 'chnl nil)
  (mapcar
   '(lambda (chan)
      (irchat-channel-newnick-set prefix chan nick))
   (get (intern nick) 'chnl))
  (if (string= prefix irchat-nickname)
      (progn
	(setq irchat-nickname nick)
	(if (string= prefix irchat-current-chat-partner)
	    (setq irchat-current-chat-partner nick))
	(irchat-insert-special
         (concat
          (irchat-pj-make-verbose-nick prefix userhost irchat-pj-handle-nick-verbose)
          " is now known as " nick "\n")))))

(defun irchat-handle-notice (prefix chan msg)
  ;; begin: add by simm@irc.fan.gr.jp, Mon, 18 Jan 1999
  (and irchat-pj-rewrite-server-notice
       (string= prefix irchat-server)
       (setq prefix nil msg (concat "=== " msg)))
  ;; end
  (cond 
   ((null prefix)
    (if (string-match "ERROR" msg)
	(irchat-insert-allchan (format "%s\n" msg))
      (irchat-insert-special (format "%s\n" msg))))
   ((string-match "\001\\(.*\\)\001" msg)
    (irchat-ctcp-notice prefix msg))
   ((string-match "^\\*\\*\\* Notice -- \\(.*\\)" msg)
    (let ((notice (matching-substring msg 1)))
      (cond
       ((and irchat-ignore-fakes
	     (string-match "^Fake" notice))
	t)
       ((and irchat-ignore-noauths
	     (string-match "^No Authorization" notice))
	t)
       ((and irchat-ignore-kills
	     (string-match "^Received KILL" notice))
	t)
       ((and irchat-shorten-kills
	     (string-match 
	      "^Received KILL message for \\([^.]*\\)\\. From \\([^ ]*\\) Path: \\([^ ]*\\) ?\\(.*\\)" notice))
	(let ((killed (matching-substring notice 1))
	      (killer (matching-substring notice 2))
	      (reason (matching-substring notice 4))
	      (cbuf (current-buffer)))
	  (set-buffer irchat-KILLS-buffer)
	  (goto-char (point-max))
	  (insert (format "%s\n" notice))
	  (set-buffer cbuf)
	  (irchat-insert0 (format "%s KILLed %s %s\n" killer killed
				  (if (= (length reason) 0)
				      "-No reason supplied-"
				    reason)))))
       (t
	(irchat-insert0 (format "%s: %s\n" prefix notice))))))
   (t
    (irchat-handle-privmsg2 prefix chan msg))))

(defun irchat-handle-ping (prefix msg)
  (irchat-send-pong msg)
  (irchat-maybe-poll))

;; modified by simm@irc.fan.gr.jp, Sat, 13 Jun 1999
(defun irchat-handle-pong (prefix he &optional msg)
  (cond
   ((and msg (string= msg "irchat-polling"))
    (setq irchat-polling t))
   ((string-match he ":irchat-polling$")
    (setq irchat-polling t))
   (t
    ;;(irchat-insert0 (format "[%s]\n" (matching-substring rest 1)))
    nil)))

(defun irchat-handle-privmsg (prefix chan msg)
  (or (null prefix)
      (and prefix
	   (memq (intern prefix) irchat-ignore-nickname)
	   (irchat-msg-from-ignored prefix chan msg))
      (let ((temp msg)
	    (case-fold-search t))
	;; begin sound extension: modified by kaoru@kaisei.org
	(cond ((and (string-match "\007" temp) irchat-beep-on-bells)
	       (funcall irchat-pj-sound-bell-function))
	      ((consp irchat-pj-sound-words-list)
	       (let ((re irchat-pj-sound-words-list))
		 (while (consp re)
		   (save-match-data
		     (cond ((string-match (car re) temp)
			    (funcall irchat-pj-sound-words-function)
			    (setq re nil))
			   (t (setq re (cdr re)))))))))
	;; end
	(if (and (string-match "\001\\(.*\\)\001" temp)
		 (or (not (string= irchat-nickname prefix))
		     (irchat-match-me chan)))
	    (setq temp (irchat-ctcp-msg prefix chan temp)))
	(if (not (string= temp ""))
	    (irchat-handle-privmsg2 prefix chan temp))
	;; add by mikami@nk.hcs.ts.fujitsu.co.jp, Tue, 31 Aug 1999 10:48:12 +0900
	(run-hooks 'irchat-privmsg-exit-hook))))

(defun irchat-handle-privmsg2 (prefix chan xmsg)
  (or (not (irchat-match-me chan))
      (get-buffer-window irchat-Dialogue-buffer)
      (get-buffer-window irchat-Others-buffer)
      (get-buffer-window irchat-Private-buffer)
      (message "IRCHAT: A private message has arrived from %s" prefix))
  ;; only private messages to us get time-stamp
  (if (irchat-match-me chan)
      (irchat-insert-private t prefix xmsg)
    (if (string= irchat-nickname prefix)
	;; my message to channel/partner
	(irchat-insert-private nil chan xmsg)
      (if (irchat-user-on-this-channel prefix chan) 
	  ;; user on this channel
	  (irchat-insert (format "%s %s\n"
				 (format "<%s:%s>"
					 (irchat-chan-virtual chan)
					 prefix) xmsg)
			 chan 'privmsg)
	;; user not on this channel
	(irchat-insert (format "%s %s\n"
			       (format "(%s:%s)"
				       (irchat-chan-virtual chan)
				       prefix) xmsg)
		       chan 'privmsg))))
  (put (intern prefix) 'lastchan chan)
  (put (intern prefix) 'lasttime (irchat-current-time)))

(defun irchat-match-me (dest)
  (or (string-ci-equal dest irchat-nickname)
      (string-ci-equal dest (format "%s@%s" irchat-my-user
				    irchat-my-server))
      (string-ci-equal dest (format "%s%%%s@%s" irchat-my-user
				    irchat-my-host irchat-my-server))))

(defun irchat-handle-wallops (prefix msg)
  "Handle the WALLOPS message."
  (if irchat-show-wallops
      (irchat-insert0 (format "*** Wallops: %s %s\n" prefix msg)))
  (let ((buf (current-buffer)))
    (set-buffer irchat-WALLOPS-buffer)
    (goto-char (point-max))
    (insert (format "%s %s\n"
		    (if prefix (concat "from " prefix) "") msg))
    (set-buffer buf)))


(defun irchat-handle-quit (prefix userhost reason)
  "Handle the QUIT message."
  (irchat-insert (concat
                  (irchat-pj-make-verbose-nick prefix userhost irchat-pj-handle-quit-verbose)
                  " has left IRC (" reason ")\n")
		 (get (intern prefix) 'chnl)
		 (concat "has left IRC (" reason ")")
		 t
                 (concat
                  (irchat-pj-make-verbose-nick prefix userhost irchat-pj-handle-quit-verbose)
                  ","))
  ;; begin sound extension: modified by kaoru@kaisei.org
  (and irchat-pj-sound-when-part
       (not irchat-ignore-changes)
       (funcall irchat-pj-sound-part-function))
  ;; end sound extension:
  (put (intern prefix) 'chnl nil)
  (irchat-change-nick-of prefix nil))

(defun irchat-handle-topic (prefix chan topic)
  "Handle the TOPIC message."
  (if (not irchat-ignore-changes)
      (irchat-insert (format "New topic on %s set by %s: %s\n"
			     (irchat-chan-virtual chan) prefix topic)
		     chan t)))

(defun irchat-handle-mode (prefix chan &rest args)
  "Handle the MODE message."
  (let ((str ""))
    (while args
      (cond ((string-match "\\+ooo" (car args))
	     (irchat-channel-operator-set (nth 1 args) chan "@")
	     (irchat-channel-operator-set (nth 2 args) chan "@")
	     (irchat-channel-operator-set (nth 3 args) chan "@"))
	    ((string-match "\\+oo-o" (car args))
	     (irchat-channel-operator-set (nth 1 args) chan "@")
	     (irchat-channel-operator-set (nth 2 args) chan "@")
	     (irchat-channel-operator-set (nth 3 args) chan nil))
	    ((string-match "\\+o-o\\+o" (car args))
	     (irchat-channel-operator-set (nth 1 args) chan "@")
	     (irchat-channel-operator-set (nth 2 args) chan nil)
	     (irchat-channel-operator-set (nth 3 args) chan "@"))
	    ((string-match "-o\\+oo" (car args))
	     (irchat-channel-operator-set (nth 1 args) chan nil)
	     (irchat-channel-operator-set (nth 2 args) chan "@")
	     (irchat-channel-operator-set (nth 3 args) chan "@"))
	    ((string-match "-oo\\+o" (car args))
	     (irchat-channel-operator-set (nth 1 args) chan nil)
	     (irchat-channel-operator-set (nth 2 args) chan nil)
	     (irchat-channel-operator-set (nth 3 args) chan "@"))
	    ((string-match "-o\\+o-o" (car args))
	     (irchat-channel-operator-set (nth 1 args) chan nil)
	     (irchat-channel-operator-set (nth 2 args) chan "@")
	     (irchat-channel-operator-set (nth 3 args) chan nil))
	    ((string-match "\\+o-oo" (car args))
	     (irchat-channel-operator-set (nth 1 args) chan "@")
	     (irchat-channel-operator-set (nth 2 args) chan nil)
	     (irchat-channel-operator-set (nth 3 args) chan nil))
	    ((string-match "-ooo" (car args))
	     (irchat-channel-operator-set (nth 1 args) chan nil)
	     (irchat-channel-operator-set (nth 2 args) chan nil)
	     (irchat-channel-operator-set (nth 3 args) chan nil))
	    ((string-match "\\+oo" (car args))
	     (irchat-channel-operator-set (nth 1 args) chan "@")
	     (irchat-channel-operator-set (nth 2 args) chan "@"))
	    ((string-match "\\+o-o" (car args))
	     (irchat-channel-operator-set (nth 1 args) chan "@")
	     (irchat-channel-operator-set (nth 2 args) chan nil))
	    ((string-match "-o\\+o" (car args))
	     (irchat-channel-operator-set (nth 1 args) chan nil)
	     (irchat-channel-operator-set (nth 2 args) chan "@"))
	    ((string-match "-oo" (car args))
	     (irchat-channel-operator-set (nth 1 args) chan nil)
	     (irchat-channel-operator-set (nth 2 args) chan nil))
	    ((string-match "\\+o" (car args))
	     (irchat-channel-operator-set (nth 1 args) chan "@"))
	    ((string-match "-o" (car args))
	     (irchat-channel-operator-set (nth 1 args) chan nil)))
      (setq str (format "%s %s" str (car args)))
      (setq args (cdr args)))
    (if (string= chan irchat-nickname)
	(if (not irchat-ignore-changes)
	    (irchat-insert (format "Your new mode is set:%s\n" str)
			   irchat-Private-buffer
			   (format "Your new mode is set:")
			   nil
			   (format "%s" str)))
      (if (not irchat-ignore-changes)
	  (irchat-insert (format "New mode for %s set by %s:%s\n"
				 (irchat-chan-virtual chan) prefix str)
			 chan
			 (format "New mode for %s set by %s:"
				 (irchat-chan-virtual chan) prefix)
			 nil
			 (format "%s" str))))))


(defun irchat-handle-kick (prefix chan nick reason)
  "Handle the KICK message."
  (irchat-remove-from-channel nick chan)
  (if (string= nick irchat-nickname)
      (progn
	(irchat-insert (format
			"You were kicked off channel %s by %s. (%s)\n"
			(irchat-chan-virtual chan) prefix reason)
		       (list chan irchat-Private-buffer) t)
	(setq irchat-current-channels
	      (string-list-ci-delete chan irchat-current-channels))
	(put (intern chan) 'nicka nil)
	(irchat-Channel-part chan))
    (irchat-insert (format
		    "%s has kicked %s out from channel %s (%s)\n"
		    prefix nick (irchat-chan-virtual chan) reason)
		   chan t)))

(defun irchat-handle-invite (prefix nick chan)
  (irchat-insert-special
   (format "*** %s invites you to channel %s\n"
	   prefix (irchat-chan-virtual chan)) t)
  ;; begin sound extension: modified by kaoru@kaisei.org
  (if irchat-pj-sound-when-invited
      (funcall irchat-pj-sound-invited-function))
  ;; end sound extension:
  (setq irchat-invited-channel chan))

(defun irchat-handle-kill (prefix me path)
  (irchat-insert-special
   (format "*** IRCHAT: You were killed by %s. Path: %s\n" 
	   prefix path) t)
  (irchat-insert-allchan (format "ERROR: You were Killed.\n") nil)
  (irchat-Channel-part nil))

(defun irchat-handle-join (nick userhost chan)
  "Handle the JOIN message."
  (let ((oper "") (voice "") (xnick nick))
    (if (string-match "^\\([^\007]+\\)\007\\(o*\\)\\(v*\\)$" chan)
	(setq voice (matching-substring chan 3)
	      oper (matching-substring chan 2)
	      chan (matching-substring chan 1)))
    (if (string= oper "o")
	(setq xnick (format "@%s" xnick))
      (if (string= voice "v")
	  (setq xnick (format "+%s" xnick))))
    (if (string= nick irchat-nickname)
	(progn
	  (setq irchat-current-channels
		(cons chan irchat-current-channels))
	  (irchat-Channel-join chan)
	  (put (intern chan) 'init t)
	  (put (intern chan) 'nicka nil))
      ;; add by simm@irc.fan.gr.jp, Sat, 18 Dec 1999 01:26:35 +0900
      (and irchat-pj-auto-oper-list
	   (irchat-pj-action-auto-oper nick userhost chan))
      (irchat-add-to-channel nick chan))
    (if (not irchat-ignore-changes)
	(irchat-insert (concat
                        (irchat-pj-make-verbose-nick xnick userhost
						     irchat-pj-handle-join-verbose)
                        " has joined channel "
                        (irchat-chan-virtual chan)
                        "\n")
		       (if (string= nick irchat-nickname)
			   (list chan irchat-Private-buffer) chan)
		       (concat "has joined channel " (irchat-chan-virtual chan))
                       t
                       (concat
                        (irchat-pj-make-verbose-nick xnick userhost
						     irchat-pj-handle-join-verbose)
                        ",")))
    ;; begin sound extension: modified by kaoru@kaisei.org
    (and irchat-pj-sound-when-join
	 (not irchat-ignore-changes)
	 (funcall irchat-pj-sound-join-function))
    ;; end sound extension:
    (irchat-change-nick-of nick nick)))

(defun irchat-handle-part (prefix userhost chan &optional reason)
  "Handle the PART message."
  (if (null reason) (setq reason ""))
  (if (string= prefix irchat-nickname)
      (progn
	(setq irchat-current-channels
	      (string-list-ci-delete chan irchat-current-channels))
	(put (intern chan) 'nicka nil)))
  (if (not irchat-ignore-changes)
      (irchat-insert (concat
                      (irchat-pj-make-verbose-nick prefix userhost
						   irchat-pj-handle-part-verbose)
                      " has left channel "
                      (irchat-chan-virtual chan)
                      " (" reason ")\n")
		     (if (string= prefix irchat-nickname)
			 (list chan irchat-Private-buffer) chan)
		     (concat "has left channel "
                             (irchat-chan-virtual chan)
                             " (" reason ")")
		     t
                     (concat
                      (irchat-pj-make-verbose-nick prefix userhost
						   irchat-pj-handle-part-verbose)
                      ",")))
  (if (not (string= prefix irchat-nickname))
      ;; begin sound extension: modified by kaoru@kaisei.org
      (and irchat-pj-sound-when-part
	   (not irchat-ignore-changes)
	   (funcall irchat-pj-sound-part-function))
      ;; end sound extension:
    (irchat-Channel-part chan)
    (if (null irchat-invited-channel)
	(setq irchat-invited-channel chan)))
  (irchat-remove-from-channel prefix chan)
  (irchat-change-nick-of prefix prefix))

;;;
;;;  000 replies -- what the fuck is the author of ircd thinking.
;;;
(defun irchat-handle-000s (number prefix me &rest args)
  (cond
   ((= (length args) 1)
    (irchat-insert0 (format "*** %s\n" (car args))))
   ((= (length args) 2)
    (irchat-insert0 (format "*** %s %s\n" (car args) (nth 1 args))))
   ((= (length args) 3)
    (irchat-insert0 (format "*** %s %s (%s)\n"
			    (car args) (nth 2 args) (nth 1 args))))
   (t
    (message "IRCHAT: Strange %s reply" number))))


(defun irchat-handle-001 (prefix nick msg)
  (setq irchat-my-server prefix)
  (if (string-match ".*!\\([^!]*\\)" msg)
      (setq irchat-pj-my-userhost (matching-substring msg 1)))
  (if irchat-no-configure-windows
      (irchat-configure-windows))
  (or (string= irchat-nickname nick)
      (irchat-insert-special (format "%s is now known as %s\n" irchat-nickname nick) t))
  (irchat-insert-special
   (format "*** Welcome to the Internet Relay Chat world. Your nick is %s.\n" nick))
  (setq irchat-servername prefix)
  (setq irchat-nickname nick)
  (irchat-send "USERHOST %s" irchat-nickname)
  (setq irchat-my-userhost nil))

(defun irchat-handle-002 (prefix me msg)
  (if (string-match "running version \\(.*\\)" msg)
      (irchat-insert-special
       (format "*** Your server is %s (version %s).\n"
	       irchat-servername (matching-substring msg 1))))
  (irchat-insert0
   (format "*** Your client version is %s.\n" irchat-pj-version-string)))

(defun irchat-handle-003 (prefix me msg)
  (irchat-insert0 (format "*** %s \n" msg)))

;;(defun irchat-handle-004 (prefix me server version mode1 mode2)
(defun irchat-handle-004 (prefix me server &optional version mode1 mode2)
  ;;(irchat-insert0
   ;;(format "*** %s %s %s %s\n" server version mode1 mode2))
  (irchat-maybe-poll))


;;;
;;;  200 replies
;;;
(defun irchat-handle-200s (number prefix me &rest args)
  (cond
   ((= (length args) 1)
    (irchat-insert0 (format "*** %s\n" (car args))))
   ((= (length args) 2)
    (irchat-insert0 (format "*** %s %s\n" (car args) (nth 1 args))))
   ((= (length args) 3)
    (irchat-insert0
     (format "*** %s %s (%s)\n" (car args) (nth 2 args) (nth 1 args))))
   (t
    (message "IRCHAT: Strange %s reply" number))))

(defun irchat-handle-200 (prefix me status version dest next &optional ver sec q1 q2)
  "200 RPL_TRACELINK Link <version & debug level> <destination> <next server>"
  (irchat-insert0 (format "*** Link %s (%s%s) ==> %s (%s)%s\n"
			  prefix version
			  (if ver (format " %s" ver) "")
			  next dest
			  (if sec (format " [%s] %s/%s"
					  (irchat-convert-seconds sec)
					  q1 q2) ""))))

(defun irchat-handle-201 (prefix me status class who)
  "201 RPL_TRACECONNECTING Try. <class> <server>"
  (irchat-insert0 (format "*** %s %s (%s) ==> %s\n"
			  status prefix class who)))

(defun irchat-handle-202 (prefix me status class who)
  "202 RPL_TRACEHANDSHAKE H.S. <class> <server>"
  (irchat-insert0 (format "*** %s %s (%s) ==> %s\n"
			  status prefix class who)))

(defun irchat-handle-203 (prefix me status class who)
  "203 RPL_TRACEUNKNOWN ???? <class> [<client IP address in dot form>]"
  (irchat-insert0 (format "*** %s %s (%s) ==> %s\n"
			  status prefix class who)))

(defun irchat-handle-204 (prefix me status class who)
  "204 RPL_TRACEOPERATOR Oper <class> <nick>"
  (irchat-insert0 (format "*** %s %s (%s) ==> %s\n"
			  status prefix class who)))

(defun irchat-handle-205 (prefix me status class who &optional dummy time)
  (irchat-insert0 (format "*** %s %s (%s) ==> %s%s\n"
			  status prefix class who
			  (if time (format " (%s)" time) ""))))

(defun irchat-handle-206 (prefix me status class Nserver Nclient name how &optional version)
  "206 RPL_TRACELINK Serv 200 3S 15C irc.other *!*@other.host :V3"
  (irchat-insert0 (format "*** Server %s (%s) ==> %s {%s,%s} %s %s\n"
			  prefix class name Nserver Nclient how
			  (if version (format "[%s]" version) ""))))

(defun irchat-handle-209 (prefix me status class entries)
  (irchat-insert0 (format "*** Class %s (%s entries)\n"
				class entries)))

(defvar irchat-stats-now nil)

(defun irchat-handle-211 (prefix me link sndq sndm sndb rcvm rcvb time)
  "NOTICE %s :%-15.15s%5u%7u%10u%7u%10u %s"
  (setq	time (string-to-int time))
  (if (not irchat-stats-now)
      (progn
	(irchat-insert0 (format "STATS L %s\n" prefix))
	(irchat-insert0 
   "    Time             Send-Q   Send-Msg    Send-KB   Recv-Msg    Recv-KB\n")
	(setq irchat-stats-now prefix)))
  (irchat-insert0 (format "%s\n" link))
  (irchat-insert0
   (format "%5ddays %02d:%02d:%02d %8s %10s %10s %10s %10s\n"
	   (/ (/ (/ time 60) 60) 24)
	   (mod (/ (/ time 60) 60) 24)
	   (mod (/ time 60) 60)
	   (mod time 60)
	   sndq sndm sndb rcvm rcvb)))

(defun irchat-handle-213 (prefix me cmd host pass server port class)
  (if (not irchat-stats-now)
      (progn
	(irchat-insert0 (format "STATS C %s\n" prefix))
	(setq irchat-stats-now prefix)))
  (irchat-insert0 (format "%s:%s:%s:%s:%s:%s\n"
			  cmd host pass server port class)))

(defun irchat-handle-214 (prefix me cmd host pass server port class)
  (if (not irchat-stats-now)
      (progn
	(irchat-insert0 (format "STATS C %s\n" prefix))
	(setq irchat-stats-now prefix)))
  (irchat-insert0 (format "%s:%s:%s:%s:%s:%s\n"
			  cmd host pass server port class)))

(defun irchat-handle-212 (prefix me cmd times bytes &optional rtimes)
  (if (not irchat-stats-now)
      (progn
	(irchat-insert0 (format "STATS M %s\n" prefix))
	(setq irchat-stats-now prefix)))
  (if rtimes
      (irchat-insert0
       (format "%s has been used %s times (remote: %s times) after startup (%s bytes)\n"
	       cmd times rtimes bytes))
    (irchat-insert0
     (format "%s has been used %s times after startup (%s bytes)\n"
	     cmd times bytes))))

(defun irchat-handle-215 (prefix me cmd ip passwd domain port class)
  ":server.name 215 me I ip * domain port class"
  (if (not irchat-stats-now)
      (progn
	(irchat-insert0 (format "STATS I %s\n" prefix))
	(setq irchat-stats-now prefix)))
  (irchat-insert0 (format "*** %s:%s:%s:%s:%s:%s\n"
			  cmd ip passwd domain port class)))

(defun irchat-handle-216 (prefix me cmd domain &rest args)
  ":servernameirc 216 me K domain ..."
  (if (not irchat-stats-now)
      (progn
	(irchat-insert0 (format "STATS K %s\n" prefix))
	(setq irchat-stats-now prefix)))
  (irchat-insert0 (format "*** %s:%s:%s\n"
			  cmd domain
			  (let ((str ""))
			    (while args
			      (setq str (format "%s %s" str (car args))
				    args (cdr args)))
			    str))))

(defun irchat-handle-217 (prefix me cmd reason star host stuff)
  (if (not irchat-stats-now)
      (progn
	(irchat-insert0 (format "STATS Q %s\n" prefix))
	(setq irchat-stats-now prefix)))
  (irchat-insert0 (format "*** %s:%s:%s:%s:%s\n"
			  cmd reason star host stuff)))

(defun irchat-handle-218 (prefix me cmd class pfreq cfreq mlinks msendq &optional local global)
  ":server.name 218 me Y class 120 600 1 3000000"
  (if (not irchat-stats-now)
      (progn
	(irchat-insert0 (format "STATS Y %s\n" prefix))
	(setq irchat-stats-now prefix)))
  (irchat-insert0 (format 
       "*** Class %s: PingFreq %s, ConFreq %s, MaxLinks %s, MaxSendQ %s%s%s\n"
	     class pfreq cfreq mlinks msendq
	     (if local (format ", LocalLimit %s" local) "")
	     (if global (format ", GlobalLimit %s" global) ""))))

(defun irchat-handle-219 (prefix me type msg)
  "RPL_ENDOFSTATS"
  (if (not irchat-stats-now)
      (irchat-insert0 (format "*** Nothing (STATS %s %s)\n"
			      type prefix)))
  (setq irchat-stats-now nil))

(defun irchat-handle-221 (prefix me str) ;;; RPL_USERMODEIS?
  "Handle the MODE message."
  (irchat-insert-special (format "*** Mode for you is %s\n" str) t))

(defun irchat-handle-241 (prefix me cmd hostmask star server maxdepth etc)
  ":irc.tokyo.wide.ad.jp 241 nick L * * irc.leaf.server 0 -1"
  (if (not irchat-stats-now)
      (progn
	(irchat-insert0 (format "STATS H %s\n" prefix))
	(setq irchat-stats-now prefix)))
  (irchat-insert0 (format "*** %s:%s:%s:%s:%s:%s\n"
			  cmd hostmask star server maxdepth etc)))

(defun irchat-handle-242 (prefix me msg)
  (if (not irchat-stats-now)
      (progn
	(irchat-insert0 (format "STATS U %s\n" prefix))
	(setq irchat-stats-now prefix)))
  (irchat-insert0 (format "*** %s\n" msg)))

(defun irchat-handle-243 (prefix me cmd hostname password user nazo class)
  ":server.name 243 me O hostname password user nazo class"
  (if (not irchat-stats-now)
      (progn
	(irchat-insert0 (format "STATS O %s\n" prefix))
	(setq irchat-stats-now prefix)))
  (irchat-insert0 (format "*** %s:%s:%s:%s:%s:%s\n"
			  cmd hostname password user nazo class)))

(defun irchat-handle-244 (prefix me cmd hostmask star server maxdepth etc)
  ":irc.tokyo.wide.ad.jp 244 nick H * * irc.tokai-ic.or.jp 0 -1"
  (if (not irchat-stats-now)
      (progn
	(irchat-insert0 (format "STATS H %s\n" prefix))
	(setq irchat-stats-now prefix)))
  (irchat-insert0 (format "*** %s:%s:%s:%s:%s:%s\n"
			  cmd hostmask star server maxdepth etc)))

(defun irchat-handle-246 (prefix me server p1 p2 p3 p4)
  ":server0.jp 246 nick server1.jp[*@server1.hostname] 26 26 50 15"
  (if (not irchat-stats-now)
      (progn
	(irchat-insert0 (format "STATS P %s\n" prefix))
	(irchat-insert0 (format "*** %32s %5s %5s %5s %5s\n"
				"<server name>" "snd#" "rcv#" "ping" "pref"))
	(setq irchat-stats-now prefix)))
  (if (string-match "\\([^[]+\\)\\[[^]]+\\]" server)
      (setq server (matching-substring server 1)))
  (irchat-insert0 (format "*** %32s %5s %5s %5s %5s\n" server p1 p2 p3 p4)))

(defun irchat-handle-248 (prefix me msg)
  "RPL_STATSDEFINE"
  (if (not irchat-stats-now)
      (progn
	(irchat-insert0 (format "STATS D %s\n" prefix))
	(setq irchat-stats-now prefix)))
  (irchat-insert0 (format "*** %s\n" msg)))

(defun irchat-handle-249 (prefix me msg)
  (if (not irchat-stats-now)
      (progn
	(irchat-insert0 (format "STATS T/Z %s\n" prefix))
	(setq irchat-stats-now prefix)))
  (irchat-insert0 (format "*** %s\n" msg)))

(defun irchat-handle-250 (prefix me msg)
  "RPL_STATSDLINE"
  (irchat-insert0 (format "*** %s\n" msg)))

(defun irchat-handle-251 (prefix me msg)
  "RPL_LUSERCLIENT"
  (irchat-insert0 (format "*** %s\n" msg)))

(defun irchat-handle-252 (prefix me count msg)
  "RPL_LUSEROP"
  (irchat-insert0 (format "*** %s %s\n" count msg)))

(defun irchat-handle-253 (prefix me count msg)
  "RPL_LUSERUNKNOWN"
  (irchat-insert0 (format "*** %s %s\n" count msg)))

(defun irchat-handle-254 (prefix me count msg)
  "RPL_LUSERCHANNELS"
  (irchat-insert0 (format "*** %s %s\n" count msg)))

(defun irchat-handle-255 (prefix me msg)
  "RPL_LUSERME"
  (irchat-insert0 (format "*** %s\n" msg)))

(defun irchat-handle-256 (prefix me msg)
  "RPL_ADMINME"
  (irchat-insert0 (format "*** %s\n" msg)))

(defun irchat-handle-257 (prefix me msg)
  "RPL_ADMINLOC1"
  (if (not (string= msg ""))
      (irchat-insert0 (format "*** %s\n" msg))))

(defun irchat-handle-258 (prefix me msg)
  "RPL_ADMINLOC2"
  (if (not (string= msg ""))
      (irchat-insert0 (format "*** %s\n" msg))))

(defun irchat-handle-259 (prefix me msg)
  "RPL_ADMINEMAIL"
  (if (not (string= msg ""))
      (irchat-insert0 (format "*** %s\n" msg))))

(defun irchat-handle-261 (prefix me status filename port)  ; RPL_TRACELOG
  (irchat-insert0 (format "*** LogFile %s => %s (%s)\n"
			  prefix filename port)))

(defun irchat-handle-262 (prefix me mask version msg)  ; RPL_ END TRACE
  "262 RPL_TRACEEND *.jp 2.9.5. :End of TRACE"
  (irchat-insert0 (format "*** END of TRACE to (%s) %s [%s]\n"
			  mask prefix version)))

;;;
;;; 300 replies
;;;
(defun irchat-handle-300s (number prefix me &rest args)
  (cond
   ((= (length args) 1)
    (irchat-insert0 (format "*** %s\n" (car args))))
   ((= (length args) 2)
    (irchat-insert0 (format "*** %s %s\n" (car args) (nth 1 args))))
   ((= (length args) 3)
    (irchat-insert0
     (format "*** %s %s (%s)\n" (car args) (nth 2 args) (nth 1 args))))
   (t
    (message "IRCHAT: Strange %s reply" number))))

(defun irchat-handle-301 (prefix me nick iswhat) ; RPL_AWAY
  (if (not (string= nick irchat-auto-whois-nick))
      (if irchat-while-whois-reply
	  (irchat-insert0 (format "AWAY: %s\n" iswhat))
	(irchat-insert0 (format "%s is AWAY: %s\n" nick iswhat)))))

(defun irchat-handle-302 (prefix me data) ; RPL_USERHOST
  "Handle the 302 reply, USERHOST reply"
  (while (string-match 
	  "\\([^*=]+\\)\\([*]*\\)=\\([+-]\\)\\([^@]+\\)@\\([^ ]+\\) *\\(.*\\)" data)
    (let ((nick (matching-substring data 1))
	  (oper (matching-substring data 2))
	  (away (matching-substring data 3))
	  (user (matching-substring data 4))
	  (host (matching-substring data 5))
	  userhost)
      (setq data (matching-substring data 6))
      (setq userhost (format "%s@%s" user host))
      (put (intern nick) 'userhost userhost)
      (if (and (string= nick irchat-nickname)
	       (null irchat-my-userhost))
	  (progn
	    (setq irchat-my-userhost userhost)
	    (setq irchat-my-user user)
	    (setq irchat-my-host host)
	    (irchat-insert-special
	     (format "*** You are %s.\n" irchat-my-userhost))
	    (and irchat-pj-first-channel
		 irchat-pj-initialize-p
		 (irchat-Command-join irchat-pj-first-channel))
	    (setq irchat-pj-initialize-p nil))
	(irchat-insert0 (format "%s is <%s> [%s, %s]\n"
				nick userhost
				(concat 
				 (if (string= oper "")
				     "Not ")
				 "Operator")
				(concat 
				 (if (string= away "+")
				     "Not ")
				 "AWAY")))))))

(defun irchat-handle-303 (prefix me nicks) ; RPL_ISON
  "Handle the 303 reply, ISON reply"
  (if (string= nicks "")
      (irchat-insert0 "No one you requested is on now.\n")
    (irchat-insert0 (format "Following people(s) are on: %s\n" nicks))))

(defun irchat-handle-305 (prefix me msg) ; RPL_UNAWAY
  "Handle the 305 reply, UNAWAY reply"
  (if irchat-pj-away-p
      (progn
	(setq irchat-pj-away-p nil)
	(irchat-maybe-poll)
	(irchat-insert0 (format "*** %s (%s)\n"
				msg (current-time-string))))))

(defun irchat-handle-306 (prefix me msg) ; RPL_NOWAWAY
  "Handle the 306 reply, NOWAWAY reply"
  (setq irchat-pj-away-p t)
  (irchat-insert0 (format "*** %s (%s)\n"
			  msg (current-time-string))))

(defun irchat-handle-311 (prefix me nick user host dummy name) ;; RPL_WHOISUSER
  "Handle the 311 reply (from WHOIS)."
  (setq irchat-while-whois-reply t)
  (if (not (string= nick irchat-auto-whois-nick))
      (irchat-insert0 (format "%s is <%s@%s> %s\n"
			      nick user host name))))

(defun irchat-handle-312 (prefix me nick server info) ; RPL_WHOISSERVER
  ":server.name 312 me her server.name2 :server info"
  (if (not (string= nick irchat-auto-whois-nick))
      (irchat-insert0 (format "on via server %s (%s)\n" server info))))

(defun irchat-handle-313 (prefix me nick iswhat) ; RPL_WHOISOPERATOR
  (if (not (string= nick irchat-auto-whois-nick))
      (irchat-insert0 (format "STATUS: %s\n" iswhat))))

(defun irchat-handle-314 (prefix me nick user host star name) ;; RPL_WHOWASUSER
  "Handle the 314 reply (msa's WHOWAS)."
  (message "")
  (irchat-insert0 (format "%s was <%s@%s> %s\n"
			  nick user host name)))

(defun irchat-handle-315 (prefix ms chan msg) ; RPL_ENDOFWHO
  (setq irchat-long-reply-count 0))

(defun irchat-handle-317 (prefix me nick sec &optional time msg) ;RPL_WHOISIDLE
  ":server.name 317 me her 38 :seconds idle or\
   :server.name 317 me her 38 839678551 :seconds idle, signon time"
  (let (fun sec2)
    (if (not (string= nick irchat-auto-whois-nick))
	(irchat-insert0 (format "IDLE for %s %s\n"
				(irchat-convert-seconds sec)
				(if (setq sec2 (irchat-past-time sec))
				    (format "(%s)" sec2)
				  "")))
      (if (fboundp (setq fun (intern "irchat-whois-idle")))
	  (apply fun (list nick (string-to-int sec)))))))

(defun irchat-handle-318 (prefix me nick msg) ; RPL_ENDOFWHOIS
  ":server.name 318 me her :End of /WHOIS list."
  (setq irchat-while-whois-reply nil)
  (if (string= nick irchat-auto-whois-nick)
      (setq irchat-auto-whois-nick "")))

(defun irchat-handle-319 (prefix me nick rest) ; RPL_????
  ":server.name 319 me her :chan1 chan2 chan3"
  (let ((str "") oper chan)
    (while (string-match "^\\([@+]?\\)\\([^ ]+\\) +\\(.*\\)$" rest)
      (setq oper (matching-substring rest 1))
      (setq chan (matching-substring rest 2))
      (setq rest (matching-substring rest 3))
      (if (not (irchat-ischannel chan))
	  (setq chan (concat oper chan)
		oper ""))
      (setq str (format "%s %s%s" str oper (irchat-chan-virtual chan))))
    (if (not (string= nick irchat-auto-whois-nick))
	(irchat-insert0 (format "channels:%s\n" str)))))

(defun irchat-handle-321 (prefix me chan msg) ;;; RPL_LISTSTART
  "Handle the 321 reply (first line from LIST)."
  nil)

(defun irchat-handle-322 (prefix &optional me chan users topic bug) ; RPL_LIST
  "Handle the 322 reply (from LIST)."
  (setq irchat-long-reply-count (1+ irchat-long-reply-count))
  (if (> irchat-long-reply-count 38)
      (progn
	(setq irchat-long-reply-count 0)
	(irchat-send-pong)))
  (if topic
      (irchat-insert (format "Topic for %s (%s users): %s\n"
			     (if (string= chan "*") "Private"
			       (irchat-chan-virtual chan))
			     users topic)
		     chan t)
    (irchat-insert (format "*** Error 322: %s %s %s %s %s" prefix me chan users topic))))

(defun irchat-handle-323 (prefix me msg) ; RPL_LISTEND
  (setq irchat-channel-filter "")
  (setq irchat-long-reply-count 0))


(defun irchat-handle-324 (prefix me chan &rest args) ; RPL_CHANNELMODEIS
  "Handle the MODE message."
  (let ((str ""))
    (while args
      (setq str (format "%s %s" str (car args)))
      (setq args (cdr args)))
    (irchat-insert (format "Mode for %s:%s\n"
			   (irchat-chan-virtual chan) str) chan t)))

(defun irchat-handle-331 (prefix me chan msg) ; RPL_NOTOPIC
  (irchat-insert (format "No topic is set for %s\n"
			 (irchat-chan-virtual chan))
		 chan t))

(defun irchat-handle-332 (prefix me chan topic) ; RPL_TOPIC
  (irchat-insert (format "Topic for %s: %s\n"
			 (irchat-chan-virtual chan) topic) chan t))

(defun irchat-handle-341 (prefix me nick chan) ; RPL_INVITING
  (irchat-insert (format "*** Inviting user %s to channel %s\n"
			  nick (irchat-chan-virtual chan))
		 chan t))

(defun irchat-handle-351 (prefix me ver server mode) ; RPL_VERSION
  (irchat-insert0 (format "*** %s is running IRC version %s (%s)\n"
			  server ver mode)))

(defun irchat-handle-352 (prefix me chan user host serv nick oper name)
  "Handle the WHOREPLY message (352)"
  (setq irchat-long-reply-count (1+ irchat-long-reply-count))
  (if (> irchat-long-reply-count 38)
      (progn
	(setq irchat-long-reply-count 0)
	(irchat-send-pong)))
  (if (string-match "[0-9]* *\\(.*\\)" name)
      (setq name (matching-substring name 1)))
  (irchat-insert (format "%3s %9s %9s %30s %s\n"
			 (add-space 3 oper)
			 (add-space 9 (if (or (string= chan "*")
					      (string= chan "0"))
					  "Private"
					(irchat-chan-virtual chan)))
			 (add-space 9 nick)
			 (add-space 30
				    (format "<%s@%s>" user host))
			 name) chan t))

(defun irchat-handle-353 (prefix me flag chan nicks) ; RPL_NAMREPLY
  "Handle the 353 (NAMREPLY) message.   If we are just polling the server,
don't display anything.  Check if someone we are waiting for has entered."
  (setq irchat-long-reply-count (1+ irchat-long-reply-count))
  (if (> irchat-long-reply-count 38)
      (progn
	(setq irchat-long-reply-count 0)
	(irchat-send-pong)))
  (if irchat-polling
      nil
    (irchat-insert (format "%s = %s\n"
			   (if (string= chan "*") "Private"
			     (irchat-chan-virtual chan))
			   nicks) chan t))
  (irchat-scan-channels chan)
  (if (get (intern chan) 'init)
      (irchat-add-to-channel nicks chan)))

(defun irchat-handle-361 (prefix me who msg) ; RPL_KILLDONE
  ;; modified by simm@irc.fan.gr.jp, Sun, 27 Jun 1999
  (irchat-insert0 (format "You just KILLED %s. %s\n" who msg)))

(defun irchat-handle-364 (prefix me server next msg)
  (setq irchat-links-reply-count (1+ irchat-links-reply-count))
  (if (string-match "^\\([^ ]+\\) \\(\\[[^]]+\\]\\)? *\\(.*\\)" msg)
      (let ((hop (matching-substring msg 1))
	    (comment (matching-substring msg 3)))
	(if irchat-how-to-show-links-reply
	    (irchat-insert0 (format "%2s %30s <== %30s\n" hop
				    (add-space 30 server) 
				    (add-space 30 next)))
	  (irchat-insert0 (format "%30s %s\n"
				  (add-space 30 server) comment))))))

(defun irchat-handle-365 (prefix me mask msg) ; RPL_ENDOFLINKS
  (if (= 0 irchat-links-reply-count)
      (irchat-insert0 (format "*** No match server. (%s)\n" mask)))
  (setq irchat-links-reply-count 0))

(defun irchat-handle-366 (prefix me chan msg) ; RPL_ENDOFNAMES
  (setq irchat-long-reply-count 0)
  (setq irchat-polling nil)
  (if (get (intern chan) 'init)
      (put (intern chan) 'init nil)))

(defun irchat-handle-367 (prefix me chan ban)
  (irchat-insert (format "Banned on %s: %s\n"
			 (irchat-chan-virtual chan) ban) chan t))

(defun irchat-handle-368 (prefix me chan msg) ; BAN END
  nil)

(defun irchat-handle-369 (prefix me nick msg) ; WHOWAS
  nil)

(defun irchat-handle-371 (prefix me msg) ; RPL_INFO
  (irchat-insert0 (format "*** %s\n" msg)))

(defun irchat-handle-372 (prefix me msg) ; RPL_MOTD
  (irchat-insert0 (format "*** %s\n" msg)))

(defun irchat-handle-374 (prefix me msg) ; RPL_INFO END
  nil)

(defun irchat-handle-375 (prefix me msg) ; RPL_MOTD START
  nil)

(defun irchat-handle-376 (prefix me msg) ; RPL_MOTD END
  nil)

(defun irchat-handle-381 (prefix me msg) ; RPL_YOUREOPER
  ":server.name 381 me :Good afternoon, gentleman. I am a HAL 9000 computer."
  (irchat-insert0 (format "OPER: %s\n" msg)))

(defun irchat-handle-382 (prefix name file msg) ; RPL_REHASHING
  (irchat-insert0 (format "*** %s: %s %s\n" name msg file)))

(defun irchat-handle-391 (prefix me server time) ; RPL_TIME
  ":server.name 391 me server.name :Monday August 5 1996 -- 01:39 +09:00"
  (irchat-insert0 (format "Time: %s (%s)\n" time server)))

;;;
;;;  400 replies -- ERRORS
;;; 
(defun irchat-handle-400s (number prefix me &rest args)
  "Generic handler for 4?? messages. This is called if no specific handler exists"
  (cond
   ((= (length args) 1)
    (irchat-insert0 (format "*** %s\n" (car args))))
   ((= (length args) 2)
    (irchat-insert0 (format "*** %s (%s)\n" (nth 1 args)
			    (irchat-chan-virtual (car args)))))
   ((= (length args) 3)
    (irchat-insert0 (format "*** %s %s (%s)\n" (car args) (nth 2 args)
			    (irchat-chan-virtual (nth 1 args)))))
   (t
    (message "IRCHAT: Strange %s reply" number))))

(defun irchat-handle-401 (prefix me nick msg) ; ERR_NOSUCHNICK
  ":server.name 401 me nick :No such nick/channel"
  (if (not (string= nick irchat-auto-whois-nick))
      (irchat-send "WHOWAS %s" nick)
    (setq irchat-auto-whois-nick "")))

(defun irchat-handle-402 (prefix me nick msg) ; ERR_NOSUCHSERVER
  ":server.name 402 me servername :No such server"
  (if (string-match "^[^.*]+$" nick)
      (if (not (string= nick irchat-auto-whois-nick))
	  (irchat-insert0
	   (format "*** Error: No such nick. (%s)\n" nick))
	(setq irchat-auto-whois-nick ""))
    (irchat-insert0
     (format "*** Error: No such server. (%s)\n" nick))))

(defun irchat-handle-403 (prefix me chan msg)
  (irchat-insert0 (format "*** %s (%s)\n" msg (irchat-chan-virtual chan))))

(defun irchat-handle-404 (prefix me chan msg)
  (irchat-insert0 (format "*** %s (%s)\n" msg (irchat-chan-virtual chan))))

(defun irchat-handle-405 (prefix me chan msg)
  (irchat-insert0 (format "*** %s (%s)\n" msg (irchat-chan-virtual chan))))

(defun irchat-handle-406 (prefix me nick msg)
  (irchat-insert0 (format "*** %s (%s)\n" msg nick)))

(defun irchat-handle-407 (prefix me nick msg)
  (irchat-insert0 (format "*** %s (%s)\n" msg nick)))

(defun irchat-handle-412 (prefix me msg)
  (message "IRCHAT: No text to send"))

(defun irchat-handle-421 (prefix me cmd msg)
  (irchat-insert0 (format "*** Unknown command (%s)\n" cmd)))

(defun irchat-handle-422 (prefix me msg)
  (irchat-insert0 (format "*** No message of the day in %s\n" prefix)))

(defun irchat-handle-432 (prefix me nick msg) ;;; ERR_ERRONICK?
  "Handle the 432 reply (erroneus nickname)"
  (save-excursion
    (set-buffer irchat-Command-buffer)
    ;; modified by simm@irc.fan.gr.jp, Mon, 20 Dec 1999 21:44:10 +0900
    (funcall irchat-pj-sound-error-function)
    (if irchat-no-configure-windows
	(setq irchat-nickname-erroneus t)
      (message
       "IRCHAT: Erroneus Nickname.  Choose a new one with %s."
       (substitute-command-keys "\\[irchat-Command-nickname]")))))

(defun irchat-handle-433 (prefix me nick msg) ;;; ERR_NICKNAMEINUSE
  "Handle the 433 reply (nickname already in use)"
  (save-excursion
    (set-buffer irchat-Command-buffer)
    (if irchat-no-configure-windows
	(setq irchat-nickname-already-in-use t)
      ;; modified by simm@irc.fan.gr.jp, Mon, 20 Dec 1999 21:44:10 +0900
      (funcall irchat-pj-sound-error-function)
      (message
       "IRCHAT: Nickname %s already in use.  Choose a new one with %s." nick
       (substitute-command-keys "\\[irchat-Command-nickname]")))))

(defun irchat-handle-437 (prefix me chan msg)
  "437 ERR_UNAVAILRESOURCE nick|chan :Nick/channel is temporarily unavailable"
  (irchat-insert0 (format "*** %s (%s)\n" msg (irchat-chan-virtual chan))))

(defun irchat-handle-441 (prefix me nick chan msg)
  "441 ERR_USERNOTINCHANNEL nick chan :They aren't on that channel"
  (irchat-insert0 (format "*** %s (%s/%s)\n"
			  msg nick (irchat-chan-virtual chan))))

(defun irchat-handle-442 (prefix me chan msg)
  "442 ERR_NOTONCHANNEL chan :You're not on that channel"
  (irchat-insert0 (format "*** %s (%s)\n" msg (irchat-chan-virtual chan))))

(defun irchat-handle-451 (prefix &rest args)
  (if irchat-after-registration
      (irchat-insert0 (format "*** You have not registered.\n"))))

(defun irchat-handle-471 (prefix me chan msg) ; ERR_CHANNELISFULL
  ":server.name 471 me chan :Sorry, cannot join channel."
  (setq irchat-invited-channel chan)
  (irchat-insert0
   (format "*** Error: Sorry, channel %s is full.\n"
	   (irchat-chan-virtual chan))))

(defun irchat-handle-472 (prefix me char msg) ; ERR_UNKNOWNMODE
  ":server.name 472 me char :is unknown mode char to me"
  (irchat-insert0
   (format "*** Error: '%s' %s.\n" char msg)))

(defun irchat-handle-473 (prefix me chan msg) ; ERR_INVITEONLYCHAN
  ":server.name 473 me chan :Sorry, cannot join channel."
  (setq irchat-invited-channel chan)
  (irchat-insert0
   (format "*** Error: Sorry, channel %s is invited only.\n"
	   (irchat-chan-virtual chan))))

(defun irchat-handle-474 (prefix me chan msg) ; ERR_BANNEDFROMCHAN
  ":server.name 474 me chan :Sorry, cannot join channel."
  (setq irchat-invited-channel chan)
  (irchat-insert0
   (format "*** Error: Sorry, you are banned from channel %s.\n"
	   (irchat-chan-virtual chan))))

(defun irchat-handle-475 (prefix me chan msg) ; ERR_BADCHANNELKEY
  ":server.name 475 me chan :Sorry, cannot join channel."
  (setq irchat-invited-channel chan)
  (irchat-insert0
   (format "*** Error: Sorry, incorrect key for channel %s.\n"
	   (irchat-chan-virtual chan))))

(defun irchat-handle-477 (prefix me chan msg)
  ":server.name 477 me chan :Channel doesn't support modes"
  (irchat-insert0 (format "*** %s (%s)\n" msg
			  (irchat-chan-virtual chan))))

(defun irchat-handle-484 (prefix me msg)
  ":server.name 484 me :Your connection is restricted!"
  (irchat-insert0 (format "*** %s\n" msg)))

;;;
;;;  500 replies -- ERRORS
;;;
(defun irchat-handle-500s (number prefix me &rest args)
  "Generic handler for 5?? messages. This is called if no specific handler exists"
  (cond
   ((= (length args) 1)
    (irchat-insert0 (format "*** %s\n" (car args))))
   ((= (length args) 2)
    (irchat-insert0 (format "*** %s (%s)\n" (nth 1 args)
			    (irchat-chan-virtual (car args)))))
   ((= (length args) 3)
    (irchat-insert0 (format "*** %s %s (%s)\n" (car args) (nth 2 args)
			    (irchat-chan-virtual (nth 1 args)))))
   (t
    (message "IRCHAT: Strange %s reply" number))))

;;;
;;; answer to CTCP messages, no postprocessing
;;;
(defun irchat-ctcp-msg (from to rest)
  "It's CTCP request, act on it."
  (let* (left now right message rest-of-line hook)
    (if (string-match "^\\([^\001]*\\)\001\\([^\001]*\\)\001\\(.*\\)" rest)
	;; modified by simm@irc.fan.gr.jp, Sat, 18 Dec 1999 00:41:09 +0900
	;; progn -> save-excursion
	(save-excursion
	  ;; add by simm@irc.fan.gr.jp, Sat, 18 Dec 1999 00:54:18 +0900
	  ;; for irchat-pj-sound
	  (if irchat-pj-sound-when-ctcp
	      (funcall irchat-pj-sound-ctcp-function))
	  (setq left (matching-substring rest 1))
	  (setq now (matching-substring rest 2))
	  (setq right (matching-substring rest 3))
	  (setq rest (concat left right))
	  (if (string-match "^\\([^ ]*\\) \\(.*\\)" now)
	      (progn
		(setq message (downcase (matching-substring now 1)))
		(setq rest-of-line (matching-substring now 2)))
	    (if (string-match "^\\([^ ]*\\)" now)
		(progn
		  (setq message (downcase (matching-substring now 1)))
		  (setq rest-of-line nil))
	      (progn
		(setq message "errmsg")
		(setq rest-of-line "Couldn't figure out what was said."))))
	  (if (and (boundp (setq hook
				 (intern 
				  (concat "irchat-ctcp-" message "-hook"))))
		   (eval hook)
		   (eq (eval (list hook from to rest-of-line)) t))
	      ;; If we have a hook, and it returns T, do nothing more
	      nil
	    ;; else call the handler
	    (if (fboundp (setq fun (intern
				    (concat "irchat-ctcp-" message "-msg"))))
		(progn
		  (eval (list fun from to rest-of-line))
		  (if (not irchat-freeze)
		      (irchat-scroll-if-visible
		       (get-buffer-window (current-buffer)))))
	      (progn
		(if (string-ci-equal to irchat-nickname)
		    ;; return unknown error if it's sended to only me.
		    (irchat-ctcp-reply from (format "ERRMSG %s :%s"
						    (upcase message)
						    (format irchat-ctcp-error-msg (upcase message)))))
		(irchat-insert (format "*** Unknown CTCP %s from %s to %s %s\n"
				       (upcase message) from to
				       (if rest-of-line
					   (if (string= rest-of-line "")
					       ""
					     (format "[%s]" rest-of-line))
					 ""))
			       to t))))))
    rest))

(defun irchat-ctcp-reply (receiver message)
  (irchat-send "NOTICE %s :\001%s\001" receiver message))

(defun irchat-ctcp-message (command from to)
  (message "CTCP %s from %s to %s" command from (irchat-chan-virtual to)))

;; begin: modified by simm@irc.fan.gr.jp, Mon, 14 Jun 1999
;;        move to irchat-pj-version-string.el, Tue, 12 Oct 1999
(defun irchat-ctcp-version-msg (from to rest)
  (irchat-ctcp-reply from (irchat-pj-set-version-string))
  (irchat-ctcp-message "VERSION" from to))
;; end

(defun irchat-ctcp-userinfo-msg (from to rest)
  (irchat-ctcp-reply from (format "USERINFO :%s" irchat-ctcp-userinfo))
  (irchat-ctcp-message "USERINFO" from to))

(defun irchat-ctcp-action-msg (from to rest)
  (if rest
      (irchat-handle-privmsg2 from to (format "*** %s %s" from rest))
    (irchat-handle-privmsg2 from to (format "*** %s (nil)" from)))
  (irchat-ctcp-message "ACTION" from to))

;; add by simm@irc.fan.gr.jp, Sun, 29 Aug 1999 22:56:28 +0900
(defun irchat-ctcp-caesar-msg (from to rest)
  (if rest
      (irchat-handle-privmsg2 from to (format "*** %s %s" from (irchat-pj-caesar-string rest)))
    (irchat-handle-privmsg2 from to (format "*** %s (nil)" from)))
  (irchat-ctcp-message "CAESAR" from to))

(defun irchat-ctcp-dcc-msg (from to rest)
  (irchat-dcc-request from to rest)
  (irchat-ctcp-message "DCC" from to))

(defun irchat-ctcp-time-msg (from to rest)
  (irchat-ctcp-reply from (format "TIME %s" (current-time-string)))
  (irchat-ctcp-message "TIME" from to))

(defun irchat-ctcp-ping-msg (from to rest)
  (irchat-ctcp-reply from (if rest (format "PING %s" rest) "PING"))
  (irchat-ctcp-message "PING" from to))

(defun irchat-ctcp-echo-msg (from to rest)
  (irchat-ctcp-reply from (if rest (format "ECHO %s" rest) "ECHO"))
  (irchat-ctcp-message "ECHO" from to))

(defun irchat-ctcp-clientinfo-msg (from to rest)
  (if (null rest)
      (irchat-ctcp-reply from
       (format "CLIENTINFO :%s :%s"
	       (irchat-ctcp-list-string)
	       "Use CLIENTINFO <COMMAND> to get more specific information"))
    (setq rest (upcase rest))
    (let (info)
      (setq info (get (intern rest) 'info))
      (if info
	  (irchat-ctcp-reply from (concat "CLIENTINFO " rest " " info))
	(irchat-ctcp-reply from (concat "ERRMSG :CLIENTINFO: "
					rest
					" is not a valid command")))))
  (if (null rest)
      (irchat-ctcp-message "CLIENTINFO" from to)
    (irchat-ctcp-message (format "CLIENTINFO %s" rest) from to)))
	

(defvar irchat-ctcp-list nil)

(defun irchat-ctcp-list-string ()
  (let ((str "") (rest irchat-ctcp-list))
    (while rest
      (setq str (concat str (car rest) " "))
      (setq rest (cdr rest)))
    str))

(defsubst irchat-ctcp-add-clientinfo (command info)
  (setq command (upcase command))
  (setq irchat-ctcp-list (append irchat-ctcp-list (list command)))
  (put (intern command) 'info info))

(irchat-ctcp-add-clientinfo "ACTION"
			    "contains action descriptions for atmosphere")
(irchat-ctcp-add-clientinfo "CLIENTINFO"
			    "gives available CTCP commands")
(irchat-ctcp-add-clientinfo "CAESAR"
			    "sends message with X-ROT-5-13-47-48 encoding")
(irchat-ctcp-add-clientinfo "DCC"
			    "requests a Direct-Client-Connection")
(irchat-ctcp-add-clientinfo "ECHO"
			    "returns the arguments it receives")
(irchat-ctcp-add-clientinfo "ERRMSG"
			    "returns error messages")
(irchat-ctcp-add-clientinfo "PING"
			    "returns the arguments it receives")
(irchat-ctcp-add-clientinfo "TIME"
			    "tells you the time on the user's host")
(irchat-ctcp-add-clientinfo "USERINFO"
			    "returns user settable information")
(irchat-ctcp-add-clientinfo "VERSION"
			    "shows client type, version and environment")

;;;
;;; read CTCP messages from notice, no postprocessing done
;;;

(defun irchat-ctcp-notice (prefix rest)
  "CTCP notice."
  (let* (message rest-of-line hook to) 
    (if (string-match "\\([^ ]+\\) :" rest)
	(setq to (matching-substring rest 1)))
    (if (string-match "\001\\([^ ]*\\) :?\\(.*\\)\001" rest)
	  (setq message (downcase (matching-substring rest 1))
		rest-of-line (matching-substring rest 2))
      (if (string-match "\001\\([^ ]*\\)\001" rest)
	  (setq message (downcase (matching-substring rest 1))
		rest-of-line nil)
	(setq message "errmsg"
	      rest-of-line "Couldn't figure out what was said.")))
    (if (and (boundp (setq hook
			   (intern (concat "irchat-ctcp-" message "-notice-hook"))))
	     (eval hook)
	     (eq (eval (list hook prefix rest-of-line)) t))
	;; If we have a hook, and it returns T, do nothing more
	nil
      ;; else call the handler
      (if (fboundp (setq fun (intern
			      (concat "irchat-ctcp-" message "-notice"))))
	  (progn
	    (eval (list fun prefix rest-of-line))
	    (if (not irchat-freeze)
		(irchat-scroll-if-visible
		 (get-buffer-window (current-buffer)))))
	(irchat-insert0 (format "*** Unknown CTCP Reply %s from %s to %s %s\n"
				(upcase message) prefix to
				(if rest-of-line
				    (format "[%s]" rest-of-line)
				  "")))))))


(defun irchat-ctcp-version-notice (prefix rest)  
  (if rest
      (if (string-match "^\\(.*\\)\\s *:[^:]*$" rest)
	  (irchat-insert0 (format "VERSION@%s: %s\n" prefix (matching-substring rest 1)))
	(irchat-insert0 (format "VERSION@%s: %s\n" prefix rest)))
    (message (format "Empty CTCP version notice from \"%s\"." prefix))))

(defun irchat-ctcp-version-notice-old (prefix rest)  
  (if rest
      (if (string-match "^\\([^:]*\\):\\(.*\\)" rest)
	  (irchat-insert0 (format "VERSION@%s: %s\n" prefix 
				 (matching-substring rest 1)))
	(if (string-match "^\\([^ ]*\\) \\([^ ]*\\) \\([^ ]*\\) \\(.*\\)" rest)
	    (irchat-insert0 (format "VERSION@%s: %s\n" prefix
				    (format "%s %s for %s: %s" 
					    (matching-substring rest 1)
					    (matching-substring rest 2)
					    (matching-substring rest 3)
					    (matching-substring rest 4))))
	  (irchat-insert0 (format "VERSION@%s: %s\n" prefix rest))))
    (message (format "Empty CTCP version notice from \"%s\"." prefix))))

(defun irchat-ctcp-clientinfo-notice (prefix rest)
  (irchat-insert0 (format "CLIENTINFO@%s: %s\n" prefix rest)))

(defun irchat-ctcp-userinfo-notice (prefix rest)  
  (irchat-insert0 (format "USERINFO@%s: %s\n" prefix rest)))

(defun irchat-ctcp-finger-notice (prefix rest)
  (irchat-insert0 (format "FINGER@%s: %s\n" prefix rest)))

(defun irchat-ctcp-time-notice (prefix rest)  
  (irchat-insert0 (format "TIME@%s: %s\n" prefix rest)))

(defun irchat-ctcp-echo-notice (prefix rest)
  (irchat-insert0 (format "ECHO@%s: %s\n" prefix rest)))

(defun irchat-ctcp-ping-notice (prefix rest)
  (let (to (time rest))
    (if (string-match "\\([^ ]+\\) \\(.*\\)" rest)
	(progn
	  (setq time (matching-substring rest 1))
	  (setq to (matching-substring rest 2))))
    (if (= 0 (string-to-int (or time "0")))
	(irchat-insert0 (format "PING@%s: %s\n" prefix (or time "nothing")))
      (let ((diff (- (irchat-current-time) (string-to-int time))))
	(while (< diff 0)
	  (setq diff (+ diff 86400)))
	(irchat-insert (format "PING@%s: %s second%s\n" prefix diff
			       (if (< diff 2) "" "s"))
		       to t)))))

(defun irchat-ctcp-errmsg-notice (prefix rest)  
  (irchat-insert0 (format "ERRMSG@%s: %s\n" prefix rest)))

(defun irchat-ctcp-comment-notice (from rest)  
  (message (format "CTCP COMMENT query from %s." from)))


;;;
;;; decode and encode of binary data
;;;
(defun irchat-quote-decode (string-to-decode)
  (interactive)
  (save-excursion
    (set-buffer (get-buffer-create "*IRC DECODE*"))
    (delete-region (point-min) (point-max))
    (insert string-to-decode)
    (goto-char (point-min))
    (replace-string "\\\\" "\\")
    (goto-char (point-min))
    (replace-string "\\a" "\001")
    (goto-char (point-min))
    (replace-string "\\n" "\n")
    (goto-char (point-min))
    (replace-string "\\r" "\r")
    (setq string-to-decode (buffer-substring (point-min) (point-max)))
    string-to-decode))

(defun irchat-quote-encode (string)
  (interactive)
  (save-excursion
    (set-buffer (get-buffer-create "*IRC ENCODE*"))
    (delete-region (point-min) (point-max))
    (insert string)
    (goto-char (point-min))
    (while (search-forward "\\" nil 1)
      (insert "\\"))
    (goto-char (point-min))
    (while (search-forward "\001" nil 1)
      (delete-char -1)
      (insert "\\a"))
    (goto-char (point-min))
    (while (search-forward "\000" nil 1)
      (delete-char -1)
      (insert "\\0"))
    (goto-char (point-min))
    (while (search-forward "\n" nil 1)
      (delete-char -1)
      (insert "\\n"))
    (goto-char (point-min))
    (while (search-forward "\r" nil 1)
      (delete-char -1)
      (insert "\\r"))
    (setq string (buffer-substring (point-min) (point-max)))
    string))


(defun irchat-current-time ()
  (let* ((string (current-time-string))
         (day (string-to-int (substring string 8 10)))
         (hour (string-to-int (substring string 11 13)))
         (min  (string-to-int (substring string 14 16)))
         (sec  (string-to-int (substring string 17 19))))
    (+ sec (* 60 min) (* 3600 hour) (* 86400 day))))

(defun irchat-past-time (second)
  (let* ((string (current-time-string))
         (day (string-to-int (substring string 8 10)))
         (hour (string-to-int (substring string 11 13)))
         (min  (string-to-int (substring string 14 16)))
         (sec  (string-to-int (substring string 17 19))))
    (if (stringp second)
	(setq second (string-to-int second)))
    (setq second (- (+ sec (* 60 min) (* 3600 hour) (* 86400 day)) second))
    (if (< second 0) nil (irchat-convert-seconds2 second))))


(defun irchat-convert-seconds (time)
  "Convert seconds to printable string."
  (if (null time) (setq time 0))
  (let* ((seconds (if (stringp time) (string-to-int time) time))
	 (minutes (/ seconds 60))
	 (seconds (if minutes (% seconds 60) seconds))
	 (hours (/ minutes 60))
	 (minutes (if hours (% minutes 60) minutes))
	 (days (/ hours 24))
	 (hours (if days (% hours 24) hours))
	 (ds (and (/= 0 days)
		  (format "%d day%s, " days
			  (if (> days 1) "s" ""))))
	 (hs (and (/= 0 hours)
		  (format "%d hour%s, " hours
			  (if (> hours 1) "s" ""))))
	 (ms (and (/= 0 minutes)
		  (format "%d minute%s " minutes
			  (if (> minutes 1) "s" ""))))
	 (ss (format "%d seconds" seconds)))
    (concat ds hs ms (if seconds ss ""))))


(defun irchat-convert-seconds2 (time)
  "Convert seconds to printable string."
  (if (null time) (setq time 0))
  (let* ((seconds (if (stringp time) (string-to-int time) time))
	 (minutes (/ seconds 60))
	 (seconds (if minutes (% seconds 60) seconds))
	 (hours (/ minutes 60))
	 (minutes (if hours (% minutes 60) minutes))
	 (days (/ hours 24))
	 (hours (if days (% hours 24) hours)))
    (format "%d%s %02d:%02d:%02d" days
	    (if (= days 1) "st"
	      (if (= days 2) "nd"
		(if (= days 3) "rd" "th")))
	    hours minutes seconds)))


(defun irchat-msg-from-ignored (prefix chan msg)
  (save-excursion
    (let ((buf (current-buffer)))
      (set-buffer irchat-IGNORED-buffer)
      (goto-char (point-max))
      (insert (format "%s::%s %s\n" prefix chan msg))
      (set-buffer buf)
      t)))

(provide 'irchat-handle)
