package main

/*
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <poll.h>
*/
import "C"

import (
	"fmt"
	"../../../golang"
	"syscall"
	"unsafe"
)

// Application test
const (
	RDM_SRV_TYPE     = 18888
	STREAM_SRV_TYPE  = 17777
	SEQPKT_SRV_TYPE  = 16666
	SRV_INST         = 17
	BUF_SZ           = 40
)

const (
    POLLIN   = 0x0001
    POLLPRI  = 0x0002
    POLLOUT  = 0x0004
    POLLERR  = 0x0008
    POLLHUP  = 0x0010
    POLLNVAL = 0x0020
)

type PollFd struct {
	fd      int32
	events  int16
	revents int16
}

func bind_service(stype uint32, node uint32, sktype uint32, sk_str string) (conn *tipc.TipcConn) {
	var sockid tipc.TipcAddr
	var err error
	sbuf := make([]byte, BUF_SZ)

	conn, err = tipc.NewConn(sktype)
	if err != nil {
		fmt.Printf("Create socket failed: %s!\n", err);
		return nil
	}

	if ret := conn.Bind(stype, 0, uint32(^uint32(0) >> 1), node); ret != 0 {
		fmt.Printf("failed to bind %s socket\n", sk_str)
		defer conn.Close()
		return nil
	}

	conn.Sockaddr(&sockid);
	tipc.Tipc_ntoa(&sockid, sbuf);

	fmt.Printf("Bound %v socket %v to %v\n", sk_str,
		tipc.Tipc_ntoa(&sockid, sbuf),
		tipc.Tipc_rtoa(stype, 0, uint32(^uint32(0) >> 1), node, sbuf))

	return conn
}

func recv_rdm_msg(conn *tipc.TipcConn) {
	var cli, srv tipc.TipcAddr
	var cbuf, sbuf, msg []byte
	var result int

	cbuf = make([]byte, BUF_SZ)
	sbuf = make([]byte, BUF_SZ)
	msg = make([]byte, BUF_SZ)

	fmt.Printf("\n-------------------------------------\n")
	if ret := conn.Recvfrom(msg, &cli, &srv, &result); ret <= 0 {
		fmt.Printf("unexpected message on RDM socket\n")
	}

	fmt.Printf("Received msg: %s on SOCK_RDM\n %s <-- %s\n", msg, tipc.Tipc_ntoa(&srv, sbuf), tipc.Tipc_ntoa(&cli, cbuf))

	msg = []byte("Huh?")
	fmt.Printf("Responding with: %s\n                        --> %s\n", msg, tipc.Tipc_ntoa(&cli, sbuf));

	if ret := conn.Sendto(msg, &cli); ret <=0 {
		fmt.Printf("Server: failed to send\n")
	}
	fmt.Printf("-------------------------------------\n")

}

func recv_stream_setup(conn *tipc.TipcConn) (newconn *tipc.TipcConn) {
	var cli tipc.TipcAddr
	var cbuf, msg []byte
	var newsd int

	cbuf = make([]byte, BUF_SZ)
	msg = make([]byte, BUF_SZ)

	fmt.Printf("\n-------------------------------------\n")
	conn.Listen(32)

	newsd = conn.Accept(&cli)
	if newsd <= 0 {
		fmt.Printf("accept on SOCK_STREAM failed\n")
		return nil
	}

	newconn = tipc.NewConnFromFd(newsd)
	if ret := newconn.Recv(msg, false); ret <= 0 {
		fmt.Printf("unexpected message on STREAM socket, err: %d\n", ret)
		newconn.Close()
		return nil
	}

	fmt.Printf("Received msg: %s on STREAM connection\n", msg)

	msg = []byte("Huh?")
	fmt.Printf("Responding with: %s\n", msg)

	tipc.Tipc_ntoa(&cli, cbuf);
	fmt.Printf("SOCK_STREAM connection established\n --> %s\n", cbuf)

	if ret := newconn.Send(msg); ret <= 0 {
		fmt.Printf("failed to respond\n")
		newconn.Close()
		return nil
	}

	fmt.Printf("-------------------------------------\n")
	return newconn
}

func recv_seqpacket_setup(conn *tipc.TipcConn) (newconn *tipc.TipcConn) {
	var cli tipc.TipcAddr
	var cbuf, msg []byte
	var newsd int

	cbuf = make([]byte, BUF_SZ)
	msg = make([]byte, BUF_SZ)

	fmt.Printf("\n-------------------------------------\n")
	conn.Listen(32)

	newsd = conn.Accept(&cli);
	if newsd <= 0 {
		fmt.Printf("accept on SOCK_SEQPACKET failed\n")
		return nil
	}

	newconn = tipc.NewConnFromFd(newsd)
	if ret := newconn.Recv(msg, true); ret <= 0 {
		fmt.Printf("unexpected message on SEQPACKET socket\n")
		newconn.Close()
		return nil
	}

	fmt.Printf("Received msg: %s on SOCK_SEQPACKET connection\n", msg)

	msg = []byte("Huh?")
	fmt.Printf("Responding with: %s\n", msg)

	tipc.Tipc_ntoa(&cli, cbuf);
	fmt.Printf("SOCK_SEQPACKET connection established\n --> %s\n", cbuf)

	if ret := newconn.Send(msg); ret <= 0 {
		fmt.Printf("failed to respond\n")
		newconn.Close()
		return nil
	}

	fmt.Printf("-------------------------------------\n")

	return newconn
}

func main() {
	var node uint32 = 0
	var rdmsd, strsd, pktsd *tipc.TipcConn
	//var pfd [2]C.struct_pollfd

	fmt.Println("****** TIPC GOLang API Demo Server Started ******\n");

	if rdmsd = bind_service(RDM_SRV_TYPE, node, syscall.SOCK_RDM, "RDM"); rdmsd == nil {
		return
	}
	defer rdmsd.Close()

	if strsd = bind_service(STREAM_SRV_TYPE, 0, syscall.SOCK_STREAM, "STREAM"); strsd == nil {
		return
	}
	defer strsd.Close()

	if pktsd = bind_service(SEQPKT_SRV_TYPE, 0, syscall.SOCK_SEQPACKET, "SEQPACKET"); pktsd == nil {
		return
	}
	defer pktsd.Close()

	fds := make([]PollFd, 2)
	for {
		recv_rdm_msg(rdmsd)
		newstrsd := recv_stream_setup(strsd)
		newpktsd := recv_seqpacket_setup(pktsd)
		fds[0] = PollFd{int32(newstrsd.GetFd()), POLLIN | POLLHUP, 0}
		fds[1] = PollFd{int32(newpktsd.GetFd()), POLLIN | POLLHUP, 0}

		for _, _, e := syscall.Syscall(syscall.SYS_POLL, uintptr(unsafe.Pointer(&fds[0])), uintptr(len(fds)), uintptr(3000000)); e == 0; {
			for i:=0; i<2; i++ {
				if fds[i].revents & POLLHUP == 0 {
					continue
				}
				fmt.Printf("\n-------------------------------------\n")
				switch i {
					case 0:
						fmt.Printf("%s ", "SOCK_STREAM")
					case 1:
						fmt.Printf("%s ", "SOCK_SEQPACKET")
				}
				fmt.Printf("Connection hangup\n")
				newstrsd.Close()
				newpktsd.Close()
				fds[i].fd = 0
			}
			break
		}
	}
	fmt.Printf("\n****** TIPC GOLang API Demo Server Finished ******\n\n");
}

