package commonsNet.nntp

import std.io.OutputStream
import std.console.Console
import std.collection.ArrayList

/**
 * 包含消息检索和线程处理所需的基本状态的类。
 */
public class Article <: Threadable & ToString {

    /**
     * 遍历已连接的Article对象的线程树并将其打印出来的递归方法。
     *
     * @param article 文章树的根
     */
    public static func printThread(article: Article): Unit {
        printThread(article, 0, Console.stdOut)
    }

    /**
     * 遍历已连接的Article对象的线程树并将其打印出来的递归方法。
     *
     * @param article 文章树的根
     * @param depth   当前树的深度
     */
    public static func printThread(article: Article, depth: Int64): Unit {
        printThread(article, depth, Console.stdOut)
    }

    /**
     * 遍历已连接的Article对象的线程树并将其打印出来的递归方法。
     *
     * @param article 文章树的根
     * @param depth   当前树的深度
     * @param ps      输出流
     */
    public static func printThread(article: Article, depth: Int64, out: OutputStream): Unit {
        for (i in 0..depth) {
            out.write("==>".toArray())
        }

        out.write("${article.getSubject()}\t${article.getFrom()}\t${article.getArticleId()}\t\n".toArray())

        if (let Some(kid) <- article.kid) {
            printThread(kid, depth + 1, out)
        }

        if (let Some(next) <- article.next) {
            printThread(next, depth, out)
        }
    }

    /**
     * 遍历已连接的Article对象的线程树并将其打印出来的递归方法。
     *
     * @param article 文章树的根
     * @param ps      输出流
     */
    public static func printThread(article: Article, out: OutputStream) {
        printThread(article, 0, out)
    }

    private var articleNumber: Int64 = -1
    private var subject: String = ""
    private var date: String = ""
    private var articleId: String = ""

    private var simplifiedSubjectCache: String = ""

    private var `from`: String = ""
    private var references: ?ArrayList<String> = None

    private var isReply: Bool = false

    public var kid: ?Article = None
    public var next: ?Article = None

    public init() {
        articleNumber = -1 // isDummy
    }

    /**
     * 将消息id添加到此消息的引用列表(即回复)
     * @param msgId 要添加的消息id
     */
    public func addReference(msgId: String) {
        if (msgId.isEmpty()) {
            return
        }
        if (references == None) {
            references = ArrayList<String>()
        }
        let refs = references.getOrThrow()
        isReply = true
        refs.appendAll(msgId.split(" "))
    }

    /**
     * 清除主题缓存
     */
    private func flushSubjectCache() {
        simplifiedSubjectCache = ""
    }

    public func getArticleId() {
        return articleId
    }

    public func getArticleNumber() {
        return articleNumber
    }

    public func getDate() {
        return date
    }

    public func getFrom() {
        return `from`
    }

    /**
     * 以字符串数组的形式返回引用的消息id
     *
     * @return 消息id数组
     */
    public func getReferences(): Array<String> {
        return references?.toArray() ?? []
    }

    public func getSubject() {
        return subject
    }

    public override func isDummy() {
        return articleNumber == -1
    }

    public override func makeDummy(): Threadable {
        let dummy = Article()
        dummy.setSubject("[Dummy]")
        return dummy
    }

    public override func messageThreadId() {
        return articleId
    }

    public override func messageThreadReferences() {
        return getReferences()
    }

    public func setArticleId(string: String): Unit {
        articleId = string
    }

    public func setArticleNumber(num: Int64): Unit {
        articleNumber = num
    }

    public override func setChild(child: ?Threadable): Unit {
        this.kid = if (let Some(x) <- child) {
            x as Article
        } else {
            None
        }
        flushSubjectCache()
    }

    public func setDate(str: String): Unit {
        date = str
    }

    public func setFrom(str: String): Unit {
        `from` = str
    }

    public override func setNext(next: ?Threadable): Unit {
        this.next = if (let Some(x) <- next) {
            x as Article
        } else {
            None
        }
        flushSubjectCache()
    }

    public func setSubject(sub: String): Unit {
        subject = sub
    }

    public override func simplifiedSubject(): String {
        if (simplifiedSubjectCache.isEmpty()) {
            simplifySubject()
        }
        return simplifiedSubjectCache
    }

    /**
     * 尝试解析主题行中的一些典型回复标志,并将其删除
     */
    private func simplifySubject(): Unit {
        var start = 0
        let subject = getSubject()
        let len = subject.size
        var done = false
        while (!done) {
            done = true

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

            if (start < len - 2 && (subject[start] == b'r' || subject[start] == b'R') && (subject[start + 1] == b'e' ||
                subject[start + 1] == b'E')) {
                if (subject[start + 2] == b':') {
                    start += 3 // Skip "Re:"
                    done = false
                } else if (start < len - 2 && (subject[start + 2] == b'[' || subject[start + 2] == b'(')) {
                    var i = start + 3

                    while (i < len && subject[i] >= b'0' && subject[i] <= b'9') {
                        i++
                    }

                    if (i < len - 1 && (subject[i] == b']' || subject[i] == b')') && subject[i + 1] == b':') {
                        start = i + 2
                        done = false
                    }
                }
            }

            if ("(no subject)" == simplifiedSubjectCache) {
                simplifiedSubjectCache = ""
            }

            var end = len

            while (end > start && subject[end - 1] < b' ') {
                end--
            }

            if (start == 0 && end == len) {
                simplifiedSubjectCache = subject
            } else {
                simplifiedSubjectCache = subject[start..end]
            }
        }
    }

    public override func subjectIsReply() {
        return isReply
    }

    public func toString() {
        return articleNumber.toString() + " " + articleId + " " + subject;
    }
}