package mc

import (
	"net"
	"sync"
)

// Pool is a pool of Conn that automatically manages multiple connections and
// can be used in place of Conn
type Pool struct {
	sync.RWMutex
	conns []*Conn
	robin chan *Conn
}

var _ Cacher = (*Pool)(nil)

// DialPool establishes "pool" count connections to the memcache server.
func DialPool(nett, addr string, pool int) (*Pool, error) {
	p := Pool{
		robin: make(chan *Conn, pool),
	}

	for i := 0; i < pool; i++ {
		nc, err := net.Dial(nett, addr)
		if err != nil {
			return nil, wrapError(StatusNetworkError, err)
		}

		cn := NewConnection(nc)
		p.conns = append(p.conns, cn)
		p.robin <- cn
	}

	return &p, nil
}

// GetConn retrieves a Conn from the pool.
func (p *Pool) GetConn() *Conn {
	select {
	case cn, ok := <-p.robin:
		if !ok {
			return nil
		}
		p.robin <- cn
		return cn
	}
	return nil
}

func (p *Pool) Auth(user, pass string) error {
	for _, cn := range p.conns {
		err := cn.Auth(user, pass)
		if err != nil {
			return err
		}
	}
	return nil
}
func (p *Pool) Close() error {
	for _, cn := range p.conns {
		err := cn.Close()
		if err != nil {
			return err
		}
	}
	close(p.robin)
	return nil
}
func (p *Pool) Get(key string) (val string, flags uint32, cas uint64, err error) {
	return p.GetConn().Get(key)
}
func (p *Pool) GAT(key string, exp uint32) (val string, flags uint32, cas uint64, err error) {
	return p.GetConn().GAT(key, exp)
}
func (p *Pool) Touch(key string, exp uint32) (cas uint64, err error) {
	return p.GetConn().Touch(key, exp)
}
func (p *Pool) Set(key, val string, flags, exp uint32, ocas uint64) (cas uint64, err error) {
	return p.GetConn().Set(key, val, flags, exp, ocas)
}
func (p *Pool) Replace(key, val string, flags, exp uint32, ocas uint64) (cas uint64, err error) {
	return p.GetConn().Replace(key, val, flags, exp, ocas)
}
func (p *Pool) Add(key, val string, flags, exp uint32) (cas uint64, err error) {
	return p.GetConn().Add(key, val, flags, exp)
}
func (p *Pool) Incr(key string, delta, init uint64, exp uint32, ocas uint64) (n, cas uint64, err error) {
	return p.GetConn().Incr(key, delta, init, exp, ocas)
}
func (p *Pool) Decr(key string, delta, init uint64, exp uint32, ocas uint64) (n, cas uint64, err error) {
	return p.GetConn().Decr(key, delta, init, exp, ocas)
}
func (p *Pool) Append(key, val string, ocas uint64) (cas uint64, err error) {
	return p.GetConn().Append(key, val, ocas)
}
func (p *Pool) Prepend(key, val string, ocas uint64) (cas uint64, err error) {
	return p.GetConn().Prepend(key, val, ocas)
}
func (p *Pool) Del(key string) (err error) {
	return p.GetConn().Del(key)
}
func (p *Pool) DelCAS(key string, cas uint64) (err error) {
	return p.GetConn().DelCAS(key, cas)
}
func (p *Pool) Flush(when uint32) (err error) {
	return p.GetConn().Flush(when)
}
func (p *Pool) NoOp() (err error) {
	return p.GetConn().NoOp()
}
func (p *Pool) Version() (ver string, err error) {
	return p.GetConn().Version()
}
func (p *Pool) Quit() (err error) {
	return p.GetConn().Quit()
}
func (p *Pool) Stats() (stats map[string]string, err error) {
	return p.GetConn().Stats()
}
