package msgbus

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestEmpty(t *testing.T) {
	q := NewQueue()
	assert.Zero(t, q.Len())
	assert.True(t, q.Empty())
}

func TestSimple(t *testing.T) {
	assert := assert.New(t)

	q := NewQueue()

	for i := 0; i < minCapacity; i++ {
		q.Push(i)
	}
	assert.Equal(minCapacity, q.Len())

	for i := 0; i < minCapacity; i++ {
		assert.Equal(q.Peek(), i)
		assert.Equal(q.Pop(), i)
	}
}

func TestSimpleOverflow(t *testing.T) {
	assert := assert.New(t)

	q := NewQueue(2)

	q.Push(1) // dropped
	q.Push(2) // dropped
	q.Push(3)
	q.Push(4)

	assert.Equal(4, q.Len())

	assert.Equal(q.Peek(), 3)
	assert.Equal(q.Pop(), 3)
	assert.Equal(q.Peek(), 4)
	assert.Equal(q.Pop(), 4)
}

func TestForEach(t *testing.T) {
	assert := assert.New(t)
	require := require.New(t)

	q := NewQueue()

	ys := []int{0, 1, 2, 3}
	for _, y := range ys {
		q.Push(y)
	}
	assert.Equal(len(ys), q.Len())

	var xs []int
	err := q.ForEach(func(e interface{}) error {
		i, ok := e.(int)
		if !ok {
			return fmt.Errorf("unexpected type %T", e)
		}
		xs = append(xs, i)
		return nil
	})
	require.NoError(err)
	assert.Equal(ys, xs)
}

func TestForEachOverflow(t *testing.T) {
	assert := assert.New(t)
	require := require.New(t)

	maxlen := 4
	q := &Queue{maxlen: maxlen}

	N := (maxlen + 1)
	var ys []int
	for i := 0; i < N; i++ {
		q.Push(i)
		ys = append(ys, i)
		t.Logf("pushed %d", i)
		t.Logf("q.Len() %d", q.Len())
		t.Logf("q.Size() %d", q.Size())
	}
	assert.False(q.Full())
	assert.Equal(N, q.Len())

	var xs []int
	err := q.ForEach(func(e interface{}) error {
		i, ok := e.(int)
		if !ok {
			return fmt.Errorf("unexpected type %T", e)
		}
		xs = append(xs, i)
		return nil
	})
	require.NoError(err)
	t.Logf("#%v == #%v\n", ys[(N-maxlen):], xs)
	assert.Equal(ys[(N-maxlen):], xs)
}

func TestMaxLen(t *testing.T) {
	q := Queue{maxlen: minCapacity}
	assert.Equal(t, q.MaxLen(), minCapacity)
}

func TestFull(t *testing.T) {
	q := Queue{maxlen: minCapacity}

	for i := 0; i < minCapacity; i++ {
		q.Push(i)
	}

	assert.True(t, q.Full())
}

func TestBufferWrap(t *testing.T) {
	q := NewQueue()

	for i := 0; i < minCapacity; i++ {
		q.Push(i)
	}

	for i := 0; i < 3; i++ {
		q.Pop()
		q.Push(minCapacity + i)
	}

	for i := 0; i < minCapacity; i++ {
		assert.Equal(t, q.Peek().(int), i+3)
		q.Pop()
	}
}

func TestLen(t *testing.T) {
	assert := assert.New(t)

	q := NewQueue()
	assert.Zero(q.Len())

	for i := 0; i < 1000; i++ {
		q.Push(i)
		assert.Equal(q.Len(), i+1)
	}

	for i := 0; i < 1000; i++ {
		q.Pop()
		assert.Equal(q.Len(), 1000-i-1)
	}
}

func BenchmarkPush(b *testing.B) {
	q := NewQueue()
	for i := 0; i < b.N; i++ {
		q.Push(i)
	}
}

func BenchmarkPushPop(b *testing.B) {
	q := NewQueue()
	for i := 0; i < b.N; i++ {
		q.Push(i)
	}
	for i := 0; i < b.N; i++ {
		q.Pop()
	}
}
