package bits

type Reader struct {
	EOF bool // if end of buffer raised during reading

	buf  []byte // total buf
	byte byte   // current byte
	bits byte   // bits left in byte
	pos  int    // current pos in buf
}

func NewReader(b []byte) *Reader {
	return &Reader{buf: b}
}

//goland:noinspection GoStandardMethods
func (r *Reader) ReadByte() byte {
	if r.bits != 0 {
		return r.ReadBits8(8)
	}

	if r.pos >= len(r.buf) {
		r.EOF = true
		return 0
	}

	b := r.buf[r.pos]
	r.pos++
	return b
}

func (r *Reader) ReadUint16() uint16 {
	if r.bits != 0 {
		return r.ReadBits16(16)
	}
	return uint16(r.ReadByte())<<8 | uint16(r.ReadByte())
}

func (r *Reader) ReadUint24() uint32 {
	if r.bits != 0 {
		return r.ReadBits(24)
	}
	return uint32(r.ReadByte())<<16 | uint32(r.ReadByte())<<8 | uint32(r.ReadByte())
}

func (r *Reader) ReadUint32() uint32 {
	if r.bits != 0 {
		return r.ReadBits(32)
	}
	return uint32(r.ReadByte())<<24 | uint32(r.ReadByte())<<16 | uint32(r.ReadByte())<<8 | uint32(r.ReadByte())
}

func (r *Reader) ReadBit() byte {
	if r.bits == 0 {
		r.byte = r.ReadByte()
		r.bits = 7
	} else {
		r.bits--
	}

	return (r.byte >> r.bits) & 0b1
}

func (r *Reader) ReadBits(n byte) (res uint32) {
	for i := n - 1; i != 255; i-- {
		res |= uint32(r.ReadBit()) << i
	}
	return
}

func (r *Reader) ReadBits8(n byte) (res uint8) {
	for i := n - 1; i != 255; i-- {
		res |= r.ReadBit() << i
	}
	return
}

func (r *Reader) ReadBits16(n byte) (res uint16) {
	for i := n - 1; i != 255; i-- {
		res |= uint16(r.ReadBit()) << i
	}
	return
}

func (r *Reader) ReadBits64(n byte) (res uint64) {
	for i := n - 1; i != 255; i-- {
		res |= uint64(r.ReadBit()) << i
	}
	return
}

func (r *Reader) ReadFloat32() float64 {
	i := r.ReadUint16()
	f := r.ReadUint16()
	return float64(i) + float64(f)/65536
}

func (r *Reader) ReadBytes(n int) (b []byte) {
	if r.bits == 0 {
		if r.pos+n > len(r.buf) {
			r.EOF = true
			return nil
		}

		b = r.buf[r.pos : r.pos+n]
		r.pos += n
	} else {
		b = make([]byte, n)
		for i := 0; i < n; i++ {
			b[i] = r.ReadByte()
		}
	}

	return
}

// ReadUEGolomb - ReadExponentialGolomb (unsigned)
func (r *Reader) ReadUEGolomb() uint32 {
	var size byte
	for size = 0; size < 32; size++ {
		if b := r.ReadBit(); b != 0 || r.EOF {
			break
		}
	}
	return r.ReadBits(size) + (1 << size) - 1
}

// ReadSEGolomb - ReadSignedExponentialGolomb
func (r *Reader) ReadSEGolomb() int32 {
	if b := r.ReadUEGolomb(); b%2 == 0 {
		return -int32(b / 2)
	} else {
		return int32((b + 1) / 2)
	}
}

func (r *Reader) Left() []byte {
	return r.buf[r.pos:]
}

func (r *Reader) Pos() (int, byte) {
	return r.pos - 1, r.bits
}
