package terminal

import (
	"fmt"
	"strings"
	"testing"

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

type ansiCase struct {
	in           string
	kind         ansiKind
	params       string
	intermediate string
	final        byte
}

func (c ansiCase) run(t *testing.T) {
	t.Run(c.in, func(t *testing.T) {
		t.Parallel()

		var d ansiDecoder
		err := d.decode(strings.NewReader(c.in))
		require.NoError(t, err)
		assert.Equal(t, c.kind, d.kind)
		assert.Equal(t, c.params, string(d.params))
		assert.Equal(t, c.intermediate, string(d.intermediate))
		assert.Equal(t, c.final, d.final)
	})
}

func ansiErrorCase(lead, params, intermediate string, final byte) ansiCase {
	return ansiCase{
		in:           fmt.Sprintf("%v%v%v%v", lead, params, intermediate, string([]byte{final})),
		kind:         ansiError,
		params:       params,
		intermediate: intermediate,
		final:        final,
	}
}

func ansiKeyCase(in byte) ansiCase {
	return ansiCase{in: string([]byte{in}), kind: ansiKey, final: in}
}

func ansiEscapeCase(intermediate string, final byte) ansiCase {
	return ansiCase{
		in:           fmt.Sprintf("\x1b%v%v", intermediate, string([]byte{final})),
		kind:         ansiEscape,
		intermediate: intermediate,
		final:        final,
	}
}

func ansiControlCase(params, intermediate string, final byte) ansiCase {
	return ansiCase{
		in:           fmt.Sprintf("\x1b[%v%v%v", params, intermediate, string([]byte{final})),
		kind:         ansiControl,
		params:       params,
		intermediate: intermediate,
		final:        final,
	}
}

func TestANSIDecoder(t *testing.T) {
	t.Parallel()

	// check all key sequences
	for i := 0; i < 0x7f; i++ {
		if i != 0x1b {
			ansiKeyCase(byte(i)).run(t)
		}
	}

	// check a selection of escape sequences
	escapeCases := []ansiCase{
		ansiEscapeCase("", '7'),
		ansiEscapeCase("", '8'),
		ansiEscapeCase("(", 'A'),
		ansiEscapeCase(")", 'A'),
		ansiErrorCase("\x1b", "", "", 3),
		ansiErrorCase("\x1b", "", "(", 3),
		ansiErrorCase("\x1b", "", "", 0x7f),
	}
	for _, c := range escapeCases {
		c.run(t)
	}

	// check a selection of control sequences
	controlCases := []ansiCase{
		ansiControlCase("5", "", '~'),
		ansiControlCase("6", "", '~'),
		ansiControlCase("5;1", "", '~'),
		ansiControlCase("6;1", "", '~'),
		ansiControlCase("", "", 'A'),
		ansiControlCase("", "", 'B'),
		ansiControlCase("80;24", " ", 'T'),
		ansiErrorCase("\x1b[", "5", "", 3),
		ansiErrorCase("\x1b[", "80;24", " ", 4),
		ansiErrorCase("\x1b[", "5;1", "", 0x7f),
	}
	for _, c := range controlCases {
		c.run(t)
	}
}