// TODO wtf?
package pool

import (
	"net"
	"sync"
	"sync/atomic"
)

var (
	defaultMaxConns = 20
	defaultStrategy = &RoundrobinStrategy{
		lastSelected: initialRoundrobinAtomic(),
	}
)

func initialRoundrobinAtomic() atomic.Int64 {
	a := atomic.Int64{}
	a.Store(-1)
	return a
}

type ConnSelectionStrategy interface {
	Select() int
}

type RoundrobinStrategy struct {
	lastSelected atomic.Int64
}

func (r *RoundrobinStrategy) Select() int {
	return int(r.lastSelected.Add(1))
}

type ConnPool struct {
	m        sync.RWMutex
	strategy ConnSelectionStrategy
	conns    []net.Conn
}

type newConnPoolOpts struct {
	strategy ConnSelectionStrategy
	maxConns int
}

func newNewConnPoolOpts() newConnPoolOpts {
	return newConnPoolOpts{
		strategy: defaultStrategy,
		maxConns: defaultMaxConns,
	}
}

type NewConnPoolOpt func(p *newConnPoolOpts)

func WithStrategy(s ConnSelectionStrategy) NewConnPoolOpt {
	return func(p *newConnPoolOpts) {
		p.strategy = s
	}
}

func WithMaxConns(mc int) NewConnPoolOpt {
	return func(p *newConnPoolOpts) {
		p.maxConns = mc
	}
}

func NewConnPool(opts ...NewConnPoolOpt) *ConnPool {
	o := newNewConnPoolOpts()
	for _, opt := range opts {
		opt(&o)
	}
	return &ConnPool{
		conns:    make([]net.Conn, 0),
		strategy: o.strategy,
	}
}

func (p *ConnPool) SelectConn() net.Conn {
	p.m.RLock()
	defer p.m.RUnlock()
	return p.conns[p.strategy.Select()]
}

func (p *ConnPool) AddConn(conn net.Conn) {
	p.m.Lock()
	defer p.m.Unlock()
	p.conns = append(p.conns, conn)
}
