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 rdm_service_demo(conn *tipc.TipcConn, up bool, srv_node *uint32) {
	var cli, srv tipc.TipcAddr
	var cbuf, sbuf, rmsg []byte = make([]byte, BUF_SZ), make([]byte, BUF_SZ), make([]byte, BUF_SZ)
	var msg []byte = []byte("Hello World")
	var rc, err int

	srv = tipc.TipcAddr{RDM_SRV_TYPE, SRV_INST, 0}

	if (!up) {
		fmt.Printf("Service on SOCK_RDM went down\n")
		return
	}
	fmt.Printf("\n-------------------------------------\n")
	fmt.Printf("Service on SOCK_RDM came up\n")

	tipc.Tipc_ntoa(&srv, sbuf)
	fmt.Printf("Sending msg: %s on SOCK_RDM\n -->%s\n", msg, sbuf)

	if ret := conn.Sock_Rejectable(); ret < 0 {
		fmt.Printf("Set rejectable failed\n")
		return
	}

	if ret := conn.Sendto(msg, &srv); ret <= 0 {
		fmt.Printf("sendto() on SOCK_RDM failed\n")
		return
	}

	rc = conn.Recvfrom(rmsg, &srv, &cli, &err)
	switch {
		case rc < 0:
		case err != 0:
			fmt.Printf("Unexpected response on SOCK_RDM, rc:%d, err: %d\n", rc, err)
	}

	fmt.Printf("Received response: %s\n %s <-- %s\n", rmsg,
		tipc.Tipc_ntoa(&cli, cbuf),
		tipc.Tipc_ntoa(&srv, sbuf))
	*srv_node = *(*uint32)(unsafe.Pointer(&srv.Node))
}

func rdm_reject_demo(conn *tipc.TipcConn, up bool, srv_node uint32) {
	var srv, cli, dum tipc.TipcAddr
	dum = tipc.TipcAddr{42, 1, uint32(srv_node)}

	var cbuf, sbuf []byte = make([]byte, BUF_SZ), make([]byte, BUF_SZ)
	var msg []byte = []byte("Hello World")
	var err, rc int

	if (!up) {
		return
	}

	fmt.Printf("\nSending msg: %s on SOCK_RDM \n --> %s (non-existing)\n", msg,
			tipc.Tipc_ntoa(&dum, sbuf))

	if ret := conn.Sendto(msg, &dum); ret <=0 {
		fmt.Printf("Client sendto() failed: No route to host\n")
		return;
	}

	rc = conn.Recvfrom(msg, &srv, &cli, &err)
	switch {
		case rc < 0:
		case err == 0:
			fmt.Printf("Unexpected response on SOCK_RDM, rc:%d, err: %d\n", rc, err)
	}
	fmt.Printf("Received rejected msg: %s\n %s <-- %s, err %d\n",
			msg, tipc.Tipc_ntoa(&cli, cbuf),
			tipc.Tipc_ntoa(&srv, sbuf), err)

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

func stream_service_demo(conn *tipc.TipcConn, up bool) {
	var srv tipc.TipcAddr = tipc.TipcAddr{STREAM_SRV_TYPE, SRV_INST, 0}
	var sbuf, rmsg, msg []byte = make([]byte, BUF_SZ), make([]byte, BUF_SZ), []byte("Hello World")

	if (!up) {
		fmt.Printf("Service on SOCK_STREAM went down\n")
		return;
	}
	fmt.Printf("\n\n-------------------------------------\n")
	fmt.Printf("Service on SOCK_STREAM came up\n");
	tipc.Tipc_ntoa(&srv, sbuf);

	fmt.Printf("Performing two-way connect\n with message %s   -->%s\n", msg, sbuf);
	if ret := conn.Sendto(msg, &srv); ret <= 0 {
		fmt.Printf("send() on SOCK_STREAM failed\n")
		return
	}

	if ret := conn.Recv(rmsg, false); ret <= 0 {
		fmt.Printf("Unexpected response on SOCK_STREAM, ret: %d\n", ret)
		return
	}

	fmt.Printf("Received response: %s on SOCK_STREAM connection\n", rmsg)
	fmt.Printf("SOCK_STREAM connection established \n --> %s\n", sbuf)
	fmt.Printf("-------------------------------------\n")

}


func seqpacket_service_demo(conn *tipc.TipcConn, up bool) {
	var srv tipc.TipcAddr = tipc.TipcAddr{SEQPKT_SRV_TYPE, SRV_INST, 0}
	var sbuf,  rmsg, msg []byte = make([]byte, BUF_SZ), make([]byte, BUF_SZ), []byte("Hello World")

	if (!up) {
		fmt.Printf("Service on SOCK_SEQPACKET went down\n")
		return;
	}
	fmt.Printf("\n\n-------------------------------------\n")
	fmt.Printf("Service on SOCK_SEQPACKET came up\n")
	tipc.Tipc_ntoa(&srv, sbuf);
	fmt.Printf("Connecting to:              -->%s\n",sbuf)
	if ret := conn.Connect(&srv); ret < 0 {
		fmt.Printf("connect() on SOCK_SEQPACKET failed, ret:%d\n", ret)
		return
	}

	fmt.Printf("Sending msg: %s on connection\n", msg)
	if ret := conn.Send(msg); ret <= 0 {
		fmt.Printf("send() on SOCK_SEQPACKET failed\n")
		return
	}

	if ret := conn.Recv(rmsg, false); ret <= 0 {
		fmt.Printf("Unexpected response on SOCK_SEQPACKET, ret: %d\n", ret)
		return
	}

	fmt.Printf("Received response: %s on SOCK_SEQPACKET connection\n", rmsg)
	fmt.Printf("-------------------------------------\n")
}

func main() {
	var up bool
	var srv_node uint32 = 0
	var sbuf []byte = make([]byte, BUF_SZ)
	var srv tipc.TipcAddr = tipc.TipcAddr{RDM_SRV_TYPE, SRV_INST, 0}

	// Create traffic sockets
	var conn_rdm, conn_stream, conn_seqpacket *tipc.TipcConn

	// Topology server
	var topo_srv *tipc.TipcTopSrvConn

	fmt.Printf("****** TIPC GOLang API Demo Client Started ******\n\n");

	tipc.Tipc_ntoa(&srv, sbuf);
	fmt.Printf("Waiting for Service %s\n", sbuf);

	// Wait server come up
	tipc.Srv_wait(&srv, -1);

	conn_rdm, _ = tipc.NewConn(syscall.SOCK_RDM)
	conn_stream, _ = tipc.NewConn(syscall.SOCK_STREAM)
	conn_seqpacket, _ = tipc.NewConn(syscall.SOCK_SEQPACKET)

	switch {
		case conn_rdm == nil:
                        fmt.Println("Create socket SOCK_RDM failed!")
			return
		case conn_stream == nil:
                        fmt.Println("Create socket SOCK_STREAM failed!")
			return
		case conn_seqpacket == nil:
			fmt.Println("Create socket SOCK_SEQPACKET failed!")
			return
	}

	defer conn_rdm.Close()
	defer conn_stream.Close()
	defer conn_seqpacket.Close()

	fds := make([]PollFd, 4)

	fds[0] = PollFd{int32(conn_rdm.GetFd()), POLLIN | POLLHUP, 0}
	fds[1] = PollFd{int32(conn_stream.GetFd()), POLLIN | POLLHUP, 0}
	fds[2] = PollFd{int32(conn_seqpacket.GetFd()), POLLIN | POLLHUP, 0}

	// Subscribe for service events
	topo_srv = tipc.NewTopSrvConn(srv_node)
	fds[3] = PollFd{int32(topo_srv.GetFd()), POLLIN | POLLHUP, 0}

	if ret := topo_srv.Srv_subscr(RDM_SRV_TYPE, 0, uint32(^uint32(0) >> 1), false, -1); ret != 0 {
		fmt.Printf("subscribe for RDM server failed\n")
		return
	}

	if ret := topo_srv.Srv_subscr(STREAM_SRV_TYPE, 0, uint32(^uint32(0) >> 1), false, -1); ret != 0 {
		fmt.Printf("subscribe for STREAM server failed\n")
		return
	}
	if ret := topo_srv.Srv_subscr(SEQPKT_SRV_TYPE, 0, uint32(^uint32(0) >> 1), false, -1); ret != 0 {
		fmt.Printf("subscribe for SEQPACKET server failed\n")
		return
	}

	for _, _, e := syscall.Syscall(syscall.SYS_POLL, uintptr(unsafe.Pointer(&fds[0])), uintptr(len(fds)), uintptr(3000000)); e == 0; {
		if fds[1].revents & POLLHUP != 0 {
			fmt.Printf("SOCK_STREAM connection hangup\n");
			// Re-open socket
			conn_stream.Close()
			conn_stream, _ = tipc.NewConn(syscall.SOCK_STREAM)
			if  conn_stream == nil {
				break
			}
			fds[1].fd = int32(conn_stream.GetFd())
		}
		if fds[2].revents & POLLHUP != 0 {
			fmt.Printf("SOCK_SEQPACKET connection hangup\n");
			// Re-open socket
			conn_seqpacket.Close()
			conn_seqpacket, _ = tipc.NewConn(syscall.SOCK_SEQPACKET)
			if conn_seqpacket == nil {
				break
			}
			fds[2].fd = int32(conn_seqpacket.GetFd())
		}

		if fds[3].revents & POLLIN != 0 {
			var expired bool = false
			if ret := topo_srv.Srv_evt(&srv, nil, &up, &expired); ret != 0 {
				fmt.Printf("reception of service event failed\n")
			}
			if srv.Type == RDM_SRV_TYPE {
				rdm_service_demo(conn_rdm, up, &srv_node)
				rdm_reject_demo(conn_rdm, up, srv_node)
			}
			if srv.Type == STREAM_SRV_TYPE {
				// Re-open socket
				if up == false {
					conn_stream.Close()
					conn_stream, _ = tipc.NewConn(syscall.SOCK_STREAM)
					if conn_stream == nil {
						break
					}
					fds[1].fd = int32(conn_stream.GetFd())
				}
				stream_service_demo(conn_stream, up)
			}
			if (srv.Type == SEQPKT_SRV_TYPE) {
				// Re-open socket
				if up == false {
					conn_seqpacket.Close()
					conn_seqpacket, _ = tipc.NewConn(syscall.SOCK_SEQPACKET)
					if conn_seqpacket == nil {
						break
					}
					fds[2].fd = int32(conn_seqpacket.GetFd())
				}
				seqpacket_service_demo(conn_seqpacket, up)
			}
		}
	}
	fmt.Printf("\n****** TIPC GOLang API Demo Finished ******\n\n")
}

