package executor

import (
	"sync"
	"testing"
	"time"

	"github.com/onsi/gomega"
	//"github.com/onsi/gomega"
)

func Test_Executor(t *testing.T) {
	executor := NewExecutor(5)
	defer func() {
		executor.Close()
	}()

	results := &MockResult{
		results: make([]int, 0),
	}
	for i := 0; i < 1000; i++ {
		executor.Execute(MockTask{
			data: i,
			mr:   results,
		})
	}

	executor.Wait("A")
	executor.Wait("B")

	g := gomega.NewWithT(t)

	g.Eventually(func() bool {
		return len(results.results) == 1000
	}, time.Second*2, time.Millisecond*300).Should(gomega.BeTrue())

	for i := 0; i < 1000; i++ {
		g.Expect(results.results).To(gomega.ContainElements(i))
	}
}

func Test_TryExecutor(t *testing.T) {
	executor := NewExecutor(5)
	defer func() {
		executor.Close()
	}()

	g := gomega.NewWithT(t)
	results := &MockResult{
		results: make([]int, 0),
	}

	enQueueCount := 0
	for i := 0; i < 1000; i++ {
		enQueue := executor.TryExecute(MockTask{
			data:  i,
			mr:    results,
			sleep: true,
		})

		if enQueue {
			enQueueCount++
		}
	}

	g.Expect(enQueueCount < 11).To(gomega.BeTrue())
}

func Test_Submit(t *testing.T) {
	executor := NewExecutor(5)
	defer func() {
		executor.Close()
	}()

	results := &MockResult{
		results: make([]int, 0),
	}
	for i := 0; i < 1000; i++ {
		executor.Submit("A", MockTask{
			data: i,
			mr:   results,
		})
	}

	executor.Wait("A")
	executor.Wait("B")

	g := gomega.NewWithT(t)

	g.Expect(len(results.results)).To(gomega.Equal(1000))

	for i := 0; i < 1000; i++ {
		g.Expect(results.results).To(gomega.ContainElements(i))
	}
}

type MockResult struct {
	lock    sync.Mutex
	results []int
}

func (mr *MockResult) add(data int) {
	mr.lock.Lock()
	defer mr.lock.Unlock()

	mr.results = append(mr.results, data)
}

type MockTask struct {
	data int
	mr   *MockResult

	sleep bool
}

func (mt MockTask) Run() {
	if mt.sleep {
		time.Sleep(time.Hour)
	}

	mt.mr.add(mt.data)
}
