package main

import (
	"flag"
	"fmt"
	"net"
	"os"
	"strconv"
	"strings"
	"syscall"
	"time"
)

const (
	connHost = "0.0.0.0"
	connPort = "8080"
	connType = "tcp"
)

func main() {
	var port int

	flag.IntVar(&port, "p", 0, "Port number to listen on")
	flag.Parse()

	if port == 0 && flag.NArg() != 1 {
		fmt.Println("I need a Port and a commandline to execute.")
		flag.PrintDefaults()
		os.Exit(2)
	}

	fmt.Println("Starting " + connType + " server on " + connHost + ":" + strconv.Itoa(port))
	l, err := net.Listen("tcp", "0.0.0.0:"+strconv.Itoa(port))
	if err != nil {
		fmt.Println("Error listening:", err.Error())
		os.Exit(1)
	}
	defer l.Close()

	for {
		c, err := l.Accept()
		if err != nil {
			fmt.Println("Error connecting:", err.Error())
			return
		}
		fmt.Println("Client connected.")
		fmt.Println("Client " + c.RemoteAddr().String() + " connected.")

		// fork goes here
		go handleConnection(c, flag.Arg(0))
		c = nil
	}
}

func drain(conn net.Conn) {
	conn.SetReadDeadline(time.Now().Add(time.Second * 2))
	recvData := make([]byte, 32)
	n, err := conn.Read(recvData)
	if n > 0 {
		// do something with recvData[:n]
		fmt.Printf(" [%d]\n", n)
	}
	if err != nil {
		fmt.Printf("drain Error: %#v\n", err)
	}
	conn.SetReadDeadline(time.Time{})
}

func waitForIt(conn net.Conn, pid int) {
	process, _ := os.FindProcess(pid)
	state, _ := process.Wait()
	fmt.Printf("%d State: %#v\n", pid, state)
	conn.Write([]byte("\r\nThanks for calling!\r\n"))
	conn.Close()

	fmt.Println("Connection closed.")
}

func handleConnection(conn net.Conn, cmd string) {

	// Something here confuses the crap out of Windows Telnet Client!

	conn.Write([]byte("\xff\xfb\x01\xff\xfb\x03\xff\xfd\x10"))
	drain(conn)

	fmt.Printf("%#v\n", conn)
	tcp, _ := conn.(*net.TCPConn)
	fmt.Printf("%#v\n", tcp)

	// https://github.com/golang/go/issues/10350

	raw, err := tcp.SyscallConn()
	fmt.Printf("%#v, err: %#v\n", raw, err)

	var socket_fd uintptr
	raw.Control(func(fd uintptr) {
		socket_fd = fd
	})
	fmt.Printf("Socket: %#v\n", socket_fd)
	proc, _ := syscall.GetCurrentProcess()
	var dup_socket_fd syscall.Handle

	err = syscall.DuplicateHandle(proc, syscall.Handle(socket_fd),
		proc, &dup_socket_fd, syscall.DUPLICATE_SAME_ACCESS, true, 0)
	if err != nil {
		fmt.Printf("ERR DuplicateHandle: %#v\n", err)
	}

	fmt.Printf("Dup: %#v\n", dup_socket_fd)

	// windows:  tcp.File() failes.  net.OpError
	/*
		file, err := tcp.File()
		fd := file.Fd()

		fmt.Printf("FD %#v, %#v\n", fd, err)
	*/
	/*
		tcp, _ := conn.(*net.TCPConn)
		file, _ := tcp.File()
		fd := file.Fd()
	*/

	// what we actually put into the file
	filefd := int64(dup_socket_fd)
	// fdstr := strconv.Itoa(int(filefd))

	fp, _ := os.Create("door32.sys")
	fmt.Fprintf(fp, "2\n%d\n38400\nFake Door32\n1\nBugz Laundry\nBugz\n100\n120\n1\n1\n", filefd)
	fp.Close()

	parts := strings.Split(cmd, " ")
	// parts = append(parts, fdstr)

	// id, _ := syscall.ForkExec(parts[0], parts[1:], nil)
	fmt.Printf("Running: [%s] with %#v\n", parts[0], parts[1:])

	// https://stackoverflow.com/questions/35336131/createprocess-with-golang

	var si syscall.StartupInfo
	var pi syscall.ProcessInformation

	argv := syscall.StringToUTF16Ptr(cmd)

	err = syscall.CreateProcess(nil, argv, nil, nil, true, 0, nil, nil, &si, &pi)

	fmt.Printf("Return: %#v\n", err)
	raw = nil

	if err == nil {
		event, e := syscall.WaitForSingleObject(pi.Process, syscall.INFINITE)
		fmt.Printf("Event %#v, err: %#v\n", event, e)
	}

	/*
		exec_cmd := exec.Command(parts[0], parts[1:]...)
		fmt.Printf("exec_cmd: %#v\n", exec_cmd)

		err = exec_cmd.Run()
		if err != nil {
			fmt.Println("Error: ", err)
		}

		fmt.Println("Command completed.")
		// UH, WHAT?  I didn't see that it started, and it certainly didn't keep running.  :(
	*/

	// id := 13

	/*
		id, _ := syscall.ForkExec(parts[0], parts,
			&syscall.ProcAttr{
				Env: os.Environ(),
				Sys: &syscall.SysProcAttr{
					Setsid: true,
				},
				Files: []uintptr{0, 1, 2, fd}, // print message to the same pty
			})

	*/
	// go waitForIt(conn, id)

	// id, _, _ := syscall.Syscall(syscall.SYS_FORK, 0, 0, 0)
	/*
		if id == 0 {
			// child process

			exec.Command(parts[0], parts[1:]...)
			os.Exit(2)
		}
	*/
	// fmt.Printf("Child started: %d\n", id)
	// return

	conn.Write([]byte("Welcome back!\r\nSeeya!\r\n"))

	// I can't seem to get this to close the connection.  :(
	conn.Close()
	syscall.CloseHandle(syscall.Handle(socket_fd))
	tcp.SetLinger(0)
	tcp.Close()

	/*
		for {
			buffer, err := bufio.NewReader(conn).ReadBytes('\r') // HMM.  \r ?

			if err != nil {
				fmt.Println("Client left.")
				conn.Close()
				return
			}

			// What am I seeing here?

			log.Println("Client message:", string(buffer[:len(buffer)-1]))

			conn.Write(buffer)
			conn.Write([]byte("\n"))
		}
	*/
}