;;; -*- Mode:Common-Lisp; Package:NEWS; 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.*
	    )
	   (newsrc-component)
  :settable-instance-variables
  :initable-instance-variables
  :gettable-instance-variables)


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


(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 (= (aref articles-read-bitmap i) 0) (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*))


(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*))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;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))
      (setf *newsrc-changed* t)
      (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
	  (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*) (&optional (mode t))
  "2Mark all articles as being read (mode = t) or unread (mode = nil) and update the unread-article-count.*"

  (setf *newsrc-changed* t)
  (loop with saved-current-article-number = current-article-number
	for i from low-article-number to high-article-number
	finally (setf current-article-number saved-current-article-number)
	do
	(setf current-article-number i)
	(send self :mark-article mode))
  unread-article-count)


(defmethod 4(NEWSGROUP-COMPONENT :MARK-ARTICLE*) (&optional (mode t))
  "2Mark the article current-article-number as being read (mode = t) or unread
 (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
read (i.e. * 2mode = t)*"

  (when (and (send self :mark-article-1 mode)
	     mode
	     *mark-xref-articles-p*)
    (let (newsgroup-component (xref (parse-xref (get-header-field self current-article-number :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))
	    (send newsgroup-component :set-current-article-number (cdr item))
	    (send newsgroup-component :mark-article-1 t))))))
  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)
	 (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)
	 (incf unread-article-count 1))))))


(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 :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))))
