#include "zoost/zt.os_win.h"

#include <windows.h>

std::string zoostWinService::serviceName_ = "";
std::string zoostWinService::dispName_ = "";
std::string zoostWinService::exePath_ = "";
std::function<void()> zoostWinService::exeFunc_ = nullptr;
std::function<void()> zoostWinService::stopFunc_ = nullptr;
unsigned int zoostWinService::timeoutVal = 5000;
static SERVICE_STATUS serviceStatus;
static SERVICE_STATUS_HANDLE serviceStatusHandle;
static HANDLE serviceStopEvent;
static DWORD errorCode;

VOID WINAPI ServiceMain(DWORD argc, LPTSTR* argv);
SERVICE_TABLE_ENTRYA serviceTable[] = {{(LPSTR)zoostWinService::serviceName_.c_str(), (LPSERVICE_MAIN_FUNCTIONA)ServiceMain},
                                       {NULL, NULL}};

VOID WINAPI ServiceCtrlHandler(DWORD controlCode)
{
    switch (controlCode) {
    case SERVICE_CONTROL_STOP:
        serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
        SetServiceStatus(serviceStatusHandle, &serviceStatus);
        SetEvent(serviceStopEvent);
        return;

    case SERVICE_CONTROL_PAUSE:
        serviceStatus.dwCurrentState = SERVICE_PAUSE_PENDING;
        SetServiceStatus(serviceStatusHandle, &serviceStatus);
        serviceStatus.dwCurrentState = SERVICE_PAUSED;
        break;

    case SERVICE_CONTROL_CONTINUE:
        serviceStatus.dwCurrentState = SERVICE_CONTINUE_PENDING;
        SetServiceStatus(serviceStatusHandle, &serviceStatus);
        serviceStatus.dwCurrentState = SERVICE_RUNNING;
        break;

    case SERVICE_CONTROL_SHUTDOWN:
        serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
        SetServiceStatus(serviceStatusHandle, &serviceStatus);
        SetEvent(serviceStopEvent);
        return;

    case SERVICE_CONTROL_INTERROGATE:
        break;

    default:
        break;
    }

    SetServiceStatus(serviceStatusHandle, &serviceStatus);
}

void zoostWinService::ExportEventMsg(const std::string& msg)
{
    HANDLE hEventSource = RegisterEventSource(NULL, zoostWinService::serviceName_.c_str());
    if (hEventSource) {
        const char* messages[] = {msg.c_str()};
        ReportEvent(hEventSource, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, messages, NULL);
        DeregisterEventSource(hEventSource);
    }
}

// 服务主函数
VOID WINAPI ServiceMain(DWORD argc, LPTSTR* argv)
{
    serviceStatusHandle = RegisterServiceCtrlHandler(zoostWinService::serviceName_.c_str(), ServiceCtrlHandler);

    if (!serviceStatusHandle) {
        return;
    }

    // 初始化服务状态
    ZeroMemory(&serviceStatus, sizeof(serviceStatus));
    serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    serviceStatus.dwCurrentState = SERVICE_START_PENDING;
    serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
    serviceStatus.dwWin32ExitCode = NO_ERROR;
    serviceStatus.dwServiceSpecificExitCode = 0;
    serviceStatus.dwCheckPoint = 0;
    serviceStatus.dwWaitHint = 0;

    SetServiceStatus(serviceStatusHandle, &serviceStatus);

    serviceStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!serviceStopEvent) {
        serviceStatus.dwCurrentState = SERVICE_STOPPED;
        serviceStatus.dwWin32ExitCode = GetLastError();
        SetServiceStatus(serviceStatusHandle, &serviceStatus);
        return;
    }

    serviceStatus.dwCurrentState = SERVICE_RUNNING;
    SetServiceStatus(serviceStatusHandle, &serviceStatus);

    while (true) {
        DWORD waitResult = WaitForSingleObject(serviceStopEvent, 5000);
        if (waitResult == WAIT_OBJECT_0) {
            break;
        } else if (waitResult == WAIT_TIMEOUT) {
            zoostWinService::exeFunc_();
        }
    }

    CloseHandle(serviceStopEvent);
    serviceStatus.dwCurrentState = SERVICE_STOPPED;
    SetServiceStatus(serviceStatusHandle, &serviceStatus);
}

std::string zoostWinService::GetLastErrorMsg()
{
    LPSTR errorMessage = nullptr;
    DWORD result = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                                 NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&errorMessage, 0, NULL);
    if (result == 0) {
        return "Failed to get error msg.";
    }
    std::string msg(errorMessage);
    LocalFree(errorMessage);
    return msg;
}

bool zoostWinService::WinInstallService()
{
    auto scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (!scm) {
        errorCode = GetLastError();
        return false;
    }
    auto service = CreateService(scm, zoostWinService::serviceName_.c_str(), zoostWinService::dispName_.c_str(),
                                 SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
                                 zoostWinService::exePath_.c_str(), NULL, NULL, NULL, NULL, NULL);

    if (!service) {
        errorCode = GetLastError();
        CloseServiceHandle(scm);
        return false;
    }

    CloseServiceHandle(service);
    CloseServiceHandle(scm);

    return true;
}

bool zoostWinService::WinUninstallService()
{
    auto scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (!scm) {
        errorCode = GetLastError();
        return false;
    }

    auto service = OpenService(scm, zoostWinService::serviceName_.c_str(), SERVICE_ALL_ACCESS);
    if (!service) {
        errorCode = GetLastError();
        CloseServiceHandle(scm);
        return false;
    }

    SERVICE_STATUS status;
    ControlService(service, SERVICE_CONTROL_STOP, &status);

    Sleep(2000);

    bool result = DeleteService(service) != 0;

    if (result) {
        return true;
    } else {
        errorCode = GetLastError();
    }

    CloseServiceHandle(service);
    CloseServiceHandle(scm);
    return false;
}

bool zoostWinService::WinStartService()
{
    auto scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (!scm) {
        errorCode = GetLastError();
        return false;
    }

    auto service = OpenService(scm, zoostWinService::serviceName_.c_str(), SERVICE_ALL_ACCESS);
    if (!service) {
        errorCode = GetLastError();
        CloseServiceHandle(scm);
        return false;
    }

    bool result = StartService(service, 0, NULL) != 0;
    if (result) {
        return true;
    } else {
        errorCode = GetLastError();
    }
    CloseServiceHandle(service);
    CloseServiceHandle(scm);
    return false;
}

bool zoostWinService::WinStopService()
{
    auto scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (!scm) {
        errorCode = GetLastError();
        return false;
    }
    auto service = OpenService(scm, zoostWinService::serviceName_.c_str(), SERVICE_ALL_ACCESS);
    if (!service) {
        errorCode = GetLastError();
        CloseServiceHandle(scm);
        return false;
    }
    SERVICE_STATUS status;
    bool result = ControlService(service, SERVICE_CONTROL_STOP, &status) != 0;
    if (result) {
        return true;
    } else {
        errorCode = GetLastError();
    }
    CloseServiceHandle(service);
    CloseServiceHandle(scm);
    return result;
}

void zoostWinService::WinServiceRun()
{
    if (!StartServiceCtrlDispatcher(serviceTable)) {
        errorCode = GetLastError();
        ExportEventMsg(GetLastErrorMsg());
    }
}

std::string zoostWinUtil::U82Ansi(const std::string& str)
{
    int wideCharLen = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0);
    if (wideCharLen <= 0) {
        return "";
    }
    std::wstring wideStr(wideCharLen, L'\0');
    MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &wideStr[0], wideCharLen);
    int gbkLen = WideCharToMultiByte(CP_ACP, 0, wideStr.c_str(), -1, nullptr, 0, nullptr, nullptr);
    if (gbkLen <= 0) {
        return "";
    }
    std::string ansiStr(gbkLen, '\0');
    WideCharToMultiByte(CP_ACP, 0, wideStr.c_str(), -1, &ansiStr[0], gbkLen, nullptr, nullptr);

    ansiStr.resize(gbkLen - 1);
    return ansiStr;
}

std::string zoostWinUtil::Ansi2U8(const std::string& str)
{
    int wideCharLen = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, nullptr, 0);
    if (wideCharLen <= 0) {
        return "";
    }
    std::wstring wideStr(wideCharLen, L'\0');
    MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, &wideStr[0], wideCharLen);

    int utf8Len = WideCharToMultiByte(CP_UTF8, 0, wideStr.c_str(), -1, nullptr, 0, nullptr, nullptr);
    if (utf8Len <= 0) {
        return "";
    }
    std::string utf8Str(utf8Len, '\0');
    WideCharToMultiByte(CP_UTF8, 0, wideStr.c_str(), -1, &utf8Str[0], utf8Len, nullptr, nullptr);

    utf8Str.resize(utf8Len - 1);
    return utf8Str;
}