package network

import (
	"errors"
	"net"
)

var ErrListenerStopped = errors.New("listener was stopped")

// GracefulListener provides safe and graceful net.Listener wrapper that prevents error on graceful shutdown
type GracefulListener struct {
	listener net.Listener
	stop     chan int
}

// Accept method
func (l *GracefulListener) Accept() (net.Conn, error) {
	for {
		c, err := l.listener.Accept()
		select {
		case <-l.stop:
			if c != nil {
				c.Close()
			}
			close(l.stop)
			l.stop = nil
			return nil, ErrListenerStopped
		default:

		}
		if err != nil {
			neterr, ok := err.(net.Error)
			if ok && neterr.Timeout() && neterr.Temporary() {
				continue
			}
		}
		return c, err
	}
}

// Close method
func (l *GracefulListener) Close() (err error) {
	l.listener.Close()
	if l.stop != nil {
		l.stop <- 0
	}
	return
}

// Addr method
func (l *GracefulListener) Addr() net.Addr {
	return l.listener.Addr()
}

// WrapListener wraps a net.Listener such that it can be closed gracefully
func WrapListener(l net.Listener) net.Listener {
	return &GracefulListener{
		listener: l,
		stop:     make(chan int),
	}
}
