// Copyright 2019+ Klaus Post. All rights reserved.
// License information can be found in the LICENSE file.
// Based on work by Yann Collet, released under BSD License.

package zstd

import (
	
	
	
	

	
)

type frameDec struct {
	o   decoderOptions
	crc *xxhash.Digest

	WindowSize uint64

	// Frame history passed between blocks
	history history

	rawInput byteBuffer

	// Byte buffer that can be reused for small input blocks.
	bBuf byteBuf

	FrameContentSize uint64

	DictionaryID  uint32
	HasCheckSum   bool
	SingleSegment bool
}

const (
	// MinWindowSize is the minimum Window Size, which is 1 KB.
	MinWindowSize = 1 << 10

	// MaxWindowSize is the maximum encoder window size
	// and the default decoder maximum window size.
	MaxWindowSize = 1 << 29
)

const (
	frameMagic          = "\x28\xb5\x2f\xfd"
	skippableFrameMagic = "\x2a\x4d\x18"
)

func newFrameDec( decoderOptions) *frameDec {
	if .maxWindowSize > .maxDecodedSize {
		.maxWindowSize = .maxDecodedSize
	}
	 := frameDec{
		o: ,
	}
	return &
}

// reset will read the frame header and prepare for block decoding.
// If nothing can be read from the input, io.EOF will be returned.
// Any other error indicated that the stream contained data, but
// there was a problem.
func ( *frameDec) ( byteBuffer) error {
	.HasCheckSum = false
	.WindowSize = 0
	var  [4]byte
	for {
		var  error
		// Check if we can read more...
		,  := .readSmall(1)
		switch  {
		case io.EOF, io.ErrUnexpectedEOF:
			return io.EOF
		case nil:
			[0] = [0]
		default:
			return 
		}
		// Read the rest, don't allow io.ErrUnexpectedEOF
		,  = .readSmall(3)
		switch  {
		case io.EOF:
			return io.EOF
		case nil:
			copy([1:], )
		default:
			return 
		}

		if string([1:4]) != skippableFrameMagic || [0]&0xf0 != 0x50 {
			if debugDecoder {
				println("Not skippable", hex.EncodeToString([:]), hex.EncodeToString([]byte(skippableFrameMagic)))
			}
			// Break if not skippable frame.
			break
		}
		// Read size to skip
		,  = .readSmall(4)
		if  != nil {
			if debugDecoder {
				println("Reading Frame Size", )
			}
			return 
		}
		 := uint32([0]) | (uint32([1]) << 8) | (uint32([2]) << 16) | (uint32([3]) << 24)
		println("Skipping frame with", , "bytes.")
		 = .skipN(int64())
		if  != nil {
			if debugDecoder {
				println("Reading discarded frame", )
			}
			return 
		}
	}
	if string([:]) != frameMagic {
		if debugDecoder {
			println("Got magic numbers: ", , "want:", []byte(frameMagic))
		}
		return ErrMagicMismatch
	}

	// Read Frame_Header_Descriptor
	,  := .readByte()
	if  != nil {
		if debugDecoder {
			println("Reading Frame_Header_Descriptor", )
		}
		return 
	}
	.SingleSegment = &(1<<5) != 0

	if &(1<<3) != 0 {
		return errors.New("reserved bit set on frame header")
	}

	// Read Window_Descriptor
	// https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#window_descriptor
	.WindowSize = 0
	if !.SingleSegment {
		,  := .readByte()
		if  != nil {
			if debugDecoder {
				println("Reading Window_Descriptor", )
			}
			return 
		}
		if debugDecoder {
			printf("raw: %x, mantissa: %d, exponent: %d\n", , &7, >>3)
		}
		 := 10 + ( >> 3)
		 := uint64(1) << 
		 := ( / 8) * uint64(&0x7)
		.WindowSize =  + 
	}

	// Read Dictionary_ID
	// https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#dictionary_id
	.DictionaryID = 0
	if  :=  & 3;  != 0 {
		if  == 3 {
			 = 4
		}

		,  := .readSmall(int())
		if  != nil {
			println("Reading Dictionary_ID", )
			return 
		}
		var  uint32
		switch len() {
		case 1:
			 = uint32([0])
		case 2:
			 = uint32([0]) | (uint32([1]) << 8)
		case 4:
			 = uint32([0]) | (uint32([1]) << 8) | (uint32([2]) << 16) | (uint32([3]) << 24)
		}
		if debugDecoder {
			println("Dict size", , "ID:", )
		}
		.DictionaryID = 
	}

	// Read Frame_Content_Size
	// https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#frame_content_size
	var  int
	 :=  >> 6
	switch  {
	case 0:
		if .SingleSegment {
			 = 1
		}
	default:
		 = 1 << 
	}
	.FrameContentSize = fcsUnknown
	if  > 0 {
		,  := .readSmall()
		if  != nil {
			println("Reading Frame content", )
			return 
		}
		switch len() {
		case 1:
			.FrameContentSize = uint64([0])
		case 2:
			// When FCS_Field_Size is 2, the offset of 256 is added.
			.FrameContentSize = uint64([0]) | (uint64([1]) << 8) + 256
		case 4:
			.FrameContentSize = uint64([0]) | (uint64([1]) << 8) | (uint64([2]) << 16) | (uint64([3]) << 24)
		case 8:
			 := uint32([0]) | (uint32([1]) << 8) | (uint32([2]) << 16) | (uint32([3]) << 24)
			 := uint32([4]) | (uint32([5]) << 8) | (uint32([6]) << 16) | (uint32([7]) << 24)
			.FrameContentSize = uint64() | (uint64() << 32)
		}
		if debugDecoder {
			println("Read FCS:", .FrameContentSize)
		}
	}

	// Move this to shared.
	.HasCheckSum = &(1<<2) != 0
	if .HasCheckSum {
		if .crc == nil {
			.crc = xxhash.New()
		}
		.crc.Reset()
	}

	if .WindowSize > .o.maxWindowSize {
		if debugDecoder {
			printf("window size %d > max %d\n", .WindowSize, .o.maxWindowSize)
		}
		return ErrWindowSizeExceeded
	}

	if .WindowSize == 0 && .SingleSegment {
		// We may not need window in this case.
		.WindowSize = max(.FrameContentSize, MinWindowSize)
		if .WindowSize > .o.maxDecodedSize {
			if debugDecoder {
				printf("window size %d > max %d\n", .WindowSize, .o.maxWindowSize)
			}
			return ErrDecoderSizeExceeded
		}
	}

	// The minimum Window_Size is 1 KB.
	if .WindowSize < MinWindowSize {
		if debugDecoder {
			println("got window size: ", .WindowSize)
		}
		return ErrWindowSizeTooSmall
	}
	.history.windowSize = int(.WindowSize)
	if !.o.lowMem || .history.windowSize < maxBlockSize {
		// Alloc 2x window size if not low-mem, or window size below 2MB.
		.history.allocFrameBuffer = .history.windowSize * 2
	} else {
		if .o.lowMem {
			// Alloc with 1MB extra.
			.history.allocFrameBuffer = .history.windowSize + maxBlockSize/2
		} else {
			// Alloc with 2MB extra.
			.history.allocFrameBuffer = .history.windowSize + maxBlockSize
		}
	}

	if debugDecoder {
		println("Frame: Dict:", .DictionaryID, "FrameContentSize:", .FrameContentSize, "singleseg:", .SingleSegment, "window:", .WindowSize, "crc:", .HasCheckSum)
	}

	// history contains input - maybe we do something
	.rawInput = 
	return nil
}

// next will start decoding the next block from stream.
func ( *frameDec) ( *blockDec) error {
	if debugDecoder {
		println("decoding new block")
	}
	 := .reset(.rawInput, .WindowSize)
	if  != nil {
		println("block error:", )
		// Signal the frame decoder we have a problem.
		.sendErr()
		return 
	}
	return nil
}

// checkCRC will check the checksum, assuming the frame has one.
// Will return ErrCRCMismatch if crc check failed, otherwise nil.
func ( *frameDec) () error {
	// We can overwrite upper tmp now
	,  := .rawInput.readSmall(4)
	if  != nil {
		println("CRC missing?", )
		return 
	}

	 := binary.LittleEndian.Uint32([:4])
	 := uint32(.crc.Sum64())

	if  !=  {
		if debugDecoder {
			printf("CRC check failed: got %08x, want %08x\n", , )
		}
		return ErrCRCMismatch
	}
	if debugDecoder {
		printf("CRC ok %08x\n", )
	}
	return nil
}

// consumeCRC skips over the checksum, assuming the frame has one.
func ( *frameDec) () error {
	,  := .rawInput.readSmall(4)
	if  != nil {
		println("CRC missing?", )
	}
	return 
}

// runDecoder will run the decoder for the remainder of the frame.
func ( *frameDec) ( []byte,  *blockDec) ([]byte, error) {
	 := .history.b

	// We use the history for output to avoid copying it.
	.history.b = 
	.history.ignoreBuffer = len()
	// Store input length, so we only check new data.
	 := len()
	.history.decoders.maxSyncLen = 0
	if .o.limitToCap {
		.history.decoders.maxSyncLen = uint64(cap() - len())
	}
	if .FrameContentSize != fcsUnknown {
		if !.o.limitToCap || .FrameContentSize+uint64(len()) < .history.decoders.maxSyncLen {
			.history.decoders.maxSyncLen = .FrameContentSize + uint64(len())
		}
		if .history.decoders.maxSyncLen > .o.maxDecodedSize {
			if debugDecoder {
				println("maxSyncLen:", .history.decoders.maxSyncLen, "> maxDecodedSize:", .o.maxDecodedSize)
			}
			return , ErrDecoderSizeExceeded
		}
		if debugDecoder {
			println("maxSyncLen:", .history.decoders.maxSyncLen)
		}
		if !.o.limitToCap && uint64(cap()) < .history.decoders.maxSyncLen {
			// Alloc for output
			 := make([]byte, len(), .history.decoders.maxSyncLen+compressedBlockOverAlloc)
			copy(, )
			 = 
		}
	}
	var  error
	for {
		 = .reset(.rawInput, .WindowSize)
		if  != nil {
			break
		}
		if debugDecoder {
			println("next block:", )
		}
		 = .decodeBuf(&.history)
		if  != nil {
			break
		}
		if uint64(len(.history.b)-) > .o.maxDecodedSize {
			println("runDecoder: maxDecodedSize exceeded", uint64(len(.history.b)-), ">", .o.maxDecodedSize)
			 = ErrDecoderSizeExceeded
			break
		}
		if .o.limitToCap && len(.history.b) > cap() {
			println("runDecoder: cap exceeded", uint64(len(.history.b)), ">", cap())
			 = ErrDecoderSizeExceeded
			break
		}
		if uint64(len(.history.b)-) > .FrameContentSize {
			println("runDecoder: FrameContentSize exceeded", uint64(len(.history.b)-), ">", .FrameContentSize)
			 = ErrFrameSizeExceeded
			break
		}
		if .Last {
			break
		}
		if debugDecoder {
			println("runDecoder: FrameContentSize", uint64(len(.history.b)-), "<=", .FrameContentSize)
		}
	}
	 = .history.b
	if  == nil {
		if .FrameContentSize != fcsUnknown && uint64(len(.history.b)-) != .FrameContentSize {
			 = ErrFrameSizeMismatch
		} else if .HasCheckSum {
			if .o.ignoreChecksum {
				 = .consumeCRC()
			} else {
				.crc.Write([:])
				 = .checkCRC()
			}
		}
	}
	.history.b = 
	return , 
}