;;; -*- Mode:Common-Lisp; Package:ZWEI; Base:10; Fonts:(CPTFONT HL12B HL12I MEDFNB) -*-


(defflavor 4NEWSGROUP-COMPONENT*
	   ((current-article-number -1)          ;1Current article number.*
	    (previous-article-number -1)         ;1Previous article number.*
	    (newsrc-p nil)                       ;1T if this newsgroup is found in the newsrc file.*
	    articles-read-bitmap	         ;1Articles read (stored as a bitmap).  Ranges are supported (i.e. 4-8).*
	    unread-article-count                 ;1Count of unread articles. *
	    )
  (system-component newsrc-component si:property-list-mixin)
  :settable-instance-variables
  :initable-instance-variables
  :gettable-instance-variables)


(defflavor 4SYSTEM-COMPONENT*
	   (newsgroup-string		         ;1Newsgroup name string (preserves case).*
	    high-article-number	                 ;1High article number in the newsgroup.*
	    low-article-number	                 ;1Low article number in the newsgroup.*
	    moderated			         ;1Moderated group (modered = #\m).*
	    )
	   (newsrc-component)
  :settable-instance-variables
  :initable-instance-variables
  :gettable-instance-variables)


(defflavor 4NEWSRC-COMPONENT*
	   ((subscribed-p nil)	                 ;1T if subscribed.*
	    articles-read-string                 ;1Articles read string.*
	    )
	    ()
  :settable-instance-variables
  :initable-instance-variables
  :gettable-instance-variables)


(defmethod 4(NEWSGROUP-COMPONENT :ARTICLE-READ-P*) ()
  "2Return T if the article has been read.*"
  (or (send self :start-or-end-of-newsgroup-p)
      (not (zerop (aref articles-read-bitmap (- current-article-number low-article-number))))))


(defun 4ARTICLE-READ-P* (newsgroup)
  "2Return T if the article has been read.*"
  (let ((newsgroup-component (get-newsgroup-component newsgroup)))
    (when newsgroup-component
      (send newsgroup-component :article-read-p))))


(defmethod 4(NEWSGROUP-COMPONENT :COUNT-UNREAD-ARTICLES-IN-NEWSGROUP*) ()
  "2Return the number of unread articles in the newsgroup. * 2The
unread-article-count* 2is updated accordingly.*"
  (setf unread-article-count
	(loop with count = 0
	      for i from 0 to (1- (length articles-read-bitmap)) do
	      (if (zerop (aref articles-read-bitmap i)) (incf count 1))
	      finally (return count))))


(defmethod 4(NEWSGROUP-COMPONENT :END-OF-NEWSGROUP-P*) ()
  "2Return T if we are at the end of the newsgroup. *"
  (equal current-article-number *end-index*))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;1;;*
;1;; This method will extend the articles read bitmap based on the new high article*
;1;; number and update the unread article count.  This is necessary when the newsgroup*
;1;; contains additional articles since the last time it was read.  Nothing is done when the*
;1;; new high article number is less than the existing high article number.*
;1;;*
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmethod 4(NEWSGROUP-COMPONENT :EXTEND-ARTICLES-READ-BITMAP*) (new-high-article-number)
  "2Return T if the articles-read-bitmap was extended. * 2Otherwise return* 2NIL.
The* 2unread-article-count is updated accordingly.*"
  (when (> new-high-article-number (max low-article-number high-article-number))
    (let ((new-length (+ (length articles-read-bitmap) (- new-high-article-number
							  (max low-article-number high-article-number)))))
      (setf articles-read-bitmap (adjust-array articles-read-bitmap new-length :initial-element 0))
      (setf high-article-number new-high-article-number)
      (send self :count-unread-articles-in-newsgroup)
      t)))


(defmethod 4(NEWSGROUP-COMPONENT :GET-NEXT-ARTICLE-NUMBER*) (&optional (mode nil))
  "2Return the next (mode = t) or next unread (mode = nil) article number* 2from
the newsgroup.*  2NIL if there isn't a next article number. * "
  (loop for i
	from (cond
	       ((= current-article-number *start-index*)
		low-article-number)
	       ((= current-article-number *end-index*)
		(1+ high-article-number))
	       (t
		(1+ current-article-number)))
	to high-article-number do
	(when (if mode t (zerop (aref articles-read-bitmap (- i low-article-number))))
	  (unless (equal current-article-number *start-index*)
	    (setf previous-article-number current-article-number))
	  (setf current-article-number i)
	  (return current-article-number))
	finally
	(progn
	  (setf previous-article-number current-article-number)
	  (setf current-article-number *end-index*)
	  (return nil))))


(defmethod 4(NEWSGROUP-COMPONENT :GET-PREVIOUS-ARTICLE-NUMBER*) (&optional (mode nil))
  "2Return the previous article number (mode = t) or previous unread article
number (mode = nil)* 2from the newsgroup. * 2NIL if there isn't a previous article
number.*"
  (loop
    for i
    from (cond
	   ((= current-article-number *end-index*)
	    high-article-number)
	   ((= current-article-number *start-index*)
	    -1)
	   (t
	    (1- current-article-number)))
    downto low-article-number do
    (when (if mode t (zerop (aref articles-read-bitmap (- i low-article-number))))
      (setf previous-article-number current-article-number)
      (setf current-article-number i)
      (return current-article-number))
    finally
    (progn
      (setf previous-article-number current-article-number)
      (setf current-article-number *start-index*)
      (return nil))))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;1;;*
;1;; This method should be called whenever we want to make sure that the method*
;1;; GET-NEXT-ARTICLE-NUMBER will return the first unread article number from*
;1;; the newsgroup.  *
;1;;*
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmethod 4(NEWSGROUP-COMPONENT :INITIALIZE-CURRENT-ARTICLE-NUMBER*) ()
  "2Initialize the current-article-number and previous-article-number in the
newsgroup to *start-index*.*"
  (setf current-article-number *start-index*)
  (setf previous-article-number *start-index*))


(defmethod 4(NEWSGROUP-COMPONENT :INITIALIZE-NEWSRC-COMPONENT*) (subscribed articles-read-str)
  "2Initialize the subscribed-p and articles-read-string.*"
  (setf subscribed-p subscribed)
  (setf articles-read-string articles-read-str)
  (setf articles-read-bitmap (convert-articles-read-to-bitmap articles-read-string high-article-number low-article-number))
  (send self :count-unread-articles-in-newsgroup))


(defmethod 4(NEWSGROUP-COMPONENT :MARK-ALL-ARTICLES*) (mode)
  "2Mark all articles as being read (mode = t) or unread (mode = nil) and update the unread-article-count.*"
  (send self :mark-articles low-article-number high-article-number mode))


(defmethod 4(NEWSGROUP-COMPONENT :MARK-ARTICLES*) (low-number high-number mode)
  "2Mark articles within the range (inclusive) as being read (mode = t) or unread (mode = nil) and update
the unread article count.*"
  (loop with saved-current-article-number = current-article-number
	for i from low-number to high-number
	finally (setf current-article-number saved-current-article-number)
	do
	(setf current-article-number i)
	(send self :mark-article mode nil))
  unread-article-count)


(defmethod 4(NEWSGROUP-COMPONENT :MARK-ARTICLE*) (mode mark-xref-articles-p)
  "2Mark the article current-article-number as being read (mode = t) or unread*
 2(mode = nil) . * 2The unread-article-count is updated and returned. * 2If
mark-xref-articles-p* 2is T then xref newsgroups articles are also marked as* 2read
or unread based on mode.*"
  (when (and (send self :mark-article-1 mode)
	     mode
	     mark-xref-articles-p)
    (send self :mark-xref-articles mode))
  unread-article-count)


(defmethod 4(NEWSGROUP-COMPONENT :MARK-ARTICLE-1*) (mode)
  "2Return T if the article was marked as read.*"
  (when (<= low-article-number current-article-number high-article-number)
    (setf *newsrc-changed* t)
    (cond
      (mode
       (when (zerop (aref articles-read-bitmap (- current-article-number low-article-number)))
	 (setf (aref articles-read-bitmap (- current-article-number low-article-number)) 1)
	 (mark-newsgroup-summary-buffer-article mode)
	 (if (zerop unread-article-count)
	     unread-article-count
	     (decf unread-article-count 1))
	 t))
      (t
       (unless (zerop (aref articles-read-bitmap (- current-article-number low-article-number 0)))
	 (setf (aref articles-read-bitmap (- current-article-number low-article-number)) 0)
	 (mark-newsgroup-summary-buffer-article mode)
	 (incf unread-article-count 1)
	 t)))))


(defmethod 4(NEWSGROUP-COMPONENT :MARK-XREF-ARTICLES*) (mode)
  "2Mark the XREF articles as being read (mode = t) or unread (mode =
nil). The unread article count is updated accordingly.*"
  (let (newsgroup-component
	(saved-current-article-number current-article-number)
	(xref (parse-xref (get-header-field self :xref t))))
    (when xref
      (dolist (item xref nil)
	;1;;Only mark subscribed xref articles.  *
	(when (and (setf newsgroup-component (get-newsgroup-component (car item)))
		   (send newsgroup-component :subscribed-p))
	  ;1;Do not mark the current article number if it is also in the xref line.*
	  (unless (and (equal self newsgroup-component) (equal saved-current-article-number (cdr item)))
	    (send newsgroup-component :set-current-article-number (cdr item))
	    (send newsgroup-component :mark-article-1 mode)))))
    (setf current-article-number saved-current-article-number)))


(defmethod 4(NEWSGROUP-COMPONENT :MODERATED-P*) ()
  "2Return T if the newsgroup is moderated.*"
  (char-equal moderated *moderated*))


(defmethod 4(NEWSGROUP-COMPONENT :PARSE-SYSTEM-LINE*) (line &aux result)
  "2Parse a line of text from the system file and save the results into the system component variables.
Return T if successful.  Return NIL if error.*"
  (multiple-value-setq (result newsgroup-string high-article-number low-article-number moderated) (parse-system-line line))
  (and result
       (numberp high-article-number)
       (numberp low-article-number)))


(defmethod 4(NEWSGROUP-COMPONENT :START-OF-NEWSGROUP-P*) ()
  "2Return T if we are at the start of the newsgroup.*"
  (equal current-article-number *start-index*))


(defmethod 4(NEWSGROUP-COMPONENT :START-OR-END-OF-NEWSGROUP-P*) ()
  "2Return T if we are at the start or end of the newsgroup.*"
  (cond
    ((or (equal current-article-number *start-index*)
	 (equal current-article-number *end-index*))
     t)
    ((null current-article-number)
     (setf current-article-number *start-index*)
     t)
    (t
     nil)))


(defmethod 4(NEWSGROUP-COMPONENT :UPDATE-CURRENT-ARTICLE-NUMBER*) (article-number)
  "2Set the current-article-number to the value of ARTICLE-NUMBER.  If
ARTICLE-NUMBER is less than the low article number in the newsgroup then
set the current-article-number to the Low-Article-Number.  If
ARTICLE-NUMBER is greater than the High-Article-Number in the newsgroup
then set the current-article-number to the High-Article-Number.  Return the
article number.*"
  (setf current-article-number
	(cond ((< article-number low-article-number)
	       low-article-number)
	      ((> article-number high-article-number)
	       high-article-number)
	      (t
	       article-number))))


(compile-flavor-methods newsgroup-component)