package door

import (
	"flag"
	"fmt"

	"net"
	"os"
	"strings"
	"testing"
	"time"
)

func TestDoorInputConnection(t *testing.T) {
	var tmpFile *os.File
	var err error
	tmpFile, err = os.CreateTemp("", "test-*")
	if err != nil {
		panic("Cannot create temporary file")
	}

	// Remember to clean up the file afterwards
	defer os.Remove(tmpFile.Name())

	// establish network socket connection to set Comm_handle
	var server, client net.Conn
	server, client = setupSockets()

	// Ok, we have a server socket, and the client socket (that the door would talk to)

	// unicode 190x43 response
	buffer := []byte("\x1b[1;1R\x1b[2;3R\x1b[43;190R")
	_, err = server.Write(buffer)
	if err != nil {
		t.Error("server.Write:", err)
	}
	time.Sleep(time.Millisecond)

	// Access Fd (File descriptor) of client for dropfile
	var fd int = socket_to_fd(client)
	defer close_fd(fd)

	// Create door32.sys file
	dfc := DropfileConfig{2, fd, 1800, "Test BBSID", 1701, "Real Username", "Handle", 880, 28, 0, 12}

	_, err = tmpFile.WriteString(fmt.Sprintf("%d\n%d\n%d\n%s\n%d\n%s\n%s\n%d\n%d\n%d\n%d\n",
		dfc.Comm_type, dfc.Comm_handle, dfc.Baudrate, dfc.BBSID, dfc.User_number, dfc.Real_name, dfc.Handle,
		dfc.Security_level, dfc.Time_left, dfc.Emulation, dfc.Node))
	if err != nil {
		t.Error("tmpFile.WriteString:", err)
	}
	err = tmpFile.Close()
	if err != nil {
		t.Error("tmpFile.Close:", err)
	}

	d := Door{ReaderCanClose: true}

	// Because we're not the only one calling door.Init(), the
	// door global variables might be from a previous test run.

	Unicode = false
	CP437 = false
	Full_CP437 = false
	Width = 0
	Height = 0

	// If I call d.Init() more then once flag complains about flag redefined.
	// Reset flags
	flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
	// preset commandline args so door can init
	os.Args = []string{"door", "-d", tmpFile.Name()}
	d.Init("input-test")

	defer d.Close()
	// clean up logfile
	defer os.Remove("input-test-12.log")

	// Ok!
	if !Unicode {
		t.Errorf("Unicode not true %t", Unicode)
	}

	if Width != 190 {
		t.Errorf("Width not 190: %d", Width)
	}

	if Height != 43 {
		t.Errorf("Height not 43: %d", Height)
	}

	// These are the commands sent to detect ... throw this all away.
	buffer = make([]byte, 128)
	err = server.SetReadDeadline(time.Now().Add(time.Millisecond * 20))
	if err != nil {
		t.Error("server.SetReadDeadLine:", err)
	}
	_, err = server.Read(buffer)
	if err != nil {
		t.Errorf("server.Read: %#v", err)
	}
	// t.Errorf("Buffer : %#v\n", buffer[:r])
	err = server.SetReadDeadline(time.Time{})
	if err != nil {
		t.Error("server.SetReadDeadLine:", err)
	}

	keytest := map[string][]int{
		"\x1b":                             []int{0x1b},
		"\x0d\x00":                         []int{0x0d},
		"\x0d\x0a":                         []int{0x0d},
		"\x0dCQ":                           []int{0x0d, 'C', 'Q'},
		"\x0dA":                            []int{0x0d, 'A'},
		"\x0dCAT":                          []int{0x0d, 'C', 'A', 'T'},
		"\x00\x50\x00\x48\x00\x4b\x00\x4d": []int{XKEY_DOWN_ARROW, XKEY_UP_ARROW, XKEY_LEFT_ARROW, XKEY_RIGHT_ARROW},
		"\x00\x47\x00\x4f\x00\x49\x00\x51": []int{XKEY_HOME, XKEY_END, XKEY_PGUP, XKEY_PGDN},
		"\x00\x3b\x00\x3c\x00\x3d\x00\x3e": []int{XKEY_F1, XKEY_F2, XKEY_F3, XKEY_F4},
		"\x00\x3f\x00\x40\x00\x41\x00\x42": []int{XKEY_F5, XKEY_F6, XKEY_F7, XKEY_F8},
		"\x00\x43\x00\x44\x00\x52\x00\x53": []int{XKEY_F9, XKEY_F10, XKEY_INSERT, XKEY_DELETE},
		"\x1b[A\x1b[B\x1b[C\x1b[D":         []int{XKEY_UP_ARROW, XKEY_DOWN_ARROW, XKEY_RIGHT_ARROW, XKEY_LEFT_ARROW},
		"\x1b[H\x1b[F\x1b[K\x1b[V\x1b[U":   []int{XKEY_HOME, XKEY_END, XKEY_END, XKEY_PGUP, XKEY_PGDN},
		"\x1b[5~\x1b[6~":                   []int{XKEY_PGUP, XKEY_PGDN},
		"\x1b[@\x1b[2~\x1b[3~":             []int{XKEY_INSERT, XKEY_INSERT, XKEY_DELETE},
		"\x1bOP\x1bOQ\x1bOR\x1bOS":         []int{XKEY_F1, XKEY_F2, XKEY_F3, XKEY_F4},
		"\x1b[15~\x1b[17~\x1b[18~\x1b[19~": []int{XKEY_F5, XKEY_F6, XKEY_F7, XKEY_F8},
		"\x1b[20~\x1b[21~\x1b[23~\x1b[24~": []int{XKEY_F9, XKEY_F10, XKEY_F11, XKEY_F12},
	}

	for send, get := range keytest {
		var buffer []byte = []byte(send)
		_, err = server.Write(buffer)
		if err != nil {
			t.Error("server.Write:", err)
		}
		time.Sleep(time.Millisecond)

		var recv []int = make([]int, 0)
		var retries int = 0
		for {
			// input := d.WaitKey(0, 50)
			// running go test -count > 1 sometimes fails --
			// not getting all of the data we Write above.
			// data shows up on next read.
			// input := d.WaitKey(0, 100) // because we are retrying, reduce the wait time
			input := d.WaitKey(0, 50)

			if input != -1 {
				recv = append(recv, input)
			} else {
				// sometimes, running test using local loopback takes awhile for the characters
				// to arrive.
				if len(recv) != len(get) {
					if retries < 5 {
						retries++
						t.Logf("Retry %d want %d got %d\n", retries, len(get), len(recv))
						continue
					}
				}
				break
			}
		}

		if len(recv) != len(get) {
			t.Errorf("Send %#v, LEN expected %#v, got %#v", send, get, recv)
		} else {
			matches := true
			for idx, i := range get {
				if recv[idx] != i {
					matches = false
					break
				}
			}
			if !matches {
				t.Errorf("Send %#v, MATCH expected %#v, got %#v", send, get, recv)
			}
		}
	}

	buffer = make([]byte, 128)
	err = server.SetReadDeadline(time.Now().Add(time.Millisecond * 20))
	if err != nil {
		t.Error("server.SetReadDeadLine:", err)
	}
	r, err := server.Read(buffer)
	// input_test.go:131: server.Read: &net.OpError{Op:"read", Net:"tcp", Source:(*net.TCPAddr)(0xc000012f00), Addr:(*net.TCPAddr)(0xc000012f30), Err:(*poll.DeadlineExceededError)(0x6c39c0)}
	if !strings.Contains(err.Error(), "i/o timeout") {
		t.Errorf("Expected poll.DeadlineExceededError: %s / %#v", err.Error(), err)
	}
	if r != 0 {
		t.Errorf("Buffer After KeyTest: %#v\n", buffer[:r])
	}
	err = server.SetReadDeadline(time.Time{})
	if err != nil {
		t.Error("server.SetReadDeadLine:", err)
	}
	timeout := d.WaitKey(0, 50)
	if timeout != -1 {
		t.Errorf("Expected timeout, got %d / %X", timeout, timeout)
	} else {
		t.Logf("Ok! Buffer should be empty!  -1 (timeout)")
	}

	// Input test
	buffer = []byte("1234567890\r")
	_, err = server.Write(buffer)
	if err != nil {
		t.Error("server.Write:", err)
	}
	time.Sleep(time.Millisecond)

	var input string = d.Input(5)

	if input != "12345" {
		t.Errorf("Expected Input(5) = 12345, but got %#v", input)
	}

	// I'm not sure what they extra characters are in the buffer here.
	buffer = make([]byte, 128)
	err = server.SetReadDeadline(time.Now().Add(time.Millisecond * 20))
	if err != nil {
		t.Error("server.SetReadDeadLine:", err)
	}
	r, err = server.Read(buffer)
	if err != nil {
		t.Errorf("server.Read: %#v", err)
	}
	var result string = string(buffer[:r])
	expected := "     \x08\x08\x08\x08\x0812345\x07\x07\x07\x07\x07"
	if result != expected {
		t.Errorf("Buffer Input(5): Expected %#v, got %#v\n", expected, result)
	}
	err = server.SetReadDeadline(time.Time{})
	if err != nil {
		t.Error("server.SetReadDeadLine:", err)
	}

	buffer = []byte("12345678\x08\x089\r")
	_, err = server.Write(buffer)
	if err != nil {
		t.Error("server.Write:", err)
	}
	time.Sleep(time.Millisecond)

	input = d.Input(5)

	if input != "1239" {
		t.Errorf("Expected Input(5) = 1239, but got %#v", input)
	}

	buffer = []byte("12\x08\x08\x08987\x00\x48654321\r")
	_, err = server.Write(buffer)
	if err != nil {
		t.Error("server.Write:", err)
	}
	time.Sleep(time.Millisecond)

	input = d.Input(5)

	if input != "98765" {
		t.Errorf("Expected Input(5) = 98765, but got %#v", input)
	}

	server.Close()

	var hungup int = d.WaitKey(1, 0)
	if hungup != -2 {
		t.Errorf("Expected -2 (hangup), got %d", hungup)
	}

	if !d.Disconnect() {
		t.Errorf("Disconnected flag shows: %t (should be true)", d.Disconnect())
	}

	/*
		if !d.HasKey() {
			t.Error("HasKey should return true (disconnected).")
		}
	*/

	hungup = d.GetKey()

	if hungup != -2 {
		t.Errorf("Expected -2 (hangup), got %d", hungup)
	}
	client.Close()
	time.Sleep(time.Millisecond)

	var blank string = d.Input(5)

	if blank != "" {
		t.Errorf("Input should return blank (hangup).")
	}

	hungup = d.getch()

	if hungup != -2 {
		t.Errorf("Expected -2 (hangup), got %d", hungup)
	}

	d.Write("\x00")
	time.Sleep(time.Millisecond)
}

func TestDisplayInput(t *testing.T) {
	verify := map[int]string{1: " \x08",
		2: "  \x08\x08",
		5: "     \x08\x08\x08\x08\x08",
	}

	for count, expect := range verify {
		got := DisplayInput(count)
		if expect != got {
			t.Errorf("DisplayInput %d, expected %#v, got %#v", count, expect, got)
		}
	}
}