/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.commons.net.nntp;

import java.util.ArrayList;
import java.util.StringTokenizer;

/**
 * This is a class that contains the basic state needed for message retrieval and threading.
 * With thanks to Jamie  Zawinski <jwz@jwz.org>
 * @author rwinston <rwinston@apache.org>
 *
 */
public class Article implements Threadable {
	private int articleNumber;
	private String subject;
	private String date;
	private String articleId;
	private String simplifiedSubject;
	private String from;
	private StringBuffer header;
	private StringBuffer references;
	private boolean isReply = false;
	
	public Article kid, next;

	public Article() {
		header = new StringBuffer();
	}

	/**
	 * Adds an arbitrary header key and value to this message's header.
	 * @param name the header name
	 * @param val the header value
	 */
	public void addHeaderField(String name, String val) {
		header.append(name);
		header.append(": ");
		header.append(val);
		header.append('\n');
	}
	
	/**
	 * Adds a message-id to the list of messages that this message references (i.e. replies to)
	 * @param msgId
	 */
	public void addReference(String msgId) {
		if (references == null) {
			references = new StringBuffer();
			references.append("References: ");
		}
		references.append(msgId);
		references.append("\t");
	}

	/**
	 * Returns the MessageId references as an array of Strings
	 * @return an array of message-ids
	 */
	public String[] getReferences() {
		if (references == null)
			return new String[0];
		ArrayList list = new ArrayList();
		int terminator = references.toString().indexOf(':');
		StringTokenizer st =
			new StringTokenizer(references.substring(terminator), "\t");
		while (st.hasMoreTokens()) {
			list.add(st.nextToken());
		}
		return (String[]) list.toArray(new String[list.size()]);
	}
	
	/**
	 * Attempts to parse the subject line for some typical reply signatures, and strip them out
	 *
	 */
	private void simplifySubject() {
			int start = 0;
			String subject = getSubject();
			int len = subject.length();

			boolean done = false;

			while (!done) {
				done = true;

				// skip whitespace
				// "Re: " breaks this
				while (start < len && subject.charAt(start) == ' ') {
					start++;
				}

				if (start < (len - 2)
					&& (subject.charAt(start) == 'r' || subject.charAt(start) == 'R')
					&& (subject.charAt(start + 1) == 'e' || subject.charAt(start + 1) == 'E')) {

					if (subject.charAt(start + 2) == ':') {
						start += 3; // Skip "Re:"
						isReply = true;
						done = false;
					} else if (
						start < (len - 2) 
						&& 
						(subject.charAt(start + 2) == '[' || subject.charAt(start + 2) == '(')) {
                    	
						int i = start + 3;

						while (i < len && subject.charAt(i) >= '0' && subject.charAt(i) <= '9')
							i++;

						if (i < (len - 1)
							&& (subject.charAt(i) == ']' || subject.charAt(i) == ')')
							&& subject.charAt(i + 1) == ':') {
							start = i + 2;
							isReply = true;
							done = false;
						}
					}
				}

				if (simplifiedSubject == "(no subject)")
					simplifiedSubject = "";

				int end = len;

				while (end > start && subject.charAt(end - 1) < ' ')
					end--;

				if (start == 0 && end == len)
					simplifiedSubject = subject;
				else
					simplifiedSubject = subject.substring(start, end);
			}
		}
		
	/**
	 * Recursive method that traverses a pre-threaded graph (or tree) 
	 * of connected Article objects and prints them out.  
	 * @param article the root of the article 'tree'
	 * @param depth the current tree depth
	 */
	public static void printThread(Article article, int depth) {
			for (int i = 0; i < depth; ++i)
				System.out.print("==>");
			System.out.println(article.getSubject() + "\t" + article.getFrom());
			if (article.kid != null)
				printThread(article.kid, depth + 1);
			if (article.next != null)
				printThread(article.next, depth);
	}

	public String getArticleId() {
		return articleId;
	}

	public int getArticleNumber() {
		return articleNumber;
	}

	public String getDate() {
		return date;
	}

	public String getFrom() {
		return from;
	}

	public String getSubject() {
		return subject;
	}

	public void setArticleId(String string) {
		articleId = string;
	}

	public void setArticleNumber(int i) {
		articleNumber = i;
	}

	public void setDate(String string) {
		date = string;
	}

	public void setFrom(String string) {
		from = string;
	}

	public void setSubject(String string) {
		subject = string;
	}

	
	public boolean isDummy() {
		return (getSubject() == null);
	}

	public String messageThreadId() {
		return articleId;
	}
	
	public String[] messageThreadReferences() {
		return getReferences();
	}
	
	public String simplifiedSubject() {
		if(simplifiedSubject == null)
			simplifySubject();
		return simplifiedSubject;
	}

	
	public boolean subjectIsReply() {
		if(simplifiedSubject == null)
			simplifySubject();
		return isReply;
	}

	
	public void setChild(Threadable child) {
		this.kid = (Article) child;
		flushSubjectCache();
	}

	private void flushSubjectCache() {
		simplifiedSubject = null;
	}

	
	public void setNext(Threadable next) {
		this.next = (Article)next;
		flushSubjectCache();
	}

	
	public Threadable makeDummy() {
		return (Threadable)new Article();
	}
}

@@ -2303,16 +2303,24 @@
 rrayList
+%3CString%3E
  list = 
@@ -2332,16 +2332,24 @@
 rrayList
+%3CString%3E
 ();%0A%09%09in
@@ -2562,19 +2562,8 @@
 turn
- (String%5B%5D)
  lis
@@ -6011,20 +6011,8 @@
 urn 
-(Threadable)
 new 

Fix up missing ArrayList types. Remove unnecessary casts.