package brotli

import (
	
	
	
	
)

// HTTPCompressor chooses a compression method (brotli, gzip, or none) based on
// the Accept-Encoding header, sets the Content-Encoding header, and returns a
// WriteCloser that implements that compression. The Close method must be called
// before the current HTTP handler returns.
func ( http.ResponseWriter,  *http.Request) io.WriteCloser {
	if .Header().Get("Vary") == "" {
		.Header().Set("Vary", "Accept-Encoding")
	}

	 := negotiateContentEncoding(, []string{"br", "gzip"})
	switch  {
	case "br":
		.Header().Set("Content-Encoding", "br")
		return NewWriter()
	case "gzip":
		.Header().Set("Content-Encoding", "gzip")
		return gzip.NewWriter()
	}
	return nopCloser{}
}

// negotiateContentEncoding returns the best offered content encoding for the
// request's Accept-Encoding header. If two offers match with equal weight and
// then the offer earlier in the list is preferred. If no offers are
// acceptable, then "" is returned.
func negotiateContentEncoding( *http.Request,  []string) string {
	 := "identity"
	 := -1.0
	 := parseAccept(.Header, "Accept-Encoding")
	for ,  := range  {
		for ,  := range  {
			if .Q >  &&
				(.Value == "*" || .Value == ) {
				 = .Q
				 = 
			}
		}
	}
	if  == 0 {
		 = ""
	}
	return 
}

// acceptSpec describes an Accept* header.
type acceptSpec struct {
	Value string
	Q     float64
}

// parseAccept parses Accept* headers.
func parseAccept( http.Header,  string) ( []acceptSpec) {
:
	for ,  := range [] {
		for {
			var  acceptSpec
			.Value,  = expectTokenSlash()
			if .Value == "" {
				continue 
			}
			.Q = 1.0
			 = skipSpace()
			if strings.HasPrefix(, ";") {
				 = skipSpace([1:])
				if !strings.HasPrefix(, "q=") {
					continue 
				}
				.Q,  = expectQuality([2:])
				if .Q < 0.0 {
					continue 
				}
			}
			 = append(, )
			 = skipSpace()
			if !strings.HasPrefix(, ",") {
				continue 
			}
			 = skipSpace([1:])
		}
	}
	return
}

func skipSpace( string) ( string) {
	 := 0
	for ;  < len(); ++ {
		if octetTypes[[]]&isSpace == 0 {
			break
		}
	}
	return [:]
}

func expectTokenSlash( string) (,  string) {
	 := 0
	for ;  < len(); ++ {
		 := []
		if (octetTypes[]&isToken == 0) &&  != '/' {
			break
		}
	}
	return [:], [:]
}

func expectQuality( string) ( float64,  string) {
	switch {
	case len() == 0:
		return -1, ""
	case [0] == '0':
		 = 0
	case [0] == '1':
		 = 1
	default:
		return -1, ""
	}
	 = [1:]
	if !strings.HasPrefix(, ".") {
		return , 
	}
	 = [1:]
	 := 0
	 := 0
	 := 1
	for ;  < len(); ++ {
		 := []
		if  < '0' ||  > '9' {
			break
		}
		 = *10 + int() - '0'
		 *= 10
	}
	return  + float64()/float64(), [:]
}

// Octet types from RFC 2616.
var octetTypes [256]octetType

type octetType byte

const (
	isToken octetType = 1 << iota
	isSpace
)

func init() {
	// OCTET      = <any 8-bit sequence of data>
	// CHAR       = <any US-ASCII character (octets 0 - 127)>
	// CTL        = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
	// CR         = <US-ASCII CR, carriage return (13)>
	// LF         = <US-ASCII LF, linefeed (10)>
	// SP         = <US-ASCII SP, space (32)>
	// HT         = <US-ASCII HT, horizontal-tab (9)>
	// <">        = <US-ASCII double-quote mark (34)>
	// CRLF       = CR LF
	// LWS        = [CRLF] 1*( SP | HT )
	// TEXT       = <any OCTET except CTLs, but including LWS>
	// separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <">
	//              | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT
	// token      = 1*<any CHAR except CTLs or separators>
	// qdtext     = <any TEXT except <">>

	for  := 0;  < 256; ++ {
		var  octetType
		 :=  <= 31 ||  == 127
		 := 0 <=  &&  <= 127
		 := strings.ContainsRune(" \t\"(),/:;<=>?@[]\\{}", rune())
		if strings.ContainsRune(" \t\r\n", rune()) {
			 |= isSpace
		}
		if  && ! && ! {
			 |= isToken
		}
		octetTypes[] = 
	}
}