package ioseq

import (
	
	
	
	
)

// Seq represents a sequence of byte slices. It's somewhat equivalent to
// [Reader], although simpler in some respects.
// See [SeqFromReader] and [ReaderFromSeq] for a way to convert
// between [Seq] and [Reader].
//
// Each element in the sequence must have either a non-nil byte slice or
// a non-nil error; a producer should never produce either (nil, nil) or
// a non-nil slice and a non-nil error.
//
// The sequence always ends at the first error: if there are temporary
// errors, it's up to the producer to deal with them.
//
// The code ranging over the sequence must not use the slice outside of
// the loop or across iterations; that is, the receiver owns a slice
// until that particular iteration ends.
//
// Callers must not mutate the slice. [TODO perhaps it might be OK to
// allow callers to mutate, but not append to, the slice].
type Seq = iter.Seq2[[]byte, error]

// SeqFromReader returns a [Seq] that reads from r, allocating one buffer
// of the given size to do so unless r implements [WriterTo], in which
// case no buffer is needed.
func ( io.Reader,  int) Seq {
	if ,  := .(io.WriterTo);  {
		return func( func([]byte, error) bool) {
			 := true
			,  := .WriteTo(writerFunc(func( []byte) (int, error) {
				if !(, nil) {
					 = false
					return 0, ErrSequenceTerminated
				}
				return len(), nil
			}))
			if  != nil &&  {
				(nil, )
			}
		}
	}
	return func( func([]byte, error) bool) {
		 := make([]byte, )
		for {
			,  := .Read()
			if  != nil {
				if  == io.EOF {
					 = nil
				}
				// Note: we _could_ call slices.Clip on the buffer
				// here, but there's no particular reason to do so:
				// if the rest of the buffer is overwritten by the
				// consumer, it doesn't make any difference.
				if  > 0 && !([:], nil) {
					return
				}
				if  != nil {
					(nil, )
				}
				return
			}
			if  > 0 && !([:], nil) {
				return
			}
		}
	}
}

type writerFunc func([]byte) (int, error)

func ( writerFunc) ( []byte) (int, error) {
	return ()
}

// ReaderFromSeq converts an iterator into an io.ReadCloser.
// Close must be called after the caller is done with the reader.
func ( Seq) io.ReadCloser {
	return &iterReader{
		seq: ,
	}
}

type iterReader struct {
	seq Seq

	next  func() ([]byte, error, bool)
	close func()
	err   error
	data  []byte
}

// WriteTo implements [WriterTo].
func ( *iterReader) ( io.Writer) (int64, error) {
	if .seq != nil {
		// Read hasn't been called yet, we can just use the
		// iterator directly, saving the cost of iter.Pull2.
		,  := CopySeq(, .seq)
		// Subsequent reads should return EOF.
		.seq = func(func([]byte, error) bool) {}
		return , 
	}
	return io.Copy(, )
}

func ( *iterReader) ( []byte) (int, error) {
	if .seq != nil {
		.next, .close = iter.Pull2(.seq)
		// Can't use the fast path in WriteTo any more.
		.seq = nil
	}
	if .err != nil {
		return 0, .err
	}
	if len(.data) == 0 {
		var  bool
		.data, .err,  = .next()
		if ! {
			.err = io.EOF
		}
	}
	 := copy(, .data)
	.data = .data[:]
	if len(.data) > 0 {
		return , nil
	}
	return , .err
}

func ( *iterReader) () error {
	if .close != nil {
		.close()
		.close = nil
		if .err == nil {
			.err = io.EOF
		}
	}
	return nil
}

// CopySeq is like [io.Copy] but reads over r writing
// all the data to w. It returns the total number of bytes
// read.
func ( io.Writer,  Seq) (int64, error) {
	 := int64(0)
	for ,  := range  {
		if  != nil {
			return , 
		}
		,  := .Write()
		 += int64()
		if  != nil {
			return , 
		}
	}
	return , nil
}

// SeqWriter returns a [Writer] that operates on the yield
// function passed into a [Seq] iterator. Writes will succeed until
// the iteration is terminated, upon which Write will return
// [ErrSequenceTerminated].
//
// The returned Writer should not be used outside the scope
// of the iterator function, following the same rules as any yield
// function.
//
// If active is non-nil, it reflects the "active" status of the generator
// and its value should be (but does not have to be) true initially.
// yield will not be called when *active is false.
// If yield returns false, *active will be set to false.
//
// The caller can use the value of *active to find out whether
// the iterator is still active.
func ( func([]byte, error) bool,  *bool) io.Writer {
	if  == nil {
		 = new(bool)
		* = true
	}
	return seqWriter{
		yield:  ,
		active: ,
	}
}

type seqWriter struct {
	yield  func([]byte, error) bool
	active *bool
}

var ErrSequenceTerminated = errors.New("sequence terminated")

func ( seqWriter) ( []byte) (int, error) {
	if !*.active {
		return 0, ErrSequenceTerminated
	}
	if !.yield(slices.Clip(), nil) {
		*.active = false
		return 0, ErrSequenceTerminated
	}
	return len(), nil
}

// PipeSeqThrough returns a Seq that iterates over the data written
// by the function f to its argument Writer. The Writer implementation
// that it returns will be written with the data read from seq.
//
// In other words, data read from seq will be "piped through" f,
// resulting in a new Seq.
func [ io.WriteCloser]( Seq,  func( io.Writer) ) Seq {
	return func( func([]byte, error) bool) {
		 := func( io.WriteCloser,  Seq) error {
			if ,  := CopySeq(, );  != nil {
				return 
			}
			return .Close()
		}
		 := true
		 := (SeqWriter(, &))
		if  := (, );  != nil &&  {
			(nil, )
		}
	}
}

// PipeThrough calls f; all data written by f to its argument writer
// will be made available on the returned ReadCloser; all data read from
// f will be written to the writer implementation returned by f.
//
// In other words, it returns a reader that "pipes" the content from r
// through f.
func [ io.WriteCloser]( io.Reader,  func(io.Writer) ,  int) io.ReadCloser {
	 := SeqFromReader(, )
	 := PipeSeqThrough(, )
	return ReaderFromSeq()
}

// ReaderWithContent returns a [Reader] that calls the given function to generate the
// data to be read. If the function returns an error, that error will
// be returned from the reader.
func ( func( io.Writer) error) io.ReadCloser {
	return ReaderFromSeq(func( func([]byte, error) bool) {
		 := true
		if  := (SeqWriter(, &));  != nil &&  {
			(nil, )
		}
	})
}