package pvdata

import "io"

// BitSet is a variable-length set of bit indices, encoded on the wire as:
//
//	compact-size(n_bytes) + n_bytes of little-endian bits.
//
// Bit 0 of byte 0 is field index 0, bit 1 of byte 0 is field index 1, etc.
// Used in pvData Monitor responses for "changed" and "overrun" masks.
type BitSet struct {
	bits []byte
}

// NewBitSet returns an empty BitSet.
func NewBitSet() BitSet { return BitSet{} }

// Set sets bit i in the BitSet.
func (bs *BitSet) Set(i int) {
	byteIdx := i / 8
	for len(bs.bits) <= byteIdx {
		bs.bits = append(bs.bits, 0)
	}
	bs.bits[byteIdx] |= 1 << (uint(i) % 8)
}

// Has reports whether bit i is set.
func (bs *BitSet) Has(i int) bool {
	byteIdx := i / 8
	if byteIdx >= len(bs.bits) {
		return false
	}
	return bs.bits[byteIdx]&(1<<(uint(i)%8)) != 0
}

// ReadBitSet reads a BitSet from r.
func ReadBitSet(r io.Reader) (BitSet, error) {
	n, err := ReadSize(r)
	if err != nil {
		return BitSet{}, err
	}
	if n == 0 {
		return BitSet{}, nil
	}
	buf := make([]byte, n)
	if _, err := io.ReadFull(r, buf); err != nil {
		return BitSet{}, err
	}
	return BitSet{bits: buf}, nil
}

// WriteBitSet writes bs to w.
func WriteBitSet(w io.Writer, bs BitSet) error {
	if err := WriteSize(w, int64(len(bs.bits))); err != nil {
		return err
	}
	if len(bs.bits) == 0 {
		return nil
	}
	_, err := w.Write(bs.bits)
	return err
}

// FieldIndices returns all set bit indices in ascending order.
func (bs *BitSet) FieldIndices() []int {
	var out []int
	for bi, b := range bs.bits {
		for bit := 0; bit < 8; bit++ {
			if b&(1<<uint(bit)) != 0 {
				out = append(out, bi*8+bit)
			}
		}
	}
	return out
}
