package wave

import (
	"errors"
	"fmt"
	"reflect"
	"testing"
)

var (
	errIdenticalAddress = errors.New("Cloned audio has the same memory address with the original audio")
)

func TestBufferStoreCopyAndLoad(t *testing.T) {
	chunkInfo := ChunkInfo{
		Len:          4,
		Channels:     2,
		SamplingRate: 48000,
	}
	testCases := map[string]struct {
		New      func() EditableAudio
		Update   func(EditableAudio)
		Validate func(*testing.T, Audio, Audio)
	}{
		"Float32Interleaved": {
			New: func() EditableAudio {
				return NewFloat32Interleaved(chunkInfo)
			},
			Update: func(src EditableAudio) {
				src.Set(0, 0, Float32Sample(1))
			},
			Validate: func(t *testing.T, original Audio, clone Audio) {
				ok := reflect.ValueOf(original.(*Float32Interleaved).Data).Pointer() != reflect.ValueOf(clone.(*Float32Interleaved).Data).Pointer()
				if !ok {
					t.Error(errIdenticalAddress)
				}
			},
		},
		"Float32NonInterleaved": {
			New: func() EditableAudio {
				return NewFloat32NonInterleaved(chunkInfo)
			},
			Update: func(src EditableAudio) {
				src.Set(0, 0, Float32Sample(1))
			},
			Validate: func(t *testing.T, original Audio, clone Audio) {
				originalReal := original.(*Float32NonInterleaved)
				cloneReal := clone.(*Float32NonInterleaved)
				if reflect.ValueOf(originalReal.Data).Pointer() == reflect.ValueOf(cloneReal.Data).Pointer() {
					t.Error(errIdenticalAddress)
				}

				for i := range cloneReal.Data {
					if reflect.ValueOf(originalReal.Data[i]).Pointer() == reflect.ValueOf(cloneReal.Data[i]).Pointer() {
						err := fmt.Errorf("Channel %d memory address should be different", i)
						t.Errorf("%v: %s", errIdenticalAddress, err)
					}
				}
			},
		},
		"Int16Interleaved": {
			New: func() EditableAudio {
				return NewInt16Interleaved(chunkInfo)
			},
			Update: func(src EditableAudio) {
				src.Set(1, 1, Int16Sample(2))
			},
			Validate: func(t *testing.T, original Audio, clone Audio) {
				ok := reflect.ValueOf(original.(*Int16Interleaved).Data).Pointer() != reflect.ValueOf(clone.(*Int16Interleaved).Data).Pointer()
				if !ok {
					t.Error(errIdenticalAddress)
				}
			},
		},
		"Int16NonInterleaved": {
			New: func() EditableAudio {
				return NewInt16NonInterleaved(chunkInfo)
			},
			Update: func(src EditableAudio) {
				src.Set(1, 1, Int16Sample(2))
			},
			Validate: func(t *testing.T, original Audio, clone Audio) {
				originalReal := original.(*Int16NonInterleaved)
				cloneReal := clone.(*Int16NonInterleaved)
				if reflect.ValueOf(originalReal.Data).Pointer() == reflect.ValueOf(cloneReal.Data).Pointer() {
					t.Error(errIdenticalAddress)
				}

				for i := range cloneReal.Data {
					if reflect.ValueOf(originalReal.Data[i]).Pointer() == reflect.ValueOf(cloneReal.Data[i]).Pointer() {
						err := fmt.Errorf("Channel %d memory address should be different", i)
						t.Errorf("%v: %s", errIdenticalAddress, err)
					}
				}
			},
		},
	}

	buffer := NewBuffer()

	for name, testCase := range testCases {
		// Since the test also wants to make sure that Copier can convert from 1 type to another,
		// t.Run is not ideal since it'll run the tests separately
		t.Log("Testing", name)

		src := testCase.New()
		src.Set(0, 0, Int16Sample(1))
		buffer.StoreCopy(src)

		testCase.Validate(t, src, buffer.Load())

		if !reflect.DeepEqual(buffer.Load(), src) {
			t.Fatalf(`Expected the copied audio chunk to be identical with the source

Expected:
%v

Actual:
%v
			`, src, buffer.Load())
		}

		testCase.Update(src)
		buffer.StoreCopy(src)
		if !reflect.DeepEqual(buffer.Load(), src) {
			t.Fatal("Expected the copied audio chunk to be identical with the source after an update in source")
		}
	}
}
