package message

import (
	"bytes"
	"fmt"
	"net/mail"
	"net/textproto"
)

// ParseHeaderFields parses only the header fields in "fields" from the complete
// header buffer "header". It uses "scratch" as temporary space, which can be
// reused across calls, potentially saving lots of unneeded allocations when only a
// few headers are needed and/or many messages are parsed.
func ParseHeaderFields(header []byte, scratch []byte, fields [][]byte) (textproto.MIMEHeader, error) {
	// todo: should not use mail.ReadMessage, it allocates a bufio.Reader. should implement header parsing ourselves.

	// Gather the raw lines for the fields, with continuations, without the other
	// headers. Put them in a byte slice and only parse those headers. For now, use
	// mail.ReadMessage without letting it do allocations for all headers.
	scratch = scratch[:0]
	var keepcontinuation bool
	for len(header) > 0 {
		if header[0] == ' ' || header[0] == '\t' {
			// Continuation.
			i := bytes.IndexByte(header, '\n')
			if i < 0 {
				i = len(header)
			} else {
				i++
			}
			if keepcontinuation {
				scratch = append(scratch, header[:i]...)
			}
			header = header[i:]
			continue
		}
		i := bytes.IndexByte(header, ':')
		if i < 0 || i > 0 && (header[i-1] == ' ' || header[i-1] == '\t') {
			i = bytes.IndexByte(header, '\n')
			if i < 0 {
				break
			}
			header = header[i+1:]
			keepcontinuation = false
			continue
		}
		k := header[:i]
		keepcontinuation = false
		for _, f := range fields {
			if bytes.EqualFold(k, f) {
				keepcontinuation = true
				break
			}
		}
		i = bytes.IndexByte(header, '\n')
		if i < 0 {
			i = len(header)
		} else {
			i++
		}
		if keepcontinuation {
			scratch = append(scratch, header[:i]...)
		}
		header = header[i:]
	}

	if len(scratch) == 0 {
		return nil, nil
	}

	scratch = append(scratch, "\r\n"...)

	msg, err := mail.ReadMessage(bytes.NewReader(scratch))
	if err != nil {
		return nil, fmt.Errorf("reading message header")
	}
	return textproto.MIMEHeader(msg.Header), nil
}
