package brotli

import 

// An Encoder implements the matchfinder.Encoder interface, writing in Brotli format.
type Encoder struct {
	wroteHeader bool
	bw          bitWriter
	distCache   []distanceCode
}

func ( *Encoder) () {
	.wroteHeader = false
	.bw = bitWriter{}
}

func ( *Encoder) ( []byte,  []byte,  []matchfinder.Match,  bool) []byte {
	.bw.dst = 
	if !.wroteHeader {
		.bw.writeBits(4, 15)
		.wroteHeader = true
	}

	if len() == 0 {
		if  {
			.bw.writeBits(2, 3) // islast + isempty
			.bw.jumpToByteBoundary()
			return .bw.dst
		}
		return 
	}

	var  [256]uint32
	var  [704]uint32
	var  [64]uint32
	 := 0
	 := 0
	 := 0

	if len(.distCache) < len() {
		.distCache = make([]distanceCode, len())
	}

	// first pass: build the histograms
	 := 0

	// d is the ring buffer of the last 4 distances.
	 := [4]int{-10, -10, -10, -10}
	for ,  := range  {
		if .Unmatched > 0 {
			for ,  := range [ : +.Unmatched] {
				[]++
			}
			 += .Unmatched
		}

		 := getInsertLengthCode(uint(.Unmatched))
		 := getCopyLengthCode(uint(.Length))
		if .Length == 0 {
			// If the stream ends with unmatched bytes, we need a dummy copy length.
			 = 2
		}
		 := combineLengthCodes(, , false)
		[]++
		++

		if  >= 128 && .Length != 0 {
			var  distanceCode
			switch .Distance {
			case [3]:
				.code = 0
			case [2]:
				.code = 1
			case [1]:
				.code = 2
			case [0]:
				.code = 3
			case [3] - 1:
				.code = 4
			case [3] + 1:
				.code = 5
			case [3] - 2:
				.code = 6
			case [3] + 2:
				.code = 7
			case [3] - 3:
				.code = 8
			case [3] + 3:
				.code = 9

				// In my testing, codes 10–15 actually reduced the compression ratio.

			default:
				 = getDistanceCode(.Distance)
			}
			.distCache[] = 
			[.code]++
			++
			if .code != 0 {
				[0], [1], [2], [3] = [1], [2], [3], .Distance
			}
		}

		 += .Unmatched + .Length
	}

	storeMetaBlockHeaderBW(uint(len()), false, &.bw)
	.bw.writeBits(13, 0)

	var  [256]byte
	var  [256]uint16
	buildAndStoreHuffmanTreeFastBW([:], uint(), 8, [:], [:], &.bw)

	var  [704]byte
	var  [704]uint16
	buildAndStoreHuffmanTreeFastBW([:], uint(), 10, [:], [:], &.bw)

	var  [64]byte
	var  [64]uint16
	buildAndStoreHuffmanTreeFastBW([:], uint(), 6, [:], [:], &.bw)

	 = 0
	for ,  := range  {
		 := getInsertLengthCode(uint(.Unmatched))
		 := getCopyLengthCode(uint(.Length))
		if .Length == 0 {
			// If the stream ends with unmatched bytes, we need a dummy copy length.
			 = 2
		}
		 := combineLengthCodes(, , false)
		.bw.writeBits(uint([]), uint64([]))
		if kInsExtra[] > 0 {
			.bw.writeBits(uint(kInsExtra[]), uint64(.Unmatched)-uint64(kInsBase[]))
		}
		if kCopyExtra[] > 0 {
			.bw.writeBits(uint(kCopyExtra[]), uint64(.Length)-uint64(kCopyBase[]))
		}

		if .Unmatched > 0 {
			for ,  := range [ : +.Unmatched] {
				.bw.writeBits(uint([]), uint64([]))
			}
		}

		if  >= 128 && .Length != 0 {
			 := .distCache[]
			.bw.writeBits(uint([.code]), uint64([.code]))
			if .nExtra > 0 {
				.bw.writeBits(.nExtra, .extraBits)
			}
		}

		 += .Unmatched + .Length
	}

	if  {
		.bw.writeBits(2, 3) // islast + isempty
		.bw.jumpToByteBoundary()
	}
	return .bw.dst
}

type distanceCode struct {
	code      int
	nExtra    uint
	extraBits uint64
}

func getDistanceCode( int) distanceCode {
	 :=  + 3
	 := log2FloorNonZero(uint()) - 1
	 := ( >> ) & 1
	 := (2 + ) << 
	 := int(2*(-1)) +  + 16
	 :=  - 
	return distanceCode{, uint(), uint64()}
}