package lzfse

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"math/bits"
)

// fseBitCount signed type used to represent bit count.
type fseBitCount int32

// fseState unsigned type used to represent FSE state.
type fseState uint16

// fseInStream object representing an input stream.
type fseInStream struct {
	Accum      uint64      // Input bits
	AccumNbits fseBitCount // Number of valid bits in ACCUM, other bits are 0
}

// fseDecoderEntry entry for one state in the decoder table (32b).
type fseDecoderEntry struct { // DO NOT REORDER THE FIELDS
	k      int8  // Number of bits to read
	symbol uint8 // Emitted symbol
	delta  int16 // Signed increment used to compute next state (+bias)
}

func (f fseDecoderEntry) ToInt32() int32 {
	buf := new(bytes.Buffer)
	binary.Write(buf, binary.LittleEndian, f)
	return int32(binary.LittleEndian.Uint32(buf.Bytes()))
}

// fseValueDecoderEntry entry for one state in the value decoder table (64b).
type fseValueDecoderEntry struct {
	TotalBits uint8 // state bits + extra value bits = shift for next decode
	ValueBits uint8 // extra value bits
	Delta     int16 // state base (delta)
	Vbase     int32 // value base
}

func (f fseValueDecoderEntry) ToInt64() int64 {
	buf := new(bytes.Buffer)
	binary.Write(buf, binary.LittleEndian, f)
	return int64(binary.LittleEndian.Uint64(buf.Bytes()))
}

func fseInitDecoderTable(nstates, nsymbols int, freq [256]uint16, t *[1024]int32) error {
	var sumOfFreq, lDecoderIdx int

	nClz := bits.LeadingZeros(uint(nstates))

	for i := 0; i < nsymbols; i++ {
		f := int(freq[i])
		if f == 0 {
			continue // skip this symbol, no occurrences
		}
		sumOfFreq += f

		if sumOfFreq > nstates {
			return fmt.Errorf("sumOfFreq > nstates")
		}

		k := bits.LeadingZeros(uint(f)) - nClz // shift needed to ensure N <= (F<<K) < 2*N
		j0 := ((2 * nstates) >> k) - f

		// Initialize all states S reached by this symbol: OFFSET <= S < OFFSET + F
		for j := 0; j < f; j++ {
			var e fseDecoderEntry

			e.symbol = uint8(i)
			if j < j0 {
				e.k = int8(k)
				e.delta = int16(((f + j) << k) - nstates)
			} else {
				e.k = int8(k - 1)
				e.delta = int16((j - j0) << (k - 1))
			}
			t[lDecoderIdx] = e.ToInt32()
			lDecoderIdx++
		}
	}

	return nil
}

func fseInitValueLOrMDecoderTable(nstates, nsymbols int, freq []uint16, symbolVbits []uint8,
	symbolVbase []int32, t *[64]fseValueDecoderEntry) {
	var dDecoderIdx int

	nClz := bits.LeadingZeros(uint(nstates))
	for i := 0; i < nsymbols; i++ {
		f := int(freq[i])

		if f == 0 {
			continue // skip this symbol, no occurrences
		}
		k := bits.LeadingZeros(uint(f)) - nClz // shift needed to ensure N <= (F<<K) < 2*N
		j0 := ((2 * nstates) >> k) - f

		var ei fseValueDecoderEntry
		ei.ValueBits = symbolVbits[i]
		ei.Vbase = symbolVbase[i]

		// Initialize all states S reached by this symbol: OFFSET <= S < OFFSET + F
		for j := 0; j < f; j++ {
			e := ei
			if j < j0 {
				e.TotalBits = uint8(k) + e.ValueBits
				e.Delta = int16(((f + j) << k) - nstates)
			} else {
				e.TotalBits = uint8(k-1) + e.ValueBits
				e.Delta = int16((j - j0) << (k - 1))
			}
			t[dDecoderIdx] = e
			dDecoderIdx++
		}
	}
}

func fseInitValueDDecoderTable(nstates, nsymbols int, freq []uint16, symbolVbits []uint8,
	symbolVbase []int32, t *[256]fseValueDecoderEntry) {
	var dDecoderIdx int

	nClz := bits.LeadingZeros(uint(nstates))
	for i := 0; i < nsymbols; i++ {
		f := int(freq[i])

		if f == 0 {
			continue // skip this symbol, no occurrences
		}
		k := bits.LeadingZeros(uint(f)) - nClz // shift needed to ensure N <= (F<<K) < 2*N
		j0 := ((2 * nstates) >> k) - f

		var ei fseValueDecoderEntry
		ei.ValueBits = symbolVbits[i]
		ei.Vbase = symbolVbase[i]

		// Initialize all states S reached by this symbol: OFFSET <= S < OFFSET + F
		for j := 0; j < f; j++ {
			e := ei
			if j < j0 {
				e.TotalBits = uint8(k) + e.ValueBits
				e.Delta = int16(((f + j) << k) - nstates)
			} else {
				e.TotalBits = uint8(k-1) + e.ValueBits
				e.Delta = int16((j - j0) << (k - 1))
			}
			t[dDecoderIdx] = e
			dDecoderIdx++
		}
	}
}

/* fseInCheckedInit initialize the fse input stream so that accum holds between 56
 *  and 63 bits. We never want to have 64 bits in the stream, because that allows
 *  us to avoid a special case in the fseInPull function (eliminating an
 *  unpredictable branch), while not requiring any additional fse_flush
 *  operations. This is why we have the special case for n == 0 (in which case
 *  we want to load only 7 bytes instead of 8). */
// func fseInCheckedInit(s *fseInStream, n fseBitCount, r *bytes.Reader, buffStart int64) error {
// func fseInCheckedInit(s *fseInStream, n fseBitCount, r *bytes.Reader) error {
func fseInCheckedInit(s *fseInStream, n fseBitCount, r *io.SectionReader) error {
	// pbuf := io.NewSectionReader(r, 0, 1<<63-1)
	if n != 0 {
		if _, err := r.Seek(-8, io.SeekCurrent); err != nil {
			return err
		}
		if err := binary.Read(r, binary.LittleEndian, &s.Accum); err != nil {
			return err
		}
		r.Seek(-8, io.SeekCurrent)
		s.AccumNbits = n + 64
	} else {
		r.Seek(-7, io.SeekCurrent)
		if err := binary.Read(r, binary.LittleEndian, &s.Accum); err != nil {
			return err
		}
		r.Seek(-8, io.SeekCurrent)
		s.Accum &= 0xffffffffffffff
		s.AccumNbits = n + 56
	}

	if (s.AccumNbits < 56 || s.AccumNbits >= 64) || ((s.Accum >> s.AccumNbits) != 0) {
		return fmt.Errorf("the incoming input is wrong (encoder should have zeroed the upper bits)")
	}

	return nil
}

/* fseInCheckedFlush - read in new bytes from buffer to ensure that we have a full
 * complement of bits in the stream object (again, between 56 and 63 bits).
 * checking the new value of *pbuf remains >= buf_start.
 * @return 0 if OK.
 * @return -1 on failure. */
// func fseInCheckedFlush(s *fseInStream, r *bytes.Reader) error {
func fseInCheckedFlush(s *fseInStream, r *io.SectionReader) error {
	//  Get number of bits to add to bring us into the desired range.
	nbits := (63 - s.AccumNbits) & -8
	//  Convert bits to bytes and decrement buffer address, then load new data.
	_, err := r.Seek(int64(-(nbits >> 3)), io.SeekCurrent)
	if err != nil {
		return err
	}
	var incoming uint64
	if err := binary.Read(r, binary.LittleEndian, &incoming); err != nil {
		return err
	}
	r.Seek(-8, io.SeekCurrent)
	// Update the state object and verify its validity (in DEBUG).
	s.Accum = (s.Accum << nbits) | fseMaskLsb64(incoming, nbits)
	s.AccumNbits += nbits
	if s.AccumNbits < 56 || s.AccumNbits >= 64 {
		return fmt.Errorf("failed to fseInCheckedFlush - s.AccumNbits < 56 || s.AccumNbits >= 64")
	}
	if (s.Accum >> s.AccumNbits) != 0 {
		return fmt.Errorf("failed to fseInCheckedFlush - (s.Accum >> s.AccumNbits) == 0")
	}
	return nil // OK
}

/* fseDecode - decode and return symbol using the decoder table, and update *pstate, in.
 *  @note The caller must ensure we have enough bits available in the input
 *  stream accumulator. */
func fseDecode(pstate *uint16, decoderTable [1024]int32, in *fseInStream) uint8 {
	e := decoderTable[*pstate]
	// Update state from K bits of input + DELTA
	*pstate = uint16(e>>16) + uint16(fseInPull(in, fseBitCount(e&0xff)))
	// Return the symbol for this state
	return uint8(fseExtractBits(uint64(e), 8, 8)) // symbol
}

/* fseValueDecode - decode and return value using the decoder table, and update *pstate, in value_decoder_table[nstates]
 * @note The caller must ensure we have enough bits available in the input
 * stream accumulator. */
func fseValueDecode(pstate *uint16, valueDecoderTable []fseValueDecoderEntry, in *fseInStream) int32 {
	entry := valueDecoderTable[*pstate]
	stateAndValueBits := uint32(fseInPull(in, fseBitCount(entry.TotalBits)))
	*pstate = uint16(entry.Delta) + uint16(stateAndValueBits>>entry.ValueBits)
	return entry.Vbase + int32(fseMaskLsb64(uint64(stateAndValueBits), fseBitCount(entry.ValueBits)))
}

// fseInPull - pull n bits out of the fse stream object.
func fseInPull(s *fseInStream, n fseBitCount) uint64 {
	//   assert(n >= 0 && n <= s.AccumNbits);
	s.AccumNbits -= n
	result := s.Accum >> s.AccumNbits
	s.Accum = fseMaskLsb64(s.Accum, s.AccumNbits)
	return result
}

// Mask the NBITS lsb of X. 0 <= NBITS < 64
func fseMaskLsb64(x uint64, nbits fseBitCount) uint64 {
	mtable := [65]uint64{
		0x0000000000000000, 0x0000000000000001, 0x0000000000000003,
		0x0000000000000007, 0x000000000000000f, 0x000000000000001f,
		0x000000000000003f, 0x000000000000007f, 0x00000000000000ff,
		0x00000000000001ff, 0x00000000000003ff, 0x00000000000007ff,
		0x0000000000000fff, 0x0000000000001fff, 0x0000000000003fff,
		0x0000000000007fff, 0x000000000000ffff, 0x000000000001ffff,
		0x000000000003ffff, 0x000000000007ffff, 0x00000000000fffff,
		0x00000000001fffff, 0x00000000003fffff, 0x00000000007fffff,
		0x0000000000ffffff, 0x0000000001ffffff, 0x0000000003ffffff,
		0x0000000007ffffff, 0x000000000fffffff, 0x000000001fffffff,
		0x000000003fffffff, 0x000000007fffffff, 0x00000000ffffffff,
		0x00000001ffffffff, 0x00000003ffffffff, 0x00000007ffffffff,
		0x0000000fffffffff, 0x0000001fffffffff, 0x0000003fffffffff,
		0x0000007fffffffff, 0x000000ffffffffff, 0x000001ffffffffff,
		0x000003ffffffffff, 0x000007ffffffffff, 0x00000fffffffffff,
		0x00001fffffffffff, 0x00003fffffffffff, 0x00007fffffffffff,
		0x0000ffffffffffff, 0x0001ffffffffffff, 0x0003ffffffffffff,
		0x0007ffffffffffff, 0x000fffffffffffff, 0x001fffffffffffff,
		0x003fffffffffffff, 0x007fffffffffffff, 0x00ffffffffffffff,
		0x01ffffffffffffff, 0x03ffffffffffffff, 0x07ffffffffffffff,
		0x0fffffffffffffff, 0x1fffffffffffffff, 0x3fffffffffffffff,
		0x7fffffffffffffff, 0xffffffffffffffff,
	}
	return x & mtable[nbits]
}

// fseExtractBits - select nbits at index start from x.
// 0 <= start <= start+nbits <= 64
func fseExtractBits(x uint64, start, nbits fseBitCount) uint64 {
	return fseMaskLsb64(x>>start, nbits)
}
