package ec

import (
	"bytes"
	"io"
	"sync"
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

func Test_EncodeReconstruct(t *testing.T) {
	Convey("编码后使用校验块重建数据", t, func() {
		rs, err := NewStreamRs(2, 3, 5)
		So(err, ShouldBeNil)

		outputs := rs.EncodeAll([]io.Reader{
			bytes.NewReader([]byte{1, 2, 3, 4, 5}),
			bytes.NewReader([]byte{6, 7, 8, 9, 10}),
		})

		var outputData = [][]byte{
			make([]byte, 5),
			make([]byte, 5),
			make([]byte, 5),
		}

		{ // 编码所有块
			errs := make([]error, 3)

			wg := sync.WaitGroup{}
			for i := range outputs {
				idx := i

				wg.Add(1)
				go func() {
					defer wg.Done()
					_, err := io.ReadFull(outputs[idx], outputData[idx])
					errs[idx] = err
				}()
			}

			wg.Wait()

			for _, e := range errs {
				if e != io.EOF {
					So(e, ShouldBeNil)
				}
			}

			So(outputData[0], ShouldResemble, []byte{1, 2, 3, 4, 5})
			So(outputData[1], ShouldResemble, []byte{6, 7, 8, 9, 10})
		}

		{ // 重建所有数据块
			recOutputs := rs.ReconstructData([]io.Reader{
				bytes.NewBuffer(outputData[1]),
				bytes.NewBuffer(outputData[2]),
			}, []int{1, 2})

			recOutputData := [][]byte{
				make([]byte, 5),
				make([]byte, 5),
			}
			errs := make([]error, 2)

			wg := sync.WaitGroup{}
			for i := range recOutputs {
				idx := i

				wg.Add(1)
				go func() {
					defer wg.Done()
					_, err := io.ReadFull(recOutputs[idx], recOutputData[idx])
					errs[idx] = err
				}()
			}

			wg.Wait()

			for _, e := range errs {
				if e != io.EOF {
					So(e, ShouldBeNil)
				}
			}

			So(recOutputData[0], ShouldResemble, []byte{1, 2, 3, 4, 5})
			So(recOutputData[1], ShouldResemble, []byte{6, 7, 8, 9, 10})
		}

		{ // 重建指定的数据块
			recOutputs := rs.ReconstructSome([]io.Reader{
				bytes.NewBuffer(outputData[1]),
				bytes.NewBuffer(outputData[2]),
			}, []int{1, 2}, []int{0, 1})

			recOutputData := [][]byte{
				make([]byte, 5),
				make([]byte, 5),
			}
			errs := make([]error, 2)

			wg := sync.WaitGroup{}
			for i := range recOutputs {
				idx := i

				wg.Add(1)
				go func() {
					defer wg.Done()
					_, err := io.ReadFull(recOutputs[idx], recOutputData[idx])
					errs[idx] = err
				}()
			}

			wg.Wait()

			for _, e := range errs {
				if e != io.EOF {
					So(e, ShouldBeNil)
				}
			}

			So(recOutputData[0], ShouldResemble, []byte{1, 2, 3, 4, 5})
			So(recOutputData[1], ShouldResemble, []byte{6, 7, 8, 9, 10})
		}

		{ // 重建指定的数据块
			recOutputs := rs.ReconstructSome([]io.Reader{
				bytes.NewBuffer(outputData[1]),
				bytes.NewBuffer(outputData[2]),
			}, []int{1, 2}, []int{0})

			recOutputData := [][]byte{
				make([]byte, 5),
			}
			errs := make([]error, 2)

			wg := sync.WaitGroup{}
			for i := range recOutputs {
				idx := i

				wg.Add(1)
				go func() {
					defer wg.Done()
					_, err := io.ReadFull(recOutputs[idx], recOutputData[idx])
					errs[idx] = err
				}()
			}

			wg.Wait()

			for _, e := range errs {
				if e != io.EOF {
					So(e, ShouldBeNil)
				}
			}

			So(recOutputData[0], ShouldResemble, []byte{1, 2, 3, 4, 5})
		}

		{ // 重建指定的数据块
			recOutputs := rs.ReconstructSome([]io.Reader{
				bytes.NewBuffer(outputData[1]),
				bytes.NewBuffer(outputData[2]),
			}, []int{1, 2}, []int{1})

			recOutputData := [][]byte{
				make([]byte, 5),
			}
			errs := make([]error, 2)

			wg := sync.WaitGroup{}
			for i := range recOutputs {
				idx := i

				wg.Add(1)
				go func() {
					defer wg.Done()
					_, err := io.ReadFull(recOutputs[idx], recOutputData[idx])
					errs[idx] = err
				}()
			}

			wg.Wait()

			for _, e := range errs {
				if e != io.EOF {
					So(e, ShouldBeNil)
				}
			}

			So(recOutputData[0], ShouldResemble, []byte{6, 7, 8, 9, 10})
		}

		{ // 单独产生校验块
			encOutputs := rs.Encode([]io.Reader{
				bytes.NewBuffer(outputData[0]),
				bytes.NewBuffer(outputData[1]),
			})

			encOutputData := [][]byte{
				make([]byte, 5),
			}
			errs := make([]error, 2)

			wg := sync.WaitGroup{}
			for i := range encOutputs {
				idx := i

				wg.Add(1)
				go func() {
					defer wg.Done()
					_, err := io.ReadFull(encOutputs[idx], encOutputData[idx])
					errs[idx] = err
				}()
			}

			wg.Wait()

			for _, e := range errs {
				if e != io.EOF {
					So(e, ShouldBeNil)
				}
			}

			So(encOutputData[0], ShouldResemble, outputData[2])
		}

		{ // 使用ReconstructAny单独重建校验块
			encOutputs := rs.ReconstructAny([]io.Reader{
				bytes.NewBuffer(outputData[0]),
				bytes.NewBuffer(outputData[1]),
			}, []int{0, 1}, []int{2})

			encOutputData := [][]byte{
				make([]byte, 5),
			}
			errs := make([]error, 2)

			wg := sync.WaitGroup{}
			for i := range encOutputs {
				idx := i

				wg.Add(1)
				go func() {
					defer wg.Done()
					_, err := io.ReadFull(encOutputs[idx], encOutputData[idx])
					errs[idx] = err
				}()
			}

			wg.Wait()

			for _, e := range errs {
				if e != io.EOF {
					So(e, ShouldBeNil)
				}
			}

			So(encOutputData[0], ShouldResemble, outputData[2])
		}
	})
}
