func NewConn(c io.ReadWriteCloser) *Conn {
	conn := new(Conn)
	conn.conn = textproto.NewConn(c)
	return conn
}
NewConn returns a new Conn using c for I/O.
package bulb
import (
	"errors"
	gofmt "fmt"
	"io"
	"log"
	"net"
	"net/textproto"
	"sync"
)
const (
	maxEventBacklog    = 16
	maxResponseBacklog = 16
)
var ErrNoAsyncReader = errors.New("event requested without an async reader")
type Conn struct {
	conn            *textproto.Conn
	isAuthenticated bool
	debugLog        bool
	cachedPI        *ProtocolInfo
	asyncReaderLock    sync.Mutex
	asyncReaderRunning bool
	eventChan          chan *Response
	respChan           chan *Response
	closeWg            sync.WaitGroup
	rdErrLock sync.Mutex
	rdErr     error
}
func (c *Conn) setRdErr(err error, force bool) {
	c.rdErrLock.Lock()
	defer c.rdErrLock.Unlock()
	if c.rdErr == nil || force {
		c.rdErr = err
	}
}
func (c *Conn) getRdErr() error {
	c.rdErrLock.Lock()
	defer c.rdErrLock.Unlock()
	return c.rdErr
}
func (c *Conn) isAsyncReaderRunning() bool {
	c.asyncReaderLock.Lock()
	defer c.asyncReaderLock.Unlock()
	return c.asyncReaderRunning
}
func (c *Conn) asyncReader() {
	for {
		resp, err := c.ReadResponse()
		if err != nil {
			c.setRdErr(err, false)
			break
		}
		if resp.IsAsync() {
			c.eventChan <- resp
		} else {
			c.respChan <- resp
		}
	}
	close(c.eventChan)
	close(c.respChan)
	c.closeWg.Done()
}
func (c *Conn) Debug(enable bool) {
	c.debugLog = enable
}
func (c *Conn) Close() error {
	c.asyncReaderLock.Lock()
	defer c.asyncReaderLock.Unlock()
	err := c.conn.Close()
	if err != nil && c.asyncReaderRunning {
		c.closeWg.Wait()
	}
	c.setRdErr(io.ErrClosedPipe, true)
	return err
}
func (c *Conn) StartAsyncReader() {
	c.asyncReaderLock.Lock()
	defer c.asyncReaderLock.Unlock()
	if c.asyncReaderRunning {
		return
	}
	c.eventChan = make(chan *Response, maxEventBacklog)
	c.respChan = make(chan *Response, maxResponseBacklog)
	c.closeWg.Add(1)
	go c.asyncReader()
	c.asyncReaderRunning = true
}
func (c *Conn) NextEvent() (*Response, error) {
	if err := c.getRdErr(); err != nil {
		return nil, err
	}
	if !c.isAsyncReaderRunning() {
		return nil, ErrNoAsyncReader
	}
	resp, ok := <-c.eventChan
	if resp != nil {
		return resp, nil
	} else if !ok {
		return nil, io.ErrClosedPipe
	}
	panic("BUG: NextEvent() returned a nil response and error")
}
func (c *Conn) Request(fmt string, args ...interface{}) (*Response, error) {
	if err := c.getRdErr(); err != nil {
		return nil, err
	}
	asyncResp := c.isAsyncReaderRunning()
	if c.debugLog {
		log.Printf("C: %s", gofmt.Sprintf(fmt, args...))
	}
	id, err := c.conn.Cmd(fmt, args...)
	if err != nil {
		return nil, err
	}
	c.conn.StartResponse(id)
	defer c.conn.EndResponse(id)
	var resp *Response
	if asyncResp {
		var ok bool
		resp, ok = <-c.respChan
		if resp == nil && !ok {
			return nil, io.ErrClosedPipe
		}
	} else {
		for resp == nil || resp.IsAsync() {
			resp, err = c.ReadResponse()
			if err != nil {
				return nil, err
			}
		}
	}
	if resp == nil {
		panic("BUG: Request() returned a nil response and error")
	}
	if resp.IsOk() {
		return resp, nil
	}
	return resp, resp.Err
}
func (c *Conn) Read(p []byte) (int, error) {
	return c.conn.R.Read(p)
}
func (c *Conn) Write(p []byte) (int, error) {
	n, err := c.conn.W.Write(p)
	if err == nil {
		return n, c.conn.W.Flush()
	}
	return n, err
}
func Dial(network, addr string) (*Conn, error) {
	c, err := net.Dial(network, addr)
	if err != nil {
		return nil, err
	}
	return NewConn(c), nil
}