package routes

import (
	"reflect"
	"testing"
)

type normParenTest struct {
	in   string
	want []string
}

func (tc normParenTest) test(t *testing.T) {
	have := normalizeParens(tc.in)
	if !sameContents(have, tc.want) {
		t.Errorf("in=%q  have=%q  want=%q", tc.in, have, tc.want)
	}
}

func TestNormalizeParens(t *testing.T) {
	tests := []normParenTest{
		normParenTest{"/a", []string{"/a"}},
		normParenTest{"/a(/b)", []string{"/a", "/a/b"}},
		normParenTest{"/a(/b)(/c)", []string{"/a", "/a/b", "/a/c", "/a/b/c"}},
		normParenTest{"/a(/b/c)", []string{"/a", "/a/b/c"}},
		normParenTest{"/a(/b(/c))", []string{"/a", "/a/b", "/a/b/c"}},
		normParenTest{"/a(/b)/c", []string{"/a/c", "/a/b/c"}},
		normParenTest{"/a()/c", []string{"/a/c"}},
	}
	for _, tc := range tests {
		tc.test(t)
	}
}

func BenchmarkNormalizeZeroParenPairs(b *testing.B) {
	for i := 0; i < b.N; i++ {
		normalizeParens("/1/2/3/4/5")
	}
}

func BenchmarkNormalizeOneParenPair(b *testing.B) {
	for i := 0; i < b.N; i++ {
		normalizeParens("/1/2/(3)/4/5")
	}
}

func BenchmarkNormalizeTenParenPairs(b *testing.B) {
	for i := 0; i < b.N; i++ {
		normalizeParens("(/1)(/2)(/3)(/4)(/5)(/6)(/7)(/8)(/9)(/10)")
	}
}

func BenchmarkNormalizeTenNestedParenPairs(b *testing.B) {
	for i := 0; i < b.N; i++ {
		normalizeParens("(/1(/2(/3(/4(/5(/6(/7(/8(/9(/10))))))))))")
	}
}

func BenchmarkNormalizeFifteenParenPairs(b *testing.B) {
	for i := 0; i < b.N; i++ {
		normalizeParens("(/1)(/2)(/3)(/4)(/5)(/6)(/7)(/8)(/9)(/10)(/11)(/12)(/13)(/14)(/15)")
	}
}

func BenchmarkNormalizeFifteenNestedParenPairs(b *testing.B) {
	for i := 0; i < b.N; i++ {
		normalizeParens("(/1(/2(/3(/4(/5(/6(/7(/8(/9(/10(/11(/12(/13(/14(/15)))))))))))))))")
	}
}

func BenchmarkNormalizeLongStringParens(b *testing.B) {
	for i := 0; i < b.N; i++ {
		normalizeParens("/nonparenthesizedstatement(/andthenalongoneinsideofparens)/andthensomemoregoop")
	}
}

func sameContents(s1, s2 []string) bool {
	s1map := make(map[string]struct{})
	for _, s := range s1 {
		s1map[s] = struct{}{}
	}
	s2map := make(map[string]struct{})
	for _, s := range s2 {
		s2map[s] = struct{}{}
	}
	return reflect.DeepEqual(s1map, s2map)
}
