package zos

import (
	"bytes"
	"io"
	"os"
)

func min(a int, b int) int {
	if a < b {
		return a
	}
	return b
}

type FReader struct {
	io   io.Reader
	buf  []byte
	r, w int
	err  error
}

func NewReader(io io.Reader) *FReader {
	return &FReader{
		io:  io,
		buf: make([]byte, 4096),
		r:   0,
		w:   0,
	}
}
func (b *FReader) Read(data []byte) (int, error) {
	n, rn := len(data), 0
	for rn < n {
		d := min(b.w-b.r, n-rn)
		if d > 0 {
			copy(data[rn:rn+d], b.buf[b.r:b.r+d])
			b.r += d
			rn += d
			if rn >= n {
				break
			}
		}
		err := b.fill()
		if err != nil {
			return rn, err
		}
	}
	return rn, nil

}
func (b *FReader) ReadLine() ([]byte, error) {
	line, err := b.ReadSlice('\n')
	i := len(line) - 1
	if i >= 0 && line[i] == '\r' {
		return line[:i], err
	}
	return line, err
}
func (b *FReader) ReadLineFast() ([]byte, error) {
	line, err := b.ReadSliceFast('\n')
	i := len(line) - 1
	if i >= 0 && line[i] == '\r' {
		return line[:i], err
	}
	return line, err
}
func (b *FReader) ReadSlice(delim byte) ([]byte, error) {
	var lines []byte
	for {
		line, err := b.ReadSliceFast(delim)
		if err == nil || err != ErrBufferFull {
			return line, err
		}
		lines = append(lines, line...)
	}
	return lines, nil
}
func (b *FReader) ReadSliceFast(delim byte) ([]byte, error) {
	for {
		// Search buffer.
		if i := bytes.IndexByte(b.buf[b.r+0:b.w], delim); i >= 0 {
			i += 1
			b.r += i
			return b.buf[b.r-i : b.r-1], nil
		}
		err := b.fill()
		if err != nil {
			return b.buf[b.r:], err
		}
	}
}
func (b *FReader) fill() error {
	d := b.w - b.r
	if d >= len(b.buf) {
		b.r = 0
		b.w = 0
		return ErrBufferFull
	}
	// Slide existing data to beginning.
	if b.r > 0 {
		copy(b.buf, b.buf[b.r:b.w])
		b.w -= b.r
		b.r = 0
	}
	// Read new data: try a limited number of times.
	n, err := b.io.Read(b.buf[b.w:])
	b.w += n
	return err
}
func (b *FReader) Clear() {
	b.r = 0
	b.w = 0
}

func (b *FReader) Seek(pos, offset int) {
	r := b.r + offset
	if r >= 0 && r <= b.w {
		b.r = r
		return
	}
	f, ok := b.io.(*os.File)
	if ok {
		f.Seek(int64(pos), 0)
		b.r = 0
		b.w = 0
	}
}
