package door

import (
	"flag"
	"fmt"

	"log"
	"net"
	"os"
	"testing"
	"time"
)

func clear_socket(socket net.Conn, t *testing.T) string {
	// Clear out the data that's pending in the socket.

	buffer := make([]byte, 1204)
	var r int
	var err error

	err = socket.SetReadDeadline(time.Now().Add(time.Millisecond * 20))
	if err != nil {
		t.Error("socket.SetReadDeadLine:", err)
	}
	r, err = socket.Read(buffer)
	if err != nil {
		t.Errorf("socket.Read: %#v", err)
	}
	// t.Errorf("Buffer : %#v\n", buffer[:r])
	err = socket.SetReadDeadline(time.Time{})
	if err != nil {
		t.Error("socket.SetReadDeadLine:", err)
	}
	return string(buffer[:r])
}

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
	if !KEEP_LOGS {
		defer os.Remove(tmpFile.Name())
	} else {
		t.Logf("See: %s\n", 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{} // Deprecated: 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 - (filename is from Init name + node #)
	if !KEEP_LOGS {
		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)
	}

	clear_socket(server, t)
	// This should all be received by above (I think)...
	/*
		// 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][]rune{
		"\x1b":     []rune{0x1b},
		"\x0d\x00": []rune{0x0d},
		"\x0d\x0a": []rune{0x0d},
		"\x0dCQ":   []rune{0x0d, 'C', 'Q'},
		"\x0dA":    []rune{0x0d, 'A'},
		"\x0dCAT":  []rune{0x0d, 'C', 'A', 'T'},
	}

	keyextest := map[string][]Extended{
		"\x00\x50\x00\x48\x00\x4b\x00\x4d": []Extended{DOWN_ARROW, UP_ARROW, LEFT_ARROW, RIGHT_ARROW},
		"\x00\x47\x00\x4f\x00\x49\x00\x51": []Extended{HOME, END, PAGE_UP, PAGE_DOWN},
		"\x00\x3b\x00\x3c\x00\x3d\x00\x3e": []Extended{F1, F2, F3, F4},
		"\x00\x3f\x00\x40\x00\x41\x00\x42": []Extended{F5, F6, F7, F8},
		"\x00\x43\x00\x44\x00\x52\x00\x53": []Extended{F9, F10, INSERT, DELETE},
		"\x1b[A\x1b[B\x1b[C\x1b[D":         []Extended{UP_ARROW, DOWN_ARROW, RIGHT_ARROW, LEFT_ARROW},
		"\x1b[H\x1b[F\x1b[K\x1b[V\x1b[U":   []Extended{HOME, END, END, PAGE_UP, PAGE_DOWN},
		"\x1b[5~\x1b[6~":                   []Extended{PAGE_UP, PAGE_DOWN},
		"\x1b[@\x1b[2~\x1b[3~":             []Extended{INSERT, INSERT, DELETE},
		"\x1bOP\x1bOQ\x1bOR\x1bOS":         []Extended{F1, F2, F3, F4},
		"\x1b[15~\x1b[17~\x1b[18~\x1b[19~": []Extended{F5, F6, F7, F8},
		"\x1b[20~\x1b[21~\x1b[23~\x1b[24~": []Extended{F9, F10, F11, F12},
	}

	keyWait := time.Duration(50 * time.Millisecond)

	// Verify input is empty
	for {
		input, ex, err := d.WaitKey(keyWait)
		if err == nil {
			t.Errorf("Keys: %#v, %#v\n", input, ex)
		} else {
			break
		}
	}

	t.Logf("Starting keytest\n")

	// 5 * time.Second) //50 * time.Millisecond)

	for send, get := range keytest {
		if VERBOSE_TEST {
			t.Logf("Sending %#v: Expect: %#v\n", send, get)
		}
		var buffer []byte = []byte(send)
		_, err = server.Write(buffer)
		if err != nil {
			t.Error("server.Write:", err)
		}
		time.Sleep(time.Millisecond)

		var recv []rune = make([]rune, 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, _, err := d.WaitKey(keyWait)
			if VERBOSE_TEST {
				t.Logf(">> %#v, %#v\n", input, err)
			}

			if err == nil {
				recv = append(recv, input)
				// } else {
				// sometimes, running test using local loopback takes awhile for the characters
				// to arrive.
				if len(recv) != len(get) {
					continue
					/*
						if retries < 5 {
							retries++
							t.Logf("Retry %d want %d got %d\n", retries, len(get), len(recv))
							continue
						}
					*/
				}
				break
			} else {
				t.Logf("WaitKey Err: %#v\n", err)
				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("Sent %#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("Sent %#v, MATCH expected %#v, got %#v", send, get, recv)
			}
		}
	}

	t.Logf("Starting keyextest\n")

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

		var recv []Extended = make([]Extended, 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
			_, ex, err := d.WaitKey(keyWait)

			if err == nil {
				recv = append(recv, ex)

				// sometimes, running test using local loopback takes awhile for the characters
				// to arrive.
				if len(recv) != len(get) {
					continue
					/*
						if retries < 5 {
							retries++
							t.Logf("Retry %d want %d got %d\n", retries, len(get), len(recv))
							continue
						}
					*/
				}
				break
			} else {
				t.Logf("WaitKey err: #%v\n", err)
				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("Sent %#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("Sent %#v, MATCH expected %#v, got %#v", send, get, recv)
			}
		}
	}

	clear_socket(server, t)

	/*
		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)
		}
	*/

	t.Logf("Starting WaitKey timeout...\n")
	_, _, timeout := d.WaitKey(keyWait)
	if timeout == nil {
		t.Errorf("Expected timeout, got %d / %X", timeout, timeout)
	} else {
		t.Logf("Ok! Buffer should be empty!  -1 (timeout)")
	}

	t.Logf("Starting input test\n")
	// 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)
	}

	// What's a good way to clear out the receive buffer here??

	// I'm not sure what they extra characters are in the buffer here.
	var result string = clear_socket(server, t)

	/*
		buffer = make([]byte, 128)
		err = server.SetReadDeadline(time.Now().Add(time.Millisecond * 20))
		if err != nil {
			t.Error("server.SetReadDeadLine:", err)
		}
		var r int

		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)
	}

	t.Logf("server close")
	log.Println("server close")

	server.Close()

	time.Sleep(time.Millisecond)

	_, _, err = d.WaitKey(keyWait)
	if err != ErrDisconnected {
		t.Errorf("Expected ErrDisconnected, got %#v", err)
	}

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

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

	_, err = d.ReadRune() // GetKey()

	if err != ErrDisconnected {
		t.Errorf("Expected ErrDisconnected, got %#v", err)
	}

	t.Logf("client close")
	client.Close()
	time.Sleep(time.Millisecond)

	t.Logf("Input on closed server and client")
	var blank string = d.Input(5)

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

	_, err = d.ReadRune() // getch()

	if err != ErrDisconnected {
		t.Errorf("Expected ErrDisconnected, got %#v", err)
	}

	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)
		}
	}
}