package main

import (
	"bytes";
	"encoding/binary";
	"flag";
	"fmt";
	"net";
	"os";
	"os/signal";
	"syscall";
	"./build/msg/msg"
)

const (
	defPort = 7777;
	bufSize = 1024
)

var listenPort *int = flag.Int("p", defPort, "port to listen for connections")

// wait for incoming tcp connections
func acceptor(listener *net.TCPListener, quit chan bool) {
	var buf [bufSize]byte

	for {
		conn, e := listener.AcceptTCP()
		if e != nil {
			fmt.Fprintf(os.Stderr, "Error: %v\n", e)
			continue
		}

		num, e := conn.Read(&buf)
		if num < 0 {
			fmt.Fprintf(os.Stderr, "Error: %v\n", e)
			conn.Close()
			continue
		}

		go handleClient(conn, buf[0:num])
	}
}

// handle a single client connection
func handleClient(conn *net.TCPConn, bytebuf []byte) {
	message := new(msg.Message)
	buf := bytes.NewBuffer(bytebuf)

	binary.Read(buf, binary.LittleEndian, &message.SenderLen)
	s := make([]byte, message.SenderLen)
	buf.Read(s)
	message.SetSender(string(s))
	binary.Read(buf, binary.LittleEndian, &message.DataLen)
	d := make([]byte, message.DataLen)
	buf.Read(d)
	message.SetData(string(d))

	fmt.Printf("%s connected\n > %s\n\n",
		message.GetSender(), message.GetData())

	conn.Close()
}

// read from signal.Incoming channel and 
// return SIGINT is received
func signalHandler(quit chan bool) {
	for {
		select {
		case sig := <-signal.Incoming:
			fmt.Printf("Received signal %d\n", sig)
			if sig.(signal.UnixSignal) != syscall.SIGINT {
				continue
			}
			quit<- true
			return
		}
	}
}

func main() {
	flag.Parse()
	address := fmt.Sprintf("%s:%d", "127.0.0.1", *listenPort)
	quit := make(chan bool)

	socket, e := net.ResolveTCPAddr(address)
	if e != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", e)
		os.Exit(1)
	}
	listener, e := net.ListenTCP("tcp4", socket)
	if e != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", e)
		os.Exit(1)
	}

	go signalHandler(quit)

	fmt.Printf("Listening on %s:%d\n\n", socket.IP.String(), socket.Port)
	go acceptor(listener, quit)

	for {
		select {
		case <-quit:
			fmt.Printf("Shutting down\n")
			listener.Close()
			return
		}
	}
}

