package door

import (
	"bytes"
	"fmt"
	"testing"
	"unicode"
)

func TestLine(t *testing.T) {
	var textBuff *bytes.Buffer = &bytes.Buffer{}
	textBuff.WriteString("Test Me")
	var line Line = Line{Text: textBuff}
	var output string = string(line.Output())
	var expect string = string("Test Me")

	if output != expect {
		t.Errorf("Line: Expected %#v, got %#v", expect, output)
	}

	if line.Update() {
		t.Error("Line: No updater, should return false")
	}

	line.DefaultColor = Color([]int{0})
	line.Width = 8
	output = string(line.Output())
	expect = "\x1b[0mTest Me "

	if output != expect {
		t.Errorf("Line: Expected %#v, got %#v", expect, output)
	}

	// leave the default color, it is ignored when there's a render function
	// line.DefaultColor = ""
	line.RenderF = RenderBlueYellow
	output = string(line.Output())
	var blue string = ColorText("BOLD BLUE")
	var yellow string = ColorText("BOLD YELLOW")

	expect = blue + "T" + yellow + "est " + blue + "M" + yellow + "e "
	if output != expect {
		t.Errorf("Line: Expected %#v, got %#v", expect, output)
	}

}

func TestLineUpdate(t *testing.T) {
	var counter int = 0
	uf := func(u *bytes.Buffer) {
		u.Reset()
		fmt.Fprintf(u, "Count: %d", counter)
	}
	var line Line = Line{UpdateF: uf}
	line.Update()
	var output string = string(line.Output())
	var expect string = "Count: 0"

	if output != expect {
		t.Errorf("LineUpdate: Expected %#v, got %#v", expect, output)
	}

	if line.Update() {
		t.Error("Unexpected Update: should have returned false. (no change)")
	}

	counter++

	if !line.Update() {
		t.Error("Missing Update: value was changed, Text should have changed")
	}

	output = string(line.Output())
	expect = "Count: 1"
	if output != expect {
		t.Errorf("LineUpdate: Expected %#v, got %#v", expect, output)
	}
}

func TestLineUnicode(t *testing.T) {
	Unicode = true
	// code point > FFFF, use \U00000000 (not \u).
	var lineBuff *bytes.Buffer = &bytes.Buffer{}
	lineBuff.WriteString("Howdy \U0001f920")
	var line Line = Line{Text: lineBuff}
	var output []byte = line.Output()
	var expect []byte = []byte("Howdy 🤠")

	if bytes.Compare(output, expect) != 0 {
		t.Errorf("LineUnicode: Expected %s, got %s", expect, output)
	}

	if StringLen(expect) != 8 {
		t.Errorf("LineUnicode Strlen: Expected 8, got %d", StringLen(expect))
	}

	// 🤠 = 2 chars.
	line.Width = 9
	output = line.Output()
	expect = []byte("Howdy 🤠 ")

	if bytes.Compare(output, expect) != 0 {
		t.Errorf("LineUnicode: Expected %#v, got %#v", expect, output)
	}
}

func TestLineCP437(t *testing.T) {
	Unicode = false
	var tests []string = []string{"\xdb TEXT \xdb",
		"\xf1 F1", "\xf2 F2", "\xf3 F3"}
	for _, test := range tests {
		var lineBuff *bytes.Buffer = &bytes.Buffer{}
		lineBuff.WriteString(test)
		var line Line = Line{Text: lineBuff}
		var output string = string(line.Output())
		var expect string = test

		if output != expect {
			t.Errorf("LineCP437: Expected %#v, got %#v", expect, output)
		}
	}
}

// Benchmarks for profiling
// go test -bench=BenchmarkLine -benchmem -memprofile memory.out -cpuprofile cpu.out
// go tool pprof memory.out
// go tool pprof cpu.out

func BenchmarkLine(b *testing.B) {
	Unicode = false

	for n := 0; n < b.N; n++ {
		var lineBuff *bytes.Buffer = &bytes.Buffer{}
		lineBuff.WriteString(fmt.Sprintf("Line %d of %d", n, b.N))
		var line Line = Line{Text: lineBuff}
		var output string = string(line.Output())
		_ = output
	}
}

func BenchmarkLineRender(b *testing.B) {
	Unicode = false

	for n := 0; n < b.N; n++ {
		var lineBuff *bytes.Buffer = &bytes.Buffer{}
		lineBuff.WriteString(fmt.Sprintf("Line %d of %d", n, b.N))
		var line Line = Line{Text: lineBuff, RenderF: RenderBlueYellow}
		var output string = string(line.Output())
		_ = output
	}
}

// Benchmarks / profiling

func BenchmarkLineColor(b *testing.B) {

	var render = func(output *bytes.Buffer, text []byte) []byte {
		output.Reset()
		var last *string
		// var r Render = Render{Line: text}

		var Up string = ColorText("BLUE")
		var Down string = ColorText("BOLD BLUE")
		var Num string = ColorText("BRI GREEN")
		var Sym string = ColorText("CYAN")

		for _, letter := range text {
			if unicode.IsUpper(rune(letter)) {
				if last != &Up {
					output.WriteString(Up)
					last = &Up
				}
				// r.Append(Up, 1)
			} else if unicode.IsLower(rune(letter)) {
				if last != &Down {
					output.WriteString(Down)
					last = &Down
				}
				// r.Append(Down, 1)
			} else if unicode.IsDigit(rune(letter)) {
				if last != &Num {
					output.WriteString(Num)
					last = &Num
				}
				// r.Append(Num, 1)
			} else {
				if last != &Sym {
					output.WriteString(Sym)
					last = &Sym
				}
				//r.Append(Sym, 1)
			}
			output.WriteByte(letter)
			// output.WriteString(string(letter))
		}
		return output.Bytes()
		// return r.Result
	}

	var up int
	var updater = func(u *bytes.Buffer) {
		u.Reset()
		fmt.Fprintf(u, "The Value: %d", up)
		// u.WriteString("The Value: ")
		// u.WriteString(strconv.Itoa(up))
		up++
		// return fmt.Sprintf("The Value: %d", up)
	}
	var line Line = Line{UpdateF: updater,
		RenderF: render,
		Width:   18}

	for i := 0; i < b.N; i++ {
		line.Update()
		line.Output()
	}
}