#include "vendor_base.h"
#include "vendor_global.h"
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <string.h>
#include <utils/logger.h>

namespace vendor {

LongOpts::LongOpts(const MyOpt opts[], const int size)
{
    myOpt = opts;
    sizeOpt = size;
}

void LongOpts::GetOptions(option *opts, std::string &shorts)
{
    for (int i = 0; i < sizeOpt; i++) {
        // printf("val: %d(%c) ", myOpt[i].val, myOpt[i].val);
        opts[i].name = strdup(myOpt[i].name);
        opts[i].has_arg = myOpt[i].has_arg;
        opts[i].flag = NULL;
        opts[i].val = myOpt[i].val;
        if (myOpt[i].val == 0) {
            continue;
        }
        if (myOpt[i].has_arg == no_argument) {
            shorts.push_back(static_cast<char>(myOpt[i].val));
        } else if (myOpt[i].has_arg == required_argument) {
            shorts.push_back(static_cast<char>(myOpt[i].val));
            shorts.push_back(':');
        } else {
            shorts.push_back(static_cast<char>(myOpt[i].val));
        }
    }
}

MyOpt LongOpts::GetOption(char ch, int index)
{
    if (ch == 0) {
        return myOpt[index];
    }

    for (int i = 0; i < sizeOpt; i++) {
        if (myOpt[i].val == ch) {
            return myOpt[i];
        }
    }
    return myOpt[0];
}

VendorBase::VendorBase()
{
    HTELINK_LOG_ENABLE(true);
    uvLoop_ = uv_default_loop();
    if (uvLoop_ == nullptr) {
        HTELINK_LOG_ERR("event new failed, %s", strerror(errno));
    }
}

VendorBase::~VendorBase()
{
    if (uvLoop_ == nullptr) {
        return;
    }

    for (auto sig : uvSignals_) {
        uv_close(reinterpret_cast<uv_handle_t *>(sig), nullptr);
        delete sig;
    }
    uvSignals_.clear();
    uv_loop_close(uvLoop_);
    Exit(SIGINT);
}

void VendorBase::ListenSignal(int signal)
{
    uv_signal_t *uvSigal = new uv_signal_t;
    uv_signal_init(uvLoop_, uvSigal);
    uvSigal->data = this;
    uv_signal_start(uvSigal, VendorBase::SignalHandler, signal);
    uvSignals_.push_back(reinterpret_cast<uv_handle_t *>(uvSigal));
}

int VendorBase::Exec()
{
    HTELINK_LOG_INFO("register event...");
    // 注册信号事件
    ListenSignal(SIGSEGV);
    ListenSignal(SIGFPE);
    ListenSignal(SIGINT);
    ListenSignal(SIGTERM);
#ifndef WIN32
    ListenSignal(SIGTSTP);
    signal(SIGPIPE, SIG_IGN);
#endif

    AppendTimerEvent(10 * 60 * 1000, [] {
        /* move files if disk full */
        utils::check_disk_log(VENDOR_LOG_PATH);
    });

    uv_work_t workReq;
    workReq.data = this;
    uv_queue_work(
        uvLoop_, &workReq,
        [](uv_work_t *req) {
            VendorBase *vb = reinterpret_cast<VendorBase *>(req->data);
            HTELINK_LOG_INFO("start to Run");
            vb->Run();
        },
        [](uv_work_t *req, int status) {});
    HTELINK_LOG_INFO("start to deamon...");
    return uv_run(uvLoop_, UV_RUN_DEFAULT);
}

void VendorBase::Exit(int signal)
{
    Stop(signal);
    if (uvLoop_ == nullptr) {
        return;
    }
    uv_stop(uvLoop_);
}

uv_handle_t *VendorBase::AppendEvent(int fd, int ev, VendorIf *vif)
{
    uv_async_t *async = new uv_async_t;
    uv_async_cb asyncCb = [](uv_async_t *handle) {
        VendorIf *vif = static_cast<VendorIf *>(handle->data);
        if (vif) {
            vif->OnEvent(nullptr);
        }
    };
    uv_async_init(uvLoop_, async, asyncCb);
    async->data = vif;
    uv_async_send(async);
    uvSignals_.push_back(reinterpret_cast<uv_handle_t *>(async));
    return (uv_handle_t *)async;
}

uv_handle_t *VendorBase::AppendTimerEvent(int millionsecs, VendorIf *vif)
{
    uv_timer_t *timerReq = new uv_timer_t;
    uv_timer_init(uvLoop_, timerReq);
    timerReq->data = vif;
    uv_timer_cb timerCb = [](uv_timer_t *handle) {
        VendorIf *vif = static_cast<VendorIf *>(handle->data);
        if (vif) {
            vif->OnTimer(nullptr);
        }
    };
    uv_timer_start(timerReq, timerCb, millionsecs, 1);
    uvSignals_.push_back(reinterpret_cast<uv_handle_t *>(timerReq));
    return (uv_handle_t *)(timerReq);
}

uv_handle_t *VendorBase::AppendTimerEvent(
    int millionsecs, std::function<void()> timerFunc)
{
    class VendorTimerIf : public VendorIf {
    public:
        VendorTimerIf(std::function<void()> timerFunc)
        {
            timerFunc_ = timerFunc;
        }
        virtual void OnEvent(void *args) {}
        virtual void OnTimer(void *args) { timerFunc_(); }
        virtual void OnShot(void *args) {}

    private:
        std::function<void()> timerFunc_;
    };
    // 存在内存泄露问题，待更好的方案
    return AppendTimerEvent(millionsecs, new VendorTimerIf(timerFunc));
}

void VendorBase::StopEvent(uv_handle_t *handle)
{
    if (handle == nullptr) {
        return;
    }
    if (handle->type == UV_TIMER) {
        uv_timer_stop((uv_timer_t *)handle);
    } else {
        uv_close(handle, nullptr);
    }
    delete handle;
}

void VendorBase::SignalHandler(uv_signal_t *handle, int signum)
{
    void *l_buffer[1024];
    char **l_ptrace;

    int signal = signum;

    VendorBase *vendorBase = static_cast<VendorBase *>(handle->data);
    if (vendorBase) {
        vendorBase->Exit(signal);
    }

    HTELINK_LOG_ERR("\r\n=========>>>catch signal %d, pid: %ld <<<=========", signal, getpid());
    HTELINK_LOG_ERR("Dump stack start...");

#ifndef WIN32
    int size = backtrace(l_buffer, 1024);
    l_ptrace = backtrace_symbols(l_buffer, size);
    if (NULL == l_ptrace) {
        HTELINK_LOG_ERR("backtrace_symbols");
        exit(signal);
    }

    for (int i = 0; i < size; i++) {
        HTELINK_LOG_ERR("  [%02d] %s", i, l_ptrace[i]);
    }
    HTELINK_LOG_ERR("Dump stack end...");
    free(l_ptrace);
#endif
    abort();
}

int32_t VendorBase::ParseOptionArgs(VendorBase *vendor, int argc, char *argv[])
{
    int longindex = 0;
    std::string shorts;
    struct option longopts[vendor->GetOptSize()];
    LongOpts longOpts(vendor->GetOpts(), vendor->GetOptSize());
    longOpts.GetOptions(longopts, shorts);
    int ch;
    while ((ch = getopt_long(argc, argv, shorts.c_str(), longopts, &longindex)) != -1) {
        MyOpt myOpt = longOpts.GetOption(ch, longindex);
        printf("longindex :%d, opt %d(%c), name:%s , optarg :%s\n", longindex, myOpt.val, myOpt.val, myOpt.name,
            optarg == nullptr ? "nullptr" : optarg);
        if (strcmp(myOpt.name, "help") == 0) {
            MyOpt *myOpts = vendor->GetOpts();
            printf("Usage: %s [OPTION] -l URL\n\nOPTIONS:\n", vendor->Name().c_str());
            for (int i = 1; i < vendor->GetOptSize(); i++) {
                if (myOpts[i].val == 0) {
                    printf("--%s:  %s\n", myOpts[i].name, myOpts[i].desc);
                } else {
                    printf("-%c, --%s:  %s\n", myOpts[i].val, myOpts[i].name, myOpts[i].desc);
                }
            }
            return -1;
        }

        if (vendor->ParseCmdline(myOpt.name, optarg == nullptr ? "" : optarg) != 0) {
            printf("parse failed!!!\n");
            return -1;
        }
    }
    return 0;
}
}; // namespace vendor
