#include <computer.h>
#include <IComputer.h>
#include <Visitor.h>
#include "include.h"
#include <iostream>

//回调函数，在C#版本的DLL中，我们需要实现IVisitor接口，并将其引用传给Computer。
//由于没有办法在C++里实现C#接口，考虑到IVisitor接口只包含函数，因此我提供了IVisitor的默认实现，
//其实现为调用以下四个回调函数，它们的指针会被转换为C#委托供IVisitor的C#实现调用。
//注意！！哪怕提供空实现也要提供，要不然就不保证不出问题了。
void VisitComputerCallBack(IComputer& computer, Visitor* visitor)
{
    computer.Traverse(visitor);
}
void VisitHardwareCallBack(Hardware& hardware, Visitor* visitor)
{
    hardware.Update();
    std::vector<Hardware> subHardwares = hardware.getSubHardware();
    for (int i = 0; i < subHardwares.size(); i++) {
        subHardwares[i].Accept(visitor);
    }
}
void VisitSensorCallBack(Sensor& sensor, Visitor* visitor)
{
}
void VisitParameterCallBack(Parameter& parameter, Visitor* visitor)
{
}
void destroy(const Napi::CallbackInfo& info){
    computer->Close();
    computer=nullptr;
    CoUninitialize();

}

// 使用智能指针管理资源
std::shared_ptr<Computer> computer;
std::shared_ptr<Visitor> visitor;

// 封装 COM 初始化/卸载
struct ComInitializer {
  HRESULT hr;
  ComInitializer() {
    hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
  }
  ~ComInitializer() {
    if (SUCCEEDED(hr)) {
      CoUninitialize();
    }
  }
};
void log(std::string log) {
  std::ofstream logfile;
  logfile.open("native_module.log", std::ios::app);
  logfile << log << std::endl;
  logfile.close();
}

// 安全的初始化函数
Napi::Object init(Napi::Env env, Napi::Object exports) {
   log("Com initing");

  ComInitializer comInit; // RAII 管理 COM 生命周期

  // 检查 COM 初始化结果
  if (FAILED(comInit.hr)) {
   log("Com init fail");

    _com_error err(comInit.hr);
    Napi::Error::New(env, err.ErrorMessage()).ThrowAsJavaScriptException();
    return exports;
  }
  log("Com inited");
    computer = std::make_shared<Computer>();
    computer->setCpuEnabled(true);
    computer->setGpuEnabled(true);
    computer.setGpuEnabled(true);
    computer.setMotherboardEnabled(true);
    computer.setStorageEnabled(true);
    computer.setBatteryEnabled(true);
    computer.setControllerEnabled(true);
    computer.setMemoryEnabled(true);
    computer.setNetworkEnabled(true);
    computer.setPsuEnabled(true);

    computer->Open();
  log("Computer inited");

    visitor = std::make_shared<Visitor>();
    visitor->setVisitComputerCallBack(VisitComputerCallBack);
    visitor->setVisitHardwareCallBack(VisitHardwareCallBack);
    visitor->setVisitParameterCallBack(VisitParameterCallBack);
    visitor->setVisitSensorCallBack(VisitSensorCallBack);
    visitor->commit();

    computer->Accept(visitor.get()); // 传递原始指针
  log("Visitor inited");

    // 导出函数
    exports.Set(Napi::String::New(env,"cpu_name"), Napi::Function::New(env, cpu_name));
    exports.Set(Napi::String::New(env,"cpu_load"), Napi::Function::New(env, cpu_load));
    exports.Set(Napi::String::New(env,"cpu_power"), Napi::Function::New(env, cpu_power));
    exports.Set(Napi::String::New(env,"cpu_speed"), Napi::Function::New(env, cpu_speed));
    exports.Set(Napi::String::New(env,"cpu_temperature"), Napi::Function::New(env, cpu_temperature));
    exports.Set(Napi::String::New(env,"cpu_voltage"), Napi::Function::New(env, cpu_voltage));

    exports.Set(Napi::String::New(env,"current_gpu_status"), Napi::Function::New(env, current_gpu_status));
    exports.Set(Napi::String::New(env,"mem_clock"), Napi::Function::New(env, mem_clock));
    exports.Set(Napi::String::New(env,"mem_free_size"), Napi::Function::New(env, mem_free_size));
    exports.Set(Napi::String::New(env,"mem_used_size"), Napi::Function::New(env, mem_used_size));
    exports.Set(Napi::String::New(env,"mem_name"), Napi::Function::New(env, mem_name));
    exports.Set(Napi::String::New(env,"mem_size"), Napi::Function::New(env, mem_size));
    exports.Set(Napi::String::New(env,"vmem_free_size"), Napi::Function::New(env, vmem_free_size));
    exports.Set(Napi::String::New(env,"vmem_used_size"), Napi::Function::New(env, vmem_used_size));
    exports.Set(Napi::String::New(env,"current_disk_used"), Napi::Function::New(env, current_disk_used));
    exports.Set(Napi::String::New(env,"disk_name"), Napi::Function::New(env, disk_name));
    exports.Set(Napi::String::New(env,"monitor_info"), Napi::Function::New(env, monitor_info));
    exports.Set(Napi::String::New(env,"destroy"), Napi::Function::New(env, destroy));

  log("Export inited");

  return exports;
}

NODE_API_MODULE(hardware_monitor, init)
