#pragma once
#include <trantor/exports.h>
#include <trantor/net/EventLoopThreadPool.h>
#include <trantor/net/InetAddress.h>
#include <trantor/net/TcpConnection.h>
#include <trantor/net/callbacks.h>
#include <trantor/utils/Logger.h>
#include <trantor/utils/NonCopyable.h>
#include <trantor/utils/TimingWheel.h>
#include <csignal>
#include <memory>
#include <set>
#include <string>
namespace trantor
{
class Acceptor;
class TRANTOR_EXPORT TcpServer : NonCopyable
{
  public:
    TcpServer(EventLoop *loop,
              const InetAddress &address,
              std::string name,
              bool reUseAddr = true,
              bool reUsePort = true);
    ~TcpServer();
    void start();
    void stop();
    void setIoLoopNum(size_t num)
    {
        assert(!started_);
        loopPoolPtr_ = std::make_shared<EventLoopThreadPool>(num);
        loopPoolPtr_->start();
        ioLoops_ = loopPoolPtr_->getLoops();
        numIoLoops_ = ioLoops_.size();
    }
    void setIoLoopThreadPool(const std::shared_ptr<EventLoopThreadPool> &pool)
    {
        assert(pool->size() > 0);
        assert(!started_);
        loopPoolPtr_ = pool;
        loopPoolPtr_->start();  
        ioLoops_ = loopPoolPtr_->getLoops();
        numIoLoops_ = ioLoops_.size();
    }
    void setIoLoops(const std::vector<trantor::EventLoop *> &ioLoops)
    {
        assert(!ioLoops.empty());
        assert(!started_);
        ioLoops_ = ioLoops;
        numIoLoops_ = ioLoops_.size();
        loopPoolPtr_.reset();
    }
    void setRecvMessageCallback(const RecvMessageCallback &cb)
    {
        recvMessageCallback_ = cb;
    }
    void setRecvMessageCallback(RecvMessageCallback &&cb)
    {
        recvMessageCallback_ = std::move(cb);
    }
    void setConnectionCallback(const ConnectionCallback &cb)
    {
        connectionCallback_ = cb;
    }
    void setConnectionCallback(ConnectionCallback &&cb)
    {
        connectionCallback_ = std::move(cb);
    }
    void setWriteCompleteCallback(const WriteCompleteCallback &cb)
    {
        writeCompleteCallback_ = cb;
    }
    void setWriteCompleteCallback(WriteCompleteCallback &&cb)
    {
        writeCompleteCallback_ = std::move(cb);
    }
    void setBeforeListenSockOptCallback(SockOptCallback cb);
    void setAfterAcceptSockOptCallback(SockOptCallback cb);
    const std::string &name() const
    {
        return serverName_;
    }
    std::string ipPort() const;
    const trantor::InetAddress &address() const;
    EventLoop *getLoop() const
    {
        return loop_;
    }
    std::vector<EventLoop *> getIoLoops() const
    {
        return ioLoops_;
    }
    void kickoffIdleConnections(size_t timeout)
    {
        loop_->runInLoop([this, timeout]() {
            assert(!started_);
            idleTimeout_ = timeout;
        });
    }
    [[deprecated("Use enableSSL(TLSPolicyPtr) instead")]] void enableSSL(
        const std::string &certPath,
        const std::string &keyPath,
        bool useOldTLS = false,
        const std::vector<std::pair<std::string, std::string>> &sslConfCmds =
            {},
        const std::string &caPath = "");
    void enableSSL(TLSPolicyPtr policy)
    {
        policyPtr_ = std::move(policy);
        sslContextPtr_ = newSSLContext(*policyPtr_, true);
    }
    void reloadSSL();
  private:
    void handleCloseInLoop(const TcpConnectionPtr &connectionPtr);
    void newConnection(int fd, const InetAddress &peer);
    void connectionClosed(const TcpConnectionPtr &connectionPtr);
    EventLoop *loop_;
    std::unique_ptr<Acceptor> acceptorPtr_;
    std::string serverName_;
    std::set<TcpConnectionPtr> connSet_;
    RecvMessageCallback recvMessageCallback_;
    ConnectionCallback connectionCallback_;
    WriteCompleteCallback writeCompleteCallback_;
    size_t idleTimeout_{0};
    std::map<EventLoop *, std::shared_ptr<TimingWheel>> timingWheelMap_;
    std::shared_ptr<EventLoopThreadPool> loopPoolPtr_;
    std::vector<EventLoop *> ioLoops_;
    size_t nextLoopIdx_{0};
    size_t numIoLoops_{0};
#ifndef _WIN32
    class IgnoreSigPipe
    {
      public:
        IgnoreSigPipe()
        {
            ::signal(SIGPIPE, SIG_IGN);
            LOG_TRACE << "Ignore SIGPIPE";
        }
    };
    IgnoreSigPipe initObj;
#endif
    bool started_{false};
    TLSPolicyPtr policyPtr_{nullptr};
    SSLContextPtr sslContextPtr_{nullptr};
};
}  