package message

import (
	"strings"
)

// ThreadSubject returns the base subject to use for matching against other
// messages, to see if they belong to the same thread. A matching subject is
// always required to match to an existing thread, both if
// References/In-Reply-To header(s) are present, and if not.
//
// isResponse indicates if this message is a response, such as a reply or a
// forward.
//
// Subject should already be q/b-word-decoded.
//
// If allowNull is true, base subjects with a \0 can be returned. If not set,
// an empty string is returned if a base subject would have a \0.
func ThreadSubject(subject string, allowNull bool) (threadSubject string, isResponse bool) {
	subject = strings.ToLower(subject)

	// ../rfc/5256:101, Step 1.
	var s string
	for _, c := range subject {
		if c == '\r' {
			continue
		} else if c == ' ' || c == '\n' || c == '\t' {
			if !strings.HasSuffix(s, " ") {
				s += " "
			}
		} else {
			s += string(c)
		}
	}

	// ../rfc/5256:107 ../rfc/5256:811, removing mailing list tag "[...]" and reply/forward "re"/"fwd" prefix.
	removeBlob := func(s string) string {
		for i, c := range s {
			if i == 0 {
				if c != '[' {
					return s
				}
			} else if c == '[' {
				return s
			} else if c == ']' {
				s = s[i+1:]                     // Past [...].
				s = strings.TrimRight(s, " \t") // *WSP
				return s
			}
		}
		return s
	}
	// ../rfc/5256:107 ../rfc/5256:811
	removeLeader := func(s string) string {
		if strings.HasPrefix(s, " ") || strings.HasPrefix(s, "\t") {
			s = s[1:] // WSP
		}

		orig := s

		// Remove zero or more subj-blob
		for {
			prevs := s
			s = removeBlob(s)
			if prevs == s {
				break
			}
		}

		if strings.HasPrefix(s, "re") {
			s = s[2:]
		} else if strings.HasPrefix(s, "fwd") {
			s = s[3:]
		} else if strings.HasPrefix(s, "fw") {
			s = s[2:]
		} else {
			return orig
		}
		s = strings.TrimLeft(s, " \t") // *WSP
		s = removeBlob(s)
		if !strings.HasPrefix(s, ":") {
			return orig
		}
		s = s[1:]
		isResponse = true
		return s
	}

	for {
		// ../rfc/5256:104 ../rfc/5256:817, remove trailing "(fwd)" or WSP, Step 2.
		for {
			prevs := s
			s = strings.TrimRight(s, " \t")
			if strings.HasSuffix(s, "(fwd)") {
				s = strings.TrimSuffix(s, "(fwd)")
				isResponse = true
			}
			if s == prevs {
				break
			}
		}

		for {
			prevs := s
			s = removeLeader(s) // Step 3.
			if ns := removeBlob(s); ns != "" {
				s = ns // Step 4.
			}
			// Step 5, ../rfc/5256:123
			if s == prevs {
				break
			}
		}

		// Step 6. ../rfc/5256:128 ../rfc/5256:805
		if strings.HasPrefix(s, "[fwd:") && strings.HasSuffix(s, "]") {
			s = s[len("[fwd:") : len(s)-1]
			isResponse = true
			continue // From step 2 again.
		}
		break
	}
	if !allowNull && strings.ContainsRune(s, 0) {
		s = ""
	}
	return s, isResponse
}