func (c *Client) scanErr() error {
	if err := c.scanner.Err(); err != nil {
		return err
	}
	return io.ErrUnexpectedEOF
}
scanError returns the error from the scanner if non-nil,
`io.ErrUnexpectedEOF` otherwise.
package ts3
import (
	"bufio"
	"fmt"
	"io"
	"net"
	"regexp"
	"strings"
	"time"
)
const (
	DefaultPort = 10011
	MaxParseTokenSize = 10 << 20
	DefaultConnectHeader = "TS3"
	startBufSize = 4096
	keepAliveData = " \n"
)
var (
	respTrailerRe = regexp.MustCompile(`^error id=(\d+) msg=([^ ]+)(.*)`)
	DefaultTimeout = 10 * time.Second
	DefaultKeepAlive = 200 * time.Second
	DefaultNotifyBufSize = 5
)
type Client struct {
	conn          net.Conn
	timeout       time.Duration
	keepAlive     time.Duration
	scanner       *bufio.Scanner
	buf           []byte
	maxBufSize    int
	notifyBufSize int
	work          chan string
	err           chan error
	notify        chan Notification
	disconnect    chan struct{}
	res           []string
	connectHeader string
	Server *ServerMethods
}
func Timeout(timeout time.Duration) func(*Client) error {
	return func(c *Client) error {
		c.timeout = timeout
		return nil
	}
}
func KeepAlive(keepAlive time.Duration) func(*Client) error {
	return func(c *Client) error {
		c.keepAlive = keepAlive
		return nil
	}
}
func NotificationBuffer(size int) func(*Client) error {
	return func(c *Client) error {
		c.notifyBufSize = size
		return nil
	}
}
func Buffer(buf []byte, max int) func(*Client) error {
	return func(c *Client) error {
		c.buf = buf
		c.maxBufSize = max
		return nil
	}
}
func ConnectHeader(connectHeader string) func(*Client) error {
	return func(c *Client) error {
		c.connectHeader = connectHeader
		return nil
	}
}
func NewClient(addr string, options ...func(c *Client) error) (*Client, error) {
	if !strings.Contains(addr, ":") {
		addr = fmt.Sprintf("%v:%v", addr, DefaultPort)
	}
	c := &Client{
		timeout:       DefaultTimeout,
		keepAlive:     DefaultKeepAlive,
		buf:           make([]byte, startBufSize),
		maxBufSize:    MaxParseTokenSize,
		notifyBufSize: DefaultNotifyBufSize,
		work:          make(chan string),
		err:           make(chan error),
		disconnect:    make(chan struct{}),
		connectHeader: DefaultConnectHeader,
	}
	for _, f := range options {
		if f == nil {
			return nil, ErrNilOption
		}
		if err := f(c); err != nil {
			return nil, err
		}
	}
	c.notify = make(chan Notification, c.notifyBufSize)
	c.Server = &ServerMethods{Client: c}
	var err error
	if c.conn, err = net.DialTimeout("tcp", addr, c.timeout); err != nil {
		return nil, err
	}
	c.scanner = bufio.NewScanner(bufio.NewReader(c.conn))
	c.scanner.Buffer(c.buf, c.maxBufSize)
	c.scanner.Split(ScanLines)
	if err := c.conn.SetDeadline(time.Now().Add(c.timeout)); err != nil {
		return nil, err
	}
	if !c.scanner.Scan() {
		return nil, c.scanErr()
	}
	if l := c.scanner.Text(); l != c.connectHeader {
		return nil, fmt.Errorf("invalid connection header %q", l)
	}
	if !c.scanner.Scan() {
		return nil, c.scanErr()
	}
	if err := c.conn.SetReadDeadline(time.Time{}); err != nil {
		return nil, err
	}
	go c.messageHandler()
	go c.workHandler()
	return c, nil
}
func (c *Client) messageHandler() {
	for {
		if c.scanner.Scan() {
			line := c.scanner.Text()
			if line == "error id=0 msg=ok" {
				c.err <- nil
			} else if matches := respTrailerRe.FindStringSubmatch(line); len(matches) == 4 {
				c.err <- NewError(matches)
			} else if strings.Index(line, "notify") == 0 {
				if n, err := decodeNotification(line); err == nil {
					select {
					case c.notify <- n:
					default:
					}
				}
			} else {
				c.res = append(c.res, line)
			}
		} else {
			err := c.scanErr()
			c.err <- err
			if err == io.ErrUnexpectedEOF {
				close(c.disconnect)
				return
			}
		}
	}
}
func (c *Client) workHandler() {
	for {
		select {
		case w := <-c.work:
			c.process(w)
		case <-time.After(c.keepAlive):
			c.process(keepAliveData)
		case <-c.disconnect:
			return
		}
	}
}
func (c *Client) process(data string) {
	if err := c.conn.SetWriteDeadline(time.Now().Add(c.timeout)); err != nil {
		c.err <- err
	}
	if _, err := c.conn.Write([]byte(data)); err != nil {
		c.err <- err
	}
}
func (c *Client) Exec(cmd string) ([]string, error) {
	return c.ExecCmd(NewCmd(cmd))
}
func (c *Client) ExecCmd(cmd *Cmd) ([]string, error) {
	if !c.IsConnected() {
		return nil, ErrNotConnected
	}
	c.work <- cmd.String()
	select {
	case err := <-c.err:
		if err != nil {
			return nil, err
		}
	case <-time.After(c.timeout):
		return nil, ErrTimeout
	}
	res := c.res
	c.res = nil
	if cmd.response != nil {
		if err := DecodeResponse(res, cmd.response); err != nil {
			return nil, err
		}
	}
	return res, nil
}
func (c *Client) IsConnected() bool {
	select {
	case <-c.disconnect:
		return false
	default:
		return true
	}
}
func (c *Client) Close() error {
	defer close(c.notify)
	_, err := c.Exec("quit")
	err2 := c.conn.Close()
	if err != nil {
		return err
	}
	return err2
}