/*
 * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved.
 *
 * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit).
 *
 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
 */

#include "UdpClient.h"

using namespace std;

namespace toolkit {

StatisticImp(UdpClient)

UdpClient::UdpClient(const EventPoller::Ptr &poller) : SocketHelper(nullptr) {
    setPoller(poller ? poller : EventPollerPool::Instance().getPoller());
    setOnCreateSocket([](const EventPoller::Ptr &poller) {
        //TCP客户端默认开启互斥锁
        return Socket::createSocket(poller, true);
    });
}

UdpClient::~UdpClient() {
    TraceL << "~" << UdpClient::getIdentifier();
}

void UdpClient::startConnect(const string &peer_host, uint16_t peer_port, uint16_t local_port) {
    weak_ptr<UdpClient> weak_self = static_pointer_cast<UdpClient>(shared_from_this());
    _timer = std::make_shared<Timer>(2.0f, [weak_self]() {
        auto strong_self = weak_self.lock();
        if (!strong_self) {
            return false;
        }
        strong_self->onManager();
        return true;
    }, getPoller());

    setSock(createSocket());

    auto sock_ptr = getSock().get();
    sock_ptr->setOnErr([weak_self, sock_ptr](const SockException &ex) {
        auto strong_self = weak_self.lock();
        if (!strong_self) {
            return;
        }
        if (sock_ptr != strong_self->getSock().get()) {
            //已经重连socket，上次的socket的事件忽略掉
            return;
        }
        strong_self->_timer.reset();
        TraceL << strong_self->getIdentifier() << " on err: " << ex;
        strong_self->onError(ex);
    });

    sock_ptr->setOnFlush([weak_self, sock_ptr]() {
        auto strong_self = weak_self.lock();
        if (!strong_self) {
            return false;
        }
        if (sock_ptr != strong_self->getSock().get()) {
            //已经重连socket，上传socket的事件忽略掉
            return false;
        }
        strong_self->onFlush();
        return true;
    });

    sock_ptr->setOnRead([weak_self, sock_ptr](const Buffer::Ptr &pBuf, struct sockaddr * addr, int addr_len) {
        auto strong_self = weak_self.lock();
        if (!strong_self) {
            return;
        }
        if (sock_ptr != strong_self->getSock().get()) {
            //已经重连socket，上传socket的事件忽略掉
            return;
        }
        try {
            strong_self->onRecvFrom(pBuf, addr, addr_len);
        } catch (std::exception &ex) {
            strong_self->shutdown(SockException(Err_other, ex.what()));
        }
    });

    bool ret = getSock()->bindUdpSock(local_port, _net_adapter);
    if (!ret) {
        WarnL << "UDP output bind local error";
    }
    auto peer_addr = SockUtil::make_sockaddr(peer_host.c_str(), peer_port);

    //只能软绑定
    ret = getSock()->bindPeerAddr((struct sockaddr *)&peer_addr, 0, true);
    if (!ret) {
        WarnL << "UDP output bind peer error";
    }
    // TraceL << getIdentifier() << " start connect " << url << ":" << peer_port;
}

void UdpClient::shutdown(const SockException &ex) {
    _timer.reset();
    SocketHelper::shutdown(ex);
}

bool UdpClient::alive() const {
    if (_timer) {
        //连接中或已连接
        return true;
    }
    //在websocket client(zlmediakit)相关代码中，
    //_timer一直为空，但是socket fd有效，alive状态也应该返回true
    auto sock = getSock();
    return sock && sock->alive();
}

void UdpClient::setNetAdapter(const string &local_ip) {
    _net_adapter = local_ip;
}

std::string UdpClient::getIdentifier() const {
    if (_id.empty()) {
        static atomic<uint64_t> s_index { 0 };
        _id = toolkit::demangle(typeid(*this).name()) + "-" + to_string(++s_index);
    }
    return _id;
}

} /* namespace toolkit */
