// Copyright (c) HashiCorp, Inc
// SPDX-License-Identifier: MPL-2.0

package segment

import (
	
	
	
	

	
)

// Reader allows reading logs from a segment file.
type Reader struct {
	info types.SegmentInfo
	rf   types.ReadableFile

	scratchFrameHeader []byte

	// tail optionally providers an interface to the writer state when this is an
	// unsealed segment so we can fetch from it's in-memory index.
	tail tailWriter
}

type tailWriter interface {
	OffsetForFrame(idx uint64) (uint32, error)
}

func openReader( types.SegmentInfo,  types.ReadableFile) (*Reader, error) {
	 := &Reader{
		info: ,
		rf:   ,
	}

	return , nil
}

// Close implements io.Closer
func ( *Reader) () error {
	return .rf.Close()
}

// GetLog returns the raw log entry bytes associated with idx. If the log
// doesn't exist in this segment types.ErrNotFound must be returned.
func ( *Reader) ( uint64,  *types.LogEntry) error {
	,  := .findFrameOffset()
	if  != nil {
		return 
	}

	if ,  := .readFrame(, );  != nil {
		return 
	}
	return nil
}

func ( *Reader) ( uint32,  *types.LogEntry) (frameHeader, error) {
	if cap(.scratchFrameHeader) < frameHeaderLen {
		.scratchFrameHeader = make([]byte, frameHeaderLen)
	}
	.scratchFrameHeader = .scratchFrameHeader[:frameHeaderLen]
	,  := .rf.ReadAt(.scratchFrameHeader, int64())
	if errors.Is(, io.EOF) &&  >= frameHeaderLen {
		// We might have hit EOF just because our read buffer (at least 64KiB) might
		// be larger than the space left in the file (say if files are tiny or if we
		// are reading a frame near the end.). So don't treat EOF as an error as
		// long as we have actually managed to read a frameHeader - we'll work out
		// if we got the whole thing or not below.
		 = nil

		// Re-slice buf.Bs so it's len() reflect only what we actually managed to
		// read. Note this doesn't impact the buffer length when it's returned to
		// the pool which will still return the whole cap.
		.Data = .Data[:]
	}
	if  != nil {
		return frameHeader{}, 
	}
	,  := readFrameHeader(.scratchFrameHeader)
	if  != nil {
		return , 
	}

	// Need to read more bytes, validate that len is a sensible number
	if .len > MaxEntrySize {
		return , fmt.Errorf("%w: frame header indicates a record larger than MaxEntrySize (%d bytes)", types.ErrCorrupt, MaxEntrySize)
	}

	if cap(.Data) < int(.len) {
		.Data = make([]byte, .len)
	}
	.Data = .Data[:.len]

	if ,  := .rf.ReadAt(.Data, int64(+frameHeaderLen));  != nil {
		return , 
	}
	return , nil
}

func ( *Reader) ( uint64) (uint32, error) {
	if .tail != nil {
		// This is not a sealed segment.
		return .tail.OffsetForFrame()
	}

	// Sealed segment, read from the on-disk index block.
	if .info.IndexStart == 0 {
		return 0, fmt.Errorf("sealed segment has no index block")
	}

	if  < .info.MinIndex || (.info.MaxIndex > 0 &&  > .info.MaxIndex) {
		return 0, types.ErrNotFound
	}

	// IndexStart is the offset to the first entry in the index array. We need to
	// find the byte offset to the Nth entry
	 := ( - .info.BaseIndex)
	 := .info.IndexStart + ( * 4)

	var  [4]byte
	,  := .rf.ReadAt([:], int64())
	if  == io.EOF &&  == 4 {
		// Read all of it just happened to be at end of file, ignore
		 = nil
	}
	if  != nil {
		return 0, fmt.Errorf("failed to read segment index: %w", )
	}
	 := binary.LittleEndian.Uint32([:])
	return , nil
}