package main

import (
	"net"
	"sync"
	"time"

	"./log"

	"go.uber.org/zap"
)

type Acceptor interface {
	Start(address string) error
	Close()
}

func NewAcceptor(log *log.Logger, onAccept func(conn net.Conn)) Acceptor {
	return &tcpAcceptor{
		log:        log,
		onAccept:   onAccept,
		blockIPMap: map[string]int64{},
	}
}

type tcpAcceptor struct {
	listener   net.Listener
	close      chan struct{} // 执行退出
	closeDone  chan struct{} // 用于同步退出完成
	log        *log.Logger   // 日志
	onAccept   func(conn net.Conn)
	isRunning  bool // 是否在运行
	blockIPMap map[string]int64
	sync.Mutex
}

func (acceptor *tcpAcceptor) Start(address string) error {
	acceptor.Lock()
	defer acceptor.Unlock()

	if acceptor.isRunning {
		return nil
	}
	ln, err := listen(address) // 返回 listener
	if err != nil {
		acceptor.log.Error("listen failed", zap.String("addr", address), zap.Error(err))
		return err
	}
	acceptor.close = make(chan struct{})
	acceptor.closeDone = make(chan struct{})
	acceptor.isRunning = true
	acceptor.listener = ln
	go acceptor.accept()
	return nil
}

func (acceptor *tcpAcceptor) Close() {
	select {
	case <-acceptor.close:
		return
	default:
		close(acceptor.close)
		acceptor.listener.Close()
		<-acceptor.closeDone
	}
}

// run in goroutine
func (acceptor *tcpAcceptor) accept() {
	var tempDelay time.Duration
	for {
		conn, e := acceptor.listener.Accept()
		if e != nil {
			select {
			case <-acceptor.close: //a 已停止工作
				goto stopAccept
			default:
			}
			//b 如果没有关闭，则睡眠后继续
			if ne, ok := e.(net.Error); ok && ne.Temporary() {
				if tempDelay == 0 {
					tempDelay = 5 * time.Millisecond // 1 第一次睡眠 5 ms
				} else {
					tempDelay *= 2 // 2 第二次以后翻倍
				}
				// 3 但最多休眠 1s
				if max := 1 * time.Second; tempDelay > max {
					tempDelay = max
				}
				acceptor.log.Error("tcp: Accept error,will retrying", zap.Duration("retrying after", tempDelay),
					zap.String("addr", acceptor.listener.Addr().String()), zap.Error(e))
				time.Sleep(tempDelay)
				continue
			}
			//c 否则退出
			acceptor.log.Error("tcp: Accept error,will stop accept", zap.String("addr", acceptor.listener.Addr().String()), zap.Error(e))
			goto stopAccept
		}
		//tempDelay = 0
		//now := timestamp
		//a, ok := conn.RemoteAddr().(*net.TCPAddr)
		//if !ok {
		//	conn.Close()
		//	continue
		//}
		//ip := a.IP.String()
		//if v, ok := acceptor.blockIPMap[ip]; ok {
		//	if v == now {
		//		conn.Close()
		//		atomic.AddInt32(&acceptBlockCount, 1)
		//		continue
		//	}
		//}
		//acceptor.blockIPMap[ip] = now
		acceptor.onAccept(conn) // 无错误执行本函数
	} // for

stopAccept:
	acceptor.log.Info("tcp: stopped accept", zap.String("addr", acceptor.listener.Addr().String()))
	close(acceptor.closeDone)
	acceptor.isRunning = false
}
