// Copyright 2019 The ebml-go authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ebml

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"reflect"
	"testing"
	"time"

	"github.com/at-wat/ebml-go/internal/errs"
)

type successWithEOFBytesReader struct {
	buf []byte
}

func (r *successWithEOFBytesReader) Read(b []byte) (int, error) {
	if len(r.buf) == 0 {
		return 0, io.EOF
	}

	b[0] = r.buf[0]
	r.buf = r.buf[1:]
	if len(r.buf) == 1 {
		return 1, io.EOF
	}
	return 1, nil
}

func runForEachReader(t *testing.T, b []byte, fn func(t *testing.T, r func() io.Reader)) {
	t.Run("NormalReader", func(t *testing.T) {
		fn(t, func() io.Reader {
			return bytes.NewReader(b)
		})
	})
	t.Run("SuccessWithEOFReader", func(t *testing.T) {
		fn(t, func() io.Reader {
			return &successWithEOFBytesReader{buf: b}
		})
	})
}

func ExampleUnmarshal() {
	TestBinary := []byte{
		0x1a, 0x45, 0xdf, 0xa3, // EBML
		0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, // 0x10
		0x42, 0x82, 0x85, 0x77, 0x65, 0x62, 0x6d, 0x00, // EBMLDocType = webm
		0x42, 0x87, 0x81, 0x02, // DocTypeVersion = 2
		0x42, 0x85, 0x81, 0x02, // DocTypeReadVersion = 2
	}
	type TestEBML struct {
		Header struct {
			DocType            string `ebml:"EBMLDocType"`
			DocTypeVersion     uint64 `ebml:"EBMLDocTypeVersion"`
			DocTypeReadVersion uint64 `ebml:"EBMLDocTypeReadVersion"`
		} `ebml:"EBML"`
	}

	r := bytes.NewReader(TestBinary)

	var ret TestEBML
	if err := Unmarshal(r, &ret); err != nil {
		fmt.Printf("error: %v\n", err)
	}
	fmt.Println(ret)

	// Output: {{webm 2 2}}
}

func TestUnmarshal_MultipleUnknownSize(t *testing.T) {
	b := []byte{
		0x18, 0x53, 0x80, 0x67, // Segment
		0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
		0x1F, 0x43, 0xB6, 0x75, // Cluster
		0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
		0x42, 0x87, 0x81, 0x01,
		0x1F, 0x43, 0xB6, 0x75, // Cluster
		0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
		0x42, 0x87, 0x81, 0x02,
	}
	type Cluster struct {
		DocTypeVersion uint64 `ebml:"EBMLDocTypeVersion"`
	}
	type Segment struct {
		Cluster []Cluster `ebml:"Cluster"`
	}
	type TestEBML struct {
		Segment Segment `ebml:"Segment"`
	}
	expected := TestEBML{
		Segment: Segment{
			Cluster: []Cluster{{0x01}, {0x02}},
		},
	}

	runForEachReader(t, b, func(t *testing.T, r func() io.Reader) {
		var ret TestEBML
		if err := Unmarshal(r(), &ret); err != nil {
			t.Fatalf("Unexpected error: '%v'\n", err)
		}
		if !reflect.DeepEqual(expected, ret) {
			t.Errorf("Expected result: %v, got: %v", expected, ret)
		}
	})
}

func TestUnmarshal_Convert(t *testing.T) {
	cases := map[string]struct {
		b        []byte
		expected interface{}
	}{
		"UInt64ToUInt64": {
			[]byte{0x42, 0x87, 0x81, 0x02},
			struct {
				DocTypeVersion uint64 `ebml:"EBMLDocTypeVersion"`
			}{2},
		},
		"UInt64ToUInt32": {
			[]byte{0x42, 0x87, 0x81, 0x02},
			struct {
				DocTypeVersion uint32 `ebml:"EBMLDocTypeVersion"`
			}{2},
		},
		"UInt64ToUInt16": {
			[]byte{0x42, 0x87, 0x81, 0x02},
			struct {
				DocTypeVersion uint16 `ebml:"EBMLDocTypeVersion"`
			}{2},
		},
		"UInt64ToUInt8": {
			[]byte{0x42, 0x87, 0x81, 0x02},
			struct {
				DocTypeVersion uint8 `ebml:"EBMLDocTypeVersion"`
			}{2},
		},
		"UInt64ToUInt": {
			[]byte{0x42, 0x87, 0x81, 0x02},
			struct {
				DocTypeVersion uint `ebml:"EBMLDocTypeVersion"`
			}{2},
		},
		"Int64ToInt64": {
			[]byte{0xFB, 0x81, 0xFF},
			struct {
				ReferenceBlock int64 `ebml:"ReferenceBlock"`
			}{-1},
		},
		"Int64ToInt32": {
			[]byte{0xFB, 0x81, 0xFF},
			struct {
				ReferenceBlock int32 `ebml:"ReferenceBlock"`
			}{-1},
		},
		"Int64ToInt16": {
			[]byte{0xFB, 0x81, 0xFF},
			struct {
				ReferenceBlock int16 `ebml:"ReferenceBlock"`
			}{-1},
		},
		"Int64ToInt8": {
			[]byte{0xFB, 0x81, 0xFF},
			struct {
				ReferenceBlock int8 `ebml:"ReferenceBlock"`
			}{-1},
		},
		"Int64ToInt": {
			[]byte{0xFB, 0x81, 0xFF},
			struct {
				ReferenceBlock int `ebml:"ReferenceBlock"`
			}{-1},
		},
		"Float64ToFloat64": {
			[]byte{0x44, 0x89, 0x84, 0x00, 0x00, 0x00, 0x00},
			struct {
				Duration float64 `ebml:"Duration"`
			}{0.0},
		},
		"Float64ToFloat32": {
			[]byte{0x44, 0x89, 0x84, 0x00, 0x00, 0x00, 0x00},
			struct {
				Duration float32 `ebml:"Duration"`
			}{0.0},
		},
		"UInt64ToUInt64Slice": {
			[]byte{0x42, 0x87, 0x81, 0x02},
			struct {
				DocTypeVersion []uint64 `ebml:"EBMLDocTypeVersion"`
			}{[]uint64{2}},
		},
		"UInt64ToUInt32Slice": {
			[]byte{0x42, 0x87, 0x81, 0x02},
			struct {
				DocTypeVersion []uint32 `ebml:"EBMLDocTypeVersion"`
			}{[]uint32{2}},
		},
		"Int64ToInt32Slice": {
			[]byte{0xFB, 0x81, 0xFF},
			struct {
				ReferenceBlock []int32 `ebml:"ReferenceBlock"`
			}{[]int32{-1}},
		},
		"Float64ToFloat32Slice": {
			[]byte{0x44, 0x89, 0x84, 0x00, 0x00, 0x00, 0x00},
			struct {
				Duration []float32 `ebml:"Duration"`
			}{[]float32{0.0}},
		},
	}

	for name, c := range cases {
		t.Run(name, func(t *testing.T) {
			runForEachReader(t, c.b, func(t *testing.T, r func() io.Reader) {
				ret := reflect.New(reflect.ValueOf(c.expected).Type())
				if err := Unmarshal(r(), ret.Interface()); err != nil {
					t.Fatalf("Unexpected error: '%v'\n", err)
				}

				if !reflect.DeepEqual(c.expected, ret.Elem().Interface()) {
					t.Errorf("Expected convert result: %v, got %v",
						c.expected, ret.Elem().Interface())
				}
			})
		})
	}
}

func TestUnmarshal_OptionError(t *testing.T) {
	errExpected := errors.New("an error")
	err := Unmarshal(&bytes.Buffer{}, &struct{}{},
		func(*UnmarshalOptions) error {
			return errExpected
		},
	)
	if err != errExpected {
		t.Errorf("Expected error against failing UnmarshalOption: '%v', got: '%v'", errExpected, err)
	}
}

func TestUnmarshal_WithElementReadHooks(t *testing.T) {
	testBinary := []byte{
		0x18, 0x53, 0x80, 0x67, 0xa6, // Segment
		0x1c, 0x53, 0xbb, 0x6b, 0x80, // Cues (empty)
		0x16, 0x54, 0xae, 0x6b, 0x9c, // Tracks
		0xae, 0x8c, // TrackEntry[0]
		0x53, 0x6e, 0x86, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x00, // Name=Video
		0xd7, 0x81, 0x01, // TrackNumber=1
		0xae, 0x8c, // TrackEntry[1]
		0x53, 0x6e, 0x86, 0x41, 0x75, 0x64, 0x69, 0x6f, 0x00, // Name=Audio
		0xd7, 0x81, 0x02, // TrackNumber=2
	}

	type TestEBML struct {
		Segment struct {
			Tracks struct {
				TrackEntry []struct {
					Name        string `ebml:"Name,omitempty"`
					TrackNumber uint64 `ebml:"TrackNumber"`
				} `ebml:"TrackEntry"`
			} `ebml:"Tracks"`
		} `ebml:"Segment"`
	}

	runForEachReader(t, testBinary, func(t *testing.T, r func() io.Reader) {
		var ret TestEBML
		m := make(map[string][]*Element)
		hook := withElementMap(m)
		if err := Unmarshal(r(), &ret, WithElementReadHooks(hook)); err != nil {
			t.Errorf("Unexpected error: '%v'", err)
		}

		// Verify positions of elements
		expected := map[string][]uint64{
			"Segment":                               {0},
			"Segment.Tracks":                        {10},
			"Segment.Tracks.TrackEntry":             {15, 29},
			"Segment.Tracks.TrackEntry.Name":        {17, 31},
			"Segment.Tracks.TrackEntry.TrackNumber": {26, 40},
		}
		posMap := elementPositionMap(m)
		if !reflect.DeepEqual(expected, posMap) {
			t.Errorf("Unexpected read hook positions, \nexpected: %v, \n     got: %v", expected, posMap)
		}
		checkTarget := "Segment.Tracks.TrackEntry.Name"
		switch {
		case len(m[checkTarget]) != 2:
			t.Fatalf("%s read hook should be called twice, but called %d times",
				checkTarget, len(m[checkTarget]))
		case m[checkTarget][0].Type != ElementName:
			t.Fatalf("ElementType of %s should be %s, got %s",
				checkTarget, ElementName, m[checkTarget][0].Type)
		}
		switch v, ok := m[checkTarget][0].Value.(string); {
		case !ok:
			t.Errorf("Invalid type of data: %T", v)
		case v != "Video":
			t.Errorf("The value should be Video, got %s", v)
		}
	})
}

func TestUnmarshal_Chan(t *testing.T) {
	testBinary := []byte{
		0x18, 0x53, 0x80, 0x67, 0x8f, // Segment
		0x16, 0x54, 0xae, 0x6b, 0x8a, // Tracks
		0xae, 0x83, // TrackEntry[0]
		0xd7, 0x81, 0x01, // TrackNumber=1
		0xae, 0x83, // TrackEntry[0]
		0xd7, 0x81, 0x02, // TrackNumber=2
	}
	type TestEBML struct {
		Segment struct {
			Tracks struct {
				TrackEntry struct {
					TrackNumber chan uint64 `ebml:"TrackNumber"`
				} `ebml:"TrackEntry"`
			} `ebml:"Tracks"`
		} `ebml:"Segment"`
	}

	runForEachReader(t, testBinary, func(t *testing.T, r func() io.Reader) {
		var ret TestEBML
		ch := make(chan uint64, 100)
		ret.Segment.Tracks.TrackEntry.TrackNumber = ch

		done := make(chan struct{})
		go func() {
			select {
			case <-time.After(5 * time.Second):
				panic("test timeout")
			case <-done:
			}
		}()
		if err := Unmarshal(r(), &ret); err != nil {
			t.Errorf("Unexpected error: '%v'", err)
		}
		close(done)
		if len(ch) != 2 {
			t.Fatalf("Element chan should be sent twice, but sent %d times", len(ch))
		}
		if v := <-ch; v != 1 {
			t.Errorf("First value should be 1, got %d", v)
		}
		if v := <-ch; v != 2 {
			t.Errorf("Second value should be 2, got %d", v)
		}
	})
}

func TestUnmarshal_Tag(t *testing.T) {
	var tagged struct {
		DocCustomNamedType string `ebml:"EBMLDocType"`
	}
	var untagged struct {
		EBMLDocType string
	}

	b := []byte{0x42, 0x82, 0x85, 0x68, 0x6F, 0x67, 0x65, 0x00}

	runForEachReader(t, b, func(t *testing.T, r func() io.Reader) {
		if err := Unmarshal(r(), &tagged); err != nil {
			t.Fatalf("Unexpected error: '%v'", err)
		}
		if err := Unmarshal(r(), &untagged); err != nil {
			t.Fatalf("Unexpected error: '%v'", err)
		}

		if tagged.DocCustomNamedType != untagged.EBMLDocType {
			t.Errorf("Unmarshal result to tagged and and untagged struct must be same, tagged: %v, untagged: %v", tagged, untagged)
		}
	})
}

func TestUnmarshal_Map(t *testing.T) {
	b := []byte{
		0x1A, 0x45, 0xDF, 0xA3, 0x8C,
		0x42, 0x82, 0x85, 0x68, 0x6F, 0x67, 0x65, 0x00,
		0xEC, 0x82, 0x00, 0x00,
		0x18, 0x53, 0x80, 0x67, 0xFF,
		0x1F, 0x43, 0xB6, 0x75, 0x80,
		0x1F, 0x43, 0xB6, 0x75, 0x80,
		0x1F, 0x43, 0xB6, 0x75, 0x80,
	}
	expected := map[string]interface{}{
		"EBML": map[string]interface{}{
			"EBMLDocType": "hoge",
			"Void":        []uint8{0, 0},
		},
		"Segment": map[string]interface{}{
			"Cluster": []interface{}{
				map[string]interface{}{},
				map[string]interface{}{},
				map[string]interface{}{},
			},
		},
	}

	t.Run("AllocatedMap", func(t *testing.T) {
		runForEachReader(t, b, func(t *testing.T, r func() io.Reader) {
			ret := make(map[string]interface{})
			if err := Unmarshal(r(), &ret); err != nil {
				t.Fatalf("Unexpected error: '%v'", err)
			}

			if !reflect.DeepEqual(expected, ret) {
				t.Errorf("Unmarshal to map differs from expected:\n%#+v\ngot:\n%#+v", expected, ret)
			}
		})
	})

	t.Run("NilMap", func(t *testing.T) {
		runForEachReader(t, b, func(t *testing.T, r func() io.Reader) {
			var ret map[string]interface{}
			if err := Unmarshal(r(), &ret); err != nil {
				t.Fatalf("Unexpected error: '%v'", err)
			}

			if !reflect.DeepEqual(expected, ret) {
				t.Errorf("Unmarshal to map differs from expected:\n%#+v\ngot:\n%#+v", expected, ret)
			}
		})
	})
}

func TestUnmarshal_IgnoreUnknown(t *testing.T) {
	b := []byte{
		0x1A, 0x45, 0xDF, 0xA3, 0x8A,
		0x42, 0x82, 0x85, 0x68, 0x6F, 0x67, 0x65, 0x00,
		0x81, 0x81, // 0x81 is not defined in Matroska v4
		0x18, 0x53, 0x80, 0x67, 0xFF,
		0x1F, 0x43, 0xB6, 0x75, 0x80,
		0x1F, 0x43, 0xB6, 0x75, 0x80,
	}
	expected := map[string]interface{}{
		"EBML": map[string]interface{}{
			"EBMLDocType": "hoge",
		},
		"Segment": map[string]interface{}{
			"Cluster": []interface{}{
				map[string]interface{}{},
				map[string]interface{}{},
			},
		},
	}

	runForEachReader(t, b, func(t *testing.T, r func() io.Reader) {
		ret := make(map[string]interface{})
		if err := Unmarshal(r(), &ret, WithIgnoreUnknown(true)); err != nil {
			t.Fatalf("Unexpected error: '%v'", err)
		}

		if !reflect.DeepEqual(expected, ret) {
			t.Errorf("Unmarshal with IgnoreUnknown differs from expected:\n%#+v\ngot:\n%#+v", expected, ret)
		}
	})
}

func TestUnmarshal_Error(t *testing.T) {
	type TestEBML struct {
		Header struct {
		} `ebml:"EBML"`
	}
	t.Run("NilValue", func(t *testing.T) {
		if err := Unmarshal(bytes.NewBuffer([]byte{}), nil); !errs.Is(err, ErrIndefiniteType) {
			t.Errorf("Expected error: '%v', got: '%v'", ErrIndefiniteType, err)
		}
	})
	t.Run("NonPtr", func(t *testing.T) {
		if err := Unmarshal(bytes.NewBuffer([]byte{}), struct{}{}); !errs.Is(err, ErrIncompatibleType) {
			t.Errorf("Expected error: '%v', got: '%v'", ErrIncompatibleType, err)
		}
	})
	t.Run("UnknownElementName", func(t *testing.T) {
		input := &struct {
			Header struct {
			} `ebml:"Unknown"`
		}{}
		if err := Unmarshal(bytes.NewBuffer([]byte{}), input); !errs.Is(err, ErrUnknownElementName) {
			t.Errorf("Expected error: '%v', got: '%v'", ErrUnknownElementName, err)
		}
	})
	t.Run("InvalidTag", func(t *testing.T) {
		input := &struct {
			Header struct {
			} `ebml:"EBML,ivalid"`
		}{}
		if err := Unmarshal(bytes.NewBuffer([]byte{}), input); !errs.Is(err, ErrInvalidTag) {
			t.Errorf("Expected error: '%v', got: '%v'", ErrInvalidTag, err)
		}
	})
	t.Run("UnknownElement", func(t *testing.T) {
		input := &TestEBML{}
		b := []byte{0x81}
		if err := Unmarshal(bytes.NewBuffer(b), input); !errs.Is(err, ErrUnknownElement) {
			t.Errorf("Expected error: '%v', got: '%v'", ErrUnknownElement, err)
		}
	})
	t.Run("NonStaticUnknownElementWithIgnoreUnknown", func(t *testing.T) {
		input := &TestEBML{}
		b := []byte{0x81, 0xFF}
		if err := Unmarshal(
			bytes.NewBuffer(b), input, WithIgnoreUnknown(true),
		); err != nil {
			t.Errorf("Unexpected error: '%v'", err)
		}
	})
	t.Run("ShortUnknownElementWithIgnoreUnknown", func(t *testing.T) {
		input := &TestEBML{}
		b := []byte{0x81, 0x85, 0x00}
		if err := Unmarshal(
			bytes.NewBuffer(b), input, WithIgnoreUnknown(true),
		); err != nil {
			t.Errorf("Unexpected error: '%v'", err)
		}
	})
	t.Run("Short", func(t *testing.T) {
		TestBinaries := map[string][]byte{
			"ElementID": {0x1a, 0x45, 0xdf},
			"DataSize":  {0x42, 0x86, 0x40},
			"UInt(0)":   {0x42, 0x86, 0x84},
			"UInt":      {0x42, 0x86, 0x84, 0x00},
			"Float(0)":  {0x44, 0x89, 0x84},
			"Float":     {0x44, 0x89, 0x84, 0x00},
			"String(0)": {0x42, 0x82, 0x84},
			"String":    {0x42, 0x82, 0x84, 0x00},
		}
		for name, b := range TestBinaries {
			t.Run(name, func(t *testing.T) {
				var val TestEBML
				if err := Unmarshal(bytes.NewBuffer(b), &val); !errs.Is(err, io.ErrUnexpectedEOF) {
					t.Errorf("Expected error: '%v', got: '%v'", io.ErrUnexpectedEOF, err)
				}
			})
		}
	})
	t.Run("ErrorPropagation", func(t *testing.T) {
		TestBinaries := map[string][]byte{
			"UInt":       {0x42, 0x86, 0x84, 0x00, 0x00, 0x00, 0x00},
			"Float":      {0x44, 0x89, 0x84, 0x00, 0x00, 0x00, 0x00},
			"String":     {0x42, 0x82, 0x84, 0x00, 0x00, 0x00, 0x00},
			"Block":      {0xA3, 0x85, 0x81, 0x00, 0x00, 0x00, 0x00},
			"BlockXiph":  {0xA3, 0x88, 0x81, 0x00, 0x00, 0x02, 0x01, 0x01, 0x00, 0x00},
			"BlockFixed": {0xA3, 0x87, 0x81, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00},
			"BlockEBML":  {0xA3, 0x88, 0x98, 0x00, 0x00, 0x06, 0x01, 0x81, 0x00, 0x00},
		}
		for name, b := range TestBinaries {
			t.Run(name, func(t *testing.T) {
				for i := 1; i < len(b)-1; i++ {
					var val TestEBML
					r := &delayedBrokenReader{b: b, limit: i}
					if err := Unmarshal(r, &val); !errs.Is(err, io.ErrClosedPipe) {
						t.Errorf("Error is not propagated from Reader, limit: %d, expected: '%v', got: '%v'", i, io.ErrClosedPipe, err)
					}
				}
			})
		}
	})
	t.Run("Incompatible", func(t *testing.T) {
		cases := map[string]struct {
			b   []byte
			ret interface{}
			err error
		}{
			"UInt64ToInt64": {
				b: []byte{0x42, 0x87, 0x81, 0x02},
				ret: &struct {
					DocTypeVersion int64 `ebml:"EBMLDocTypeVersion"`
				}{},
				err: ErrIncompatibleType,
			},
			"Int64ToUInt64": {
				b: []byte{0xFB, 0x81, 0xFF},
				ret: &struct {
					ReferenceBlock uint64 `ebml:"ReferenceBlock"`
				}{},
				err: ErrIncompatibleType,
			},
			"Float64ToInt64": {
				b: []byte{0x44, 0x89, 0x84, 0x00, 0x00, 0x00, 0x00},
				ret: &struct {
					Duration int64 `ebml:"Duration"`
				}{},
				err: ErrIncompatibleType,
			},
			"StringToInt64": {
				b: []byte{0x42, 0x82, 0x85, 0x77, 0x65, 0x62, 0x6d, 0x00},
				ret: &struct {
					EBMLDocType int64 `ebml:"EBMLDocType"`
				}{},
				err: ErrIncompatibleType,
			},
			"UInt64ToInt64Slice": {
				b: []byte{0x42, 0x87, 0x81, 0x02},
				ret: &struct {
					DocTypeVersion []int64 `ebml:"EBMLDocTypeVersion"`
				}{},
				err: ErrIncompatibleType,
			},
			"Int64ToUInt64Slice": {
				b: []byte{0xFB, 0x81, 0xFF},
				ret: &struct {
					ReferenceBlock []uint64 `ebml:"ReferenceBlock"`
				}{},
				err: ErrIncompatibleType,
			},
			"Float64ToInt64Slice": {
				b: []byte{0x44, 0x89, 0x84, 0x00, 0x00, 0x00, 0x00},
				ret: &struct {
					Duration []int64 `ebml:"Duration"`
				}{},
				err: ErrIncompatibleType,
			},
			"StringToInt64Slice": {
				b: []byte{0x42, 0x82, 0x85, 0x77, 0x65, 0x62, 0x6d, 0x00},
				ret: &struct {
					EBMLDocType []int64 `ebml:"EBMLDocType"`
				}{},
				err: ErrIncompatibleType,
			},
		}
		for name, c := range cases {
			t.Run(name, func(t *testing.T) {
				if err := Unmarshal(bytes.NewBuffer(c.b), c.ret); !errs.Is(err, c.err) {
					t.Errorf("Expected error: '%v', got: '%v'", c.err, err)
				}
			})
		}
	})
}

func ExampleUnmarshal_partial() {
	TestBinary := []byte{
		0x1a, 0x45, 0xdf, 0xa3, 0x84, // EBML
		0x42, 0x87, 0x81, 0x02, // DocTypeVersion = 2
		0x18, 0x53, 0x80, 0x67, 0xFF, // Segment
		0x16, 0x54, 0xae, 0x6b, 0x85, // Tracks
		0xae, 0x83, // TrackEntry[0]
		0xd7, 0x81, 0x01, // TrackNumber=1
		0x1F, 0x43, 0xB6, 0x75, 0xFF, // Cluster
		0xE7, 0x81, 0x00, // Timecode
		0xA3, 0x86, 0x81, 0x00, 0x00, 0x88, 0xAA, 0xCC, // SimpleBlock
	}

	type TestHeader struct {
		Header  map[string]interface{} `ebml:"EBML"`
		Segment struct {
			Tracks map[string]interface{} `ebml:"Tracks,stop"` // Stop unmarshalling after reading this element
		}
	}
	type TestClusters struct {
		Cluster []struct {
			Timecode    uint64
			SimpleBlock []Block
		}
	}

	r := bytes.NewReader(TestBinary)

	var header TestHeader
	if err := Unmarshal(r, &header); !errs.Is(err, ErrReadStopped) {
		panic("Unmarshal failed")
	}
	fmt.Printf("First unmarshal: %v\n", header)

	var clusters TestClusters
	if err := Unmarshal(r, &clusters); err != nil {
		panic("Unmarshal failed")
	}
	fmt.Printf("Second unmarshal: %v\n", clusters)

	// Output:
	// First unmarshal: {map[EBMLDocTypeVersion:2] {map[TrackEntry:map[TrackNumber:1]]}}
	// Second unmarshal: {[{0 [{1 0 true true 0 false [[170 204]]}]}]}
}

func BenchmarkUnmarshal(b *testing.B) {
	TestBinary := []byte{
		0x1a, 0x45, 0xdf, 0xa3, // EBML
		0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, // 0x10
		0x42, 0x82, 0x85, 0x77, 0x65, 0x62, 0x6d, 0x00, // DocType = webm
		0x42, 0x87, 0x81, 0x02, // DocTypeVersion = 2
		0x42, 0x85, 0x81, 0x02, // DocTypeReadVersion = 2
		0x18, 0x53, 0x80, 0x67, 0xFF, // Segment
		0x1F, 0x43, 0xB6, 0x75, 0xFF, // Cluster
		0xE7, 0x81, 0x00, // Timecode
		0xA3, 0x86, 0x81, 0x00, 0x00, 0x88, 0xAA, 0xCC, // SimpleBlock
		0xA3, 0x86, 0x81, 0x00, 0x10, 0x88, 0xAA, 0xCC, // SimpleBlock
		0xA3, 0x86, 0x81, 0x00, 0x20, 0x88, 0xAA, 0xCC, // SimpleBlock
		0x1F, 0x43, 0xB6, 0x75, 0xFF, // Cluster
		0xE7, 0x81, 0x10, // Timecode
		0xA3, 0x86, 0x81, 0x00, 0x00, 0x88, 0xAA, 0xCC, // SimpleBlock
		0xA3, 0x86, 0x81, 0x00, 0x10, 0x88, 0xAA, 0xCC, // SimpleBlock
		0xA3, 0x86, 0x81, 0x00, 0x20, 0x88, 0xAA, 0xCC, // SimpleBlock
	}
	type TestEBML struct {
		Header struct {
			DocType            string `ebml:"EBMLDocType"`
			DocTypeVersion     uint64 `ebml:"EBMLDocTypeVersion"`
			DocTypeReadVersion uint64 `ebml:"EBMLDocTypeReadVersion"`
		} `ebml:"EBML"`
		Segment []struct {
			Cluster struct {
				Timecode    uint64
				SimpleBlock []Block
			}
		}
	}

	var ret TestEBML

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if err := Unmarshal(bytes.NewReader(TestBinary), &ret); err != nil {
			b.Fatalf("Unexpected error: '%v'", err)
		}
	}
}
