package main

import (
	"fmt"
	"net"

	"gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/lyrebird/transports"
)

// typedef int (*protect_callback)(int);
// static int protect_helper(protect_callback cb, int fd) { return cb(fd); }
import "C"

var protectFn C.protect_callback

//export SanityCheck
func SanityCheck() C.int {
	return 42
}

//export InitialiseTransports
func InitialiseTransports() *C.char {
	Log(levelDebug, "initialising lyrebird transports")
	err := transports.Init()
	if err != nil {
		Log(levelDebug, "lyrebird init failed: %s", err)
		errStr := fmt.Sprintf("%s", err)
		errStrC := C.CString(errStr)
		return errStrC
	} else {
		return nil
	}
}

//export SetProtectCallback
func SetProtectCallback(cb C.protect_callback) {
	Log(levelDebug, "set protect callback")
	protectFn = cb
}

func protect(fd int) bool {
	if protectFn != nil {
		return C.protect_helper(protectFn, C.int(fd)) == 1
	}
	return true
}

func startTransport(transport string, socksAddr string, stateDir string) error {
	t := transports.Get(transport)
	if t == nil {
		return fmt.Errorf("no registered transport %s", transport)
	}
	f, err := t.ClientFactory(stateDir)
	if err != nil {
		return fmt.Errorf("initialising %s client factory failed: %w", transport, err)
	}
	listener, err := net.Listen("tcp", socksAddr)
	if err != nil {
		return fmt.Errorf("listening on provided socksAddr for %s failed: %w", transport, err)
	}
	go func() {
		err = clientAcceptLoop(f, listener)
		if err != nil {
			// FIXME(eta): We should potentially restart the transport if this happens?
			Log(levelError, "%s - client accept loop terminated: %s", transport, err)
		}
	}()
	return nil
}

//export StartTransport
func StartTransport(transportName *C.char, socksAddr *C.char, stateDir *C.char) *C.char {
	socksAddrGo := C.GoString(socksAddr)
	transportNameGo := C.GoString(transportName)
	stateDirGo := C.GoString(stateDir)
	Log(levelDebug, "starting transport %s", transportNameGo)

	err := startTransport(transportNameGo, socksAddrGo, stateDirGo)

	if err != nil {
		errStr := fmt.Sprintf("%s", err)
		errStrC := C.CString(errStr)
		return errStrC
	} else {
		return nil
	}
}

func main() {}
