package lz4

import (
	
	

	
	
	
)

var readerStates = []aState{
	noState:     newState,
	errorState:  newState,
	newState:    readState,
	readState:   closedState,
	closedState: newState,
}

// NewReader returns a new LZ4 frame decoder.
func ( io.Reader) *Reader {
	return newReader(, false)
}

func newReader( io.Reader,  bool) *Reader {
	 := &Reader{frame: lz4stream.NewFrame()}
	.state.init(readerStates)
	_ = .Apply(DefaultConcurrency, defaultOnBlockDone)
	.Reset()
	return 
}

// Reader allows reading an LZ4 stream.
type Reader struct {
	state   _State
	src     io.Reader        // source reader
	num     int              // concurrency level
	frame   *lz4stream.Frame // frame being read
	data    []byte           // block buffer allocated in non concurrent mode
	reads   chan []byte      // pending data
	idx     int              // size of pending data
	handler func(int)
	cum     uint32
	dict    []byte
}

func (*Reader) () {}

func ( *Reader) ( ...Option) ( error) {
	defer .state.check(&)
	switch .state.state {
	case newState:
	case errorState:
		return .state.err
	default:
		return lz4errors.ErrOptionClosedOrError
	}
	for ,  := range  {
		if  = ();  != nil {
			return
		}
	}
	return
}

// Size returns the size of the underlying uncompressed data, if set in the stream.
func ( *Reader) () int {
	switch .state.state {
	case readState, closedState:
		if .frame.Descriptor.Flags.Size() {
			return int(.frame.Descriptor.ContentSize)
		}
	}
	return 0
}

func ( *Reader) () bool {
	return .num == 1
}

func ( *Reader) () error {
	 := .frame.ParseHeaders(.src)
	if  != nil {
		return 
	}
	if !.frame.Descriptor.Flags.BlockIndependence() {
		// We can't decompress dependent blocks concurrently.
		// Instead of throwing an error to the user, silently drop concurrency
		.num = 1
	}
	,  := .frame.InitR(.src, .num)
	if  != nil {
		return 
	}
	.reads = 
	.idx = 0
	 := .frame.Descriptor.Flags.BlockSizeIndex()
	.data = .Get()
	.cum = 0
	return nil
}

func ( *Reader) ( []byte) ( int,  error) {
	defer .state.check(&)
	switch .state.state {
	case readState:
	case closedState, errorState:
		return 0, .state.err
	case newState:
		// First initialization.
		if  = .init(); .state.next() {
			return
		}
	default:
		return 0, .state.fail()
	}
	for len() > 0 {
		var  int
		if .idx == 0 {
			if .isNotConcurrent() {
				,  = .read()
			} else {
				lz4block.Put(.data)
				.data = <-.reads
				if len(.data) == 0 {
					// No uncompressed data: something went wrong or we are done.
					 = .frame.Blocks.ErrorR()
				}
			}
			switch  {
			case nil:
			case io.EOF:
				if  := .frame.CloseR(.src);  != nil {
					 = 
				}
				lz4block.Put(.data)
				.data = nil
				return
			default:
				return
			}
		}
		if  == 0 {
			// Fill buf with buffered data.
			 = copy(, .data[.idx:])
			.idx += 
			if .idx == len(.data) {
				// All data read, get ready for the next Read.
				.idx = 0
			}
		}
		 = [:]
		 += 
		.handler()
	}
	return
}

// read uncompresses the next block as follow:
// - if buf has enough room, the block is uncompressed into it directly
//   and the lenght of used space is returned
// - else, the uncompress data is stored in r.data and 0 is returned
func ( *Reader) ( []byte) (int, error) {
	 := .frame.Blocks.Block
	,  := .Read(.frame, .src, .cum)
	if  != nil {
		return 0, 
	}
	var  bool
	 := .data[:cap(.data)]
	if len() >= len() {
		// Uncompress directly into buf.
		 = true
		 = 
	}
	,  = .Uncompress(.frame, , .dict, true)
	if  != nil {
		return 0, 
	}
	if !.frame.Descriptor.Flags.BlockIndependence() {
		if len(.dict)+len() > 128*1024 {
			 := 64*1024 - len()
			if  < 0 {
				 = 0
			}
			.dict = .dict[len(.dict)-:]
		}
		.dict = append(.dict, ...)
	}
	.cum += uint32(len())
	if  {
		return len(), nil
	}
	.data = 
	return 0, nil
}

// Reset clears the state of the Reader r such that it is equivalent to its
// initial state from NewReader, but instead reading from reader.
// No access to reader is performed.
func ( *Reader) ( io.Reader) {
	if .data != nil {
		lz4block.Put(.data)
		.data = nil
	}
	.frame.Reset(.num)
	.state.reset()
	.src = 
	.reads = nil
}

// WriteTo efficiently uncompresses the data from the Reader underlying source to w.
func ( *Reader) ( io.Writer) ( int64,  error) {
	switch .state.state {
	case closedState, errorState:
		return 0, .state.err
	case newState:
		if  = .init(); .state.next() {
			return
		}
	default:
		return 0, .state.fail()
	}
	defer .state.nextd(&)

	var  []byte
	if .isNotConcurrent() {
		 := .frame.Descriptor.Flags.BlockSizeIndex()
		 = .Get()
		defer lz4block.Put()
	}
	for {
		var  int
		var  []byte
		if .isNotConcurrent() {
			,  = .read()
			 = [:]
		} else {
			lz4block.Put()
			 = <-.reads
			 = len()
			if  == 0 {
				// No uncompressed data: something went wrong or we are done.
				 = .frame.Blocks.ErrorR()
			}
		}
		switch  {
		case nil:
		case io.EOF:
			 = .frame.CloseR(.src)
			return
		default:
			return
		}
		.handler()
		,  = .Write()
		 += int64()
		if  != nil {
			return
		}
	}
}

// ValidFrameHeader returns a bool indicating if the given bytes slice matches a LZ4 header.
func ( []byte) (bool, error) {
	 := lz4stream.NewFrame()
	 := .ParseHeaders(bytes.NewReader())
	if  == nil {
		return true, nil
	}
	if  == lz4errors.ErrInvalidFrame {
		return false, nil
	}
	return false, 
}