package connectivity

import (
	"net"
	"sync"
)

// ProxyACN because there is rarely a problem that can't be solved by another layer of indirection.
// ACN is a core resource that many parts of a system may need access too e.g. all clients and servers need an instance
// and a UI may also need status information and a configuration interface.
// We want to allow configuration and replacement of an ACN without impacting the API of all downstream systems - introducing
// ProxyACN - a wrapper around an ACN that allows safe replacement of a running ACN that is transparent to callers.
type ProxyACN struct {
	acn ACN

	// All operations on the underlying acn are assumed to be thread safe, however changing the actual
	// acn in ReplaceACN will lock to force an ordering of Close and Callback
	lock sync.Mutex
}

func NewProxyACN(acn ACN) ProxyACN {
	return ProxyACN{
		acn: acn,
	}
}

// ReplaceACN closes down the current ACN and replaces it with a new ACN.
func (p *ProxyACN) ReplaceACN(acn ACN) {
	p.lock.Lock()
	defer p.lock.Unlock()
	p.acn.Close()
	acn.SetStatusCallback(p.acn.GetStatusCallback())
	acn.SetVersionCallback(p.acn.GetVersionCallback())
	p.acn = acn
}

func (p *ProxyACN) GetInfo(addr string) (map[string]string, error) {
	return p.acn.GetInfo(addr)
}

func (p *ProxyACN) GetBootstrapStatus() (int, string) {
	return p.acn.GetBootstrapStatus()
}

func (p *ProxyACN) WaitTillBootstrapped() error {
	return p.acn.WaitTillBootstrapped()
}

func (p *ProxyACN) SetStatusCallback(callback func(int, string)) {
	p.acn.SetStatusCallback(callback)
}

func (p *ProxyACN) SetVersionCallback(callback func(string)) {
	p.acn.SetVersionCallback(callback)
}

func (p *ProxyACN) Restart() {
	p.acn.Restart()
}

func (p *ProxyACN) Open(hostname string) (net.Conn, string, error) {
	return p.acn.Open(hostname)
}

func (p *ProxyACN) Listen(identity PrivateKey, port int) (ListenService, error) {
	return p.acn.Listen(identity, port)
}

func (p *ProxyACN) GetPID() (int, error) {
	return p.acn.GetPID()
}

func (p *ProxyACN) GetVersion() string {
	return p.acn.GetVersion()
}

func (p *ProxyACN) Close() {
	p.acn.Close()
}

func (p *ProxyACN) GetStatusCallback() func(int, string) {
	return p.acn.GetStatusCallback()
}

func (p *ProxyACN) GetVersionCallback() func(string) {
	return p.acn.GetVersionCallback()
}
