package bufreader

import (
	"bufio"
	"bytes"
	"io"

	"github.com/imgproxy/imgproxy/v3/imath"
)

type Reader struct {
	r   io.Reader
	buf *bytes.Buffer
	cur int
}

func New(r io.Reader, buf *bytes.Buffer) *Reader {
	br := Reader{
		r:   r,
		buf: buf,
	}
	return &br
}

func (br *Reader) Read(p []byte) (int, error) {
	if err := br.fill(br.cur + len(p)); err != nil {
		return 0, err
	}

	n := copy(p, br.buf.Bytes()[br.cur:])
	br.cur += n
	return n, nil
}

func (br *Reader) ReadByte() (byte, error) {
	if err := br.fill(br.cur + 1); err != nil {
		return 0, err
	}

	b := br.buf.Bytes()[br.cur]
	br.cur++
	return b, nil
}

func (br *Reader) Discard(n int) (int, error) {
	if n < 0 {
		return 0, bufio.ErrNegativeCount
	}
	if n == 0 {
		return 0, nil
	}

	if err := br.fill(br.cur + n); err != nil {
		return 0, err
	}

	n = imath.Min(n, br.buf.Len()-br.cur)
	br.cur += n
	return n, nil
}

func (br *Reader) Peek(n int) ([]byte, error) {
	if n < 0 {
		return []byte{}, bufio.ErrNegativeCount
	}
	if n == 0 {
		return []byte{}, nil
	}

	if err := br.fill(br.cur + n); err != nil {
		return []byte{}, err
	}

	if n > br.buf.Len()-br.cur {
		return br.buf.Bytes()[br.cur:], io.EOF
	}

	return br.buf.Bytes()[br.cur : br.cur+n], nil
}

func (br *Reader) Flush() error {
	_, err := br.buf.ReadFrom(br.r)
	return err
}

func (br *Reader) fill(need int) error {
	n := need - br.buf.Len()
	if n <= 0 {
		return nil
	}

	n = imath.Max(4096, n)

	if _, err := br.buf.ReadFrom(io.LimitReader(br.r, int64(n))); err != nil {
		return err
	}

	// Nothing was read, it's EOF
	if br.cur == br.buf.Len() {
		return io.EOF
	}

	return nil
}