#include "tcp_server.hpp"
#include "coroutine.hpp"
#include "fd_event.hpp"
#include "logger.hpp"
#include "coroutine_hook.hpp"
#include "net_address.hpp"
#include "protocol_type.hpp"
#include "reactor.hpp"
#include "tinypb_coder.hpp"
#include "tinypb_dispatcher.hpp"
#include <cerrno>
#include <cstring>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>


namespace tinyrpc {
    TcpAcceptor::TcpAcceptor(const NetAddress& bindNetAddr) : m_bindNetAddr(bindNetAddr) {

    }
    
    void TcpAcceptor::init() {

        m_listenfd = socket(AF_INET, SOCK_STREAM,0);
        // FdEvent(m_listenfd).setNonblock();
        FdEventPool::getInstance()->getFdEvent(m_listenfd)->setNonblock();
        if(m_listenfd == -1) {
            logger() << "socket ret -1 err:" << strerror(errno);
            exit(-1);
        }
        int val = 1;
        if (setsockopt(m_listenfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) {
            logger() << "setsockopt ret < 0 err:" << strerror(errno);;
            exit(-1);
        }

        int ret = bind(m_listenfd, m_bindNetAddr.getSockaddr(), m_bindNetAddr.getSockLen());

        if(ret == -1) {
            logger() << "bind ret -1 err:" << strerror(errno);
            exit(-1);
        }

        if(listen(m_listenfd, 5) == -1) {
            logger() << "listen ret -1 err:" << strerror(errno);
            exit(-1);
        }

    }
    int TcpAcceptor::accept() {
        
        sockaddr_in addr{};
        socklen_t len = sizeof(addr);
        int ret = accept_hook(m_listenfd, reinterpret_cast<sockaddr*>(&addr), &len);
        if(ret == -1) {
            logger() << "accept_hook ret -1 err:" << strerror(errno);
        }
        logger() << "accept: " << NetAddress(&addr).toString();
        return ret;
    }



    TcpServer::TcpServer(const NetAddress& addr) : 
        m_accept_cor(std::bind(&TcpServer::mainAcceptCorFun, this)),
        m_acceptor(addr),
        m_coder(new TinypbCoder),
        m_dispatcher(new TinypbDispatcher)
    {
        m_acceptor.init();
    }

    TcpServer::TcpServer() : 
        TcpServer(NetAddress("127.0.0.1", 9001)) {}

    TcpServer::TcpServer(const std::string& ip, uint16_t port) : 
        TcpServer(NetAddress(ip, port)) {}

    TcpServer::~TcpServer() {
        delete m_coder;
        delete m_dispatcher;
    }

    void TcpServer::start() {
        
        m_accept_cor.resume();
        Reactor* mainReactor = Reactor::getReactor();
        mainReactor->loop();
    }

    void TcpServer::mainAcceptCorFun() {
        while(!m_stop_accept) {
            logger();
            int fd = m_acceptor.accept();
            if(fd == -1) {
                logger() << "m_acceptor.accept() ret -1 yeild this coroutine";
                Coroutine::yeild();
            }
            logger() << "new connect fd = " << fd;
            
            logger() << " 添加 fd 到子 reactor 中 " << fd;
            m_ioThreadPool.getIOThread()->addClient(this, fd);
        }
        
    }

    void TcpServer::registerService(std::shared_ptr<google::protobuf::Service> service) {
        if(m_protocolType != ProtocolType::Tinypb) return;
        if(service) {
            dynamic_cast<TinypbDispatcher*>(m_dispatcher)->registerService(service);
        }

    }
}