package internal

import (
	"bytes"
	"encoding/binary"
	"io"
	"io/ioutil"
	"math/rand"
)

func Fuzz(b []byte, join interface {
	io.WriterAt
	io.Reader
	io.Closer
}, logf func(string, ...interface{})) int {

	const seedSize = 8
	if len(b) < seedSize {
		return 0
	}

	// Use the first few bytes to generate source data, use the remaining bytes
	// to generate WriteAt calls
	seed, b := b[:seedSize], b[seedSize:]

	source := make([]byte, 1<<8)
	rng := rand.New(rand.NewSource(int64(binary.BigEndian.Uint64(seed))))
	for i := range source {
		source[i] = byte(rng.Intn(1 << 8))
	}

	max := 0
	for len(b) >= 2 {
		start, end := int(b[0]), int(b[1])
		b = b[2:]
		if end < start {
			end = len(source)
		}
		segment := source[start:end]
		n, err := join.WriteAt(segment, int64(start))
		logf("WriteAt(data[%d:%d]) = %d, %v", start, end, n, err)
		if err != nil {
			panic(err)
		}
		if n != len(segment) {
			panic("wrong write length")
		}
		if end > max {
			max = end
		}
	}

	err := join.Close()
	if err != nil {
		panic(err)
	}

	result, err := ioutil.ReadAll(join)
	if err != nil {
		panic(err)
	}

	if have, want := result, source[:len(result)]; !bytes.Equal(have, want) {
		logf("have %02x", have)
		logf("want %02x", want)
		panic("incorrect reassembly")
	}

	return 1
}
