#include "datexportapi.h"

#include <EasyLogging++/easylogging++.h>

void ReportWinAPIError(std::string error_msg) {
  int error_code = GetLastError();
  LOG(ERROR) << error_msg << "Error code: " << error_code;
  
  error_msg = error_msg + 
              " Error code: "  + 
              std::to_string(error_code) +
              "\n\n"
              "Please, contact #support at LotRO Legacy Discord Channel (https://discord.gg/j25MdKR)\n"
              "or write an e-mail to developer (me@endevir.ru) with this error description!";

  MessageBox(
      NULL,
      error_msg.c_str(),
      "Endevir's LotroDat library error!",
      MB_ICONERROR | MB_OK
  );

  exit(1);
}

DatExportApi::DatExportApi() {
  datexport_dll_ = LoadLibraryA("datexport");
  if (datexport_dll_ == NULL) {
    ReportWinAPIError("Error while opening runtime library!");
  }

  /* get pointer to the function in the dll*/ 
  open_dat_file_addr_ = GetProcAddress(datexport_dll_, "OpenDatFileEx2"); 
  if (open_dat_file_addr_ == NULL) {
    ReportWinAPIError("Error while parsing runtime library funciton 0x1!");
  }
  open_dat_file_func_ = DatExportApiPrivate::OpenDatFileFunc(open_dat_file_addr_); 
  
  get_num_subfiles_addr_ = GetProcAddress(datexport_dll_, "GetNumSubfiles"); 
  if (get_num_subfiles_addr_ == NULL) {
    ReportWinAPIError("Error while parsing runtime library funciton 0x2!");
  }
  get_num_subfiles_func_ = DatExportApiPrivate::GetNumSubfilesFunc(get_num_subfiles_addr_); 
  
  get_subfile_sizes_addr_ = GetProcAddress(datexport_dll_, "GetSubfileSizes"); 
  if (get_subfile_sizes_addr_ == NULL) {
    ReportWinAPIError("Error while parsing runtime library funciton 0x3!");
  }
  get_subfile_sizes_func_ = DatExportApiPrivate::GetSubFileSizesFunc(get_subfile_sizes_addr_); 
  
  get_subfile_version_addr_ = GetProcAddress(datexport_dll_, "GetSubfileVersion"); 
    if (get_subfile_version_addr_ == NULL) {
    ReportWinAPIError("Error while parsing runtime library funciton 0x4!");
  }
  get_subfile_version_func_ = DatExportApiPrivate::GetSubFileVersionFunc(get_subfile_version_addr_); 
  
  get_subfile_data_addr_ = GetProcAddress(datexport_dll_, "GetSubfileData"); 
  if (get_subfile_data_addr_ == NULL) {
    ReportWinAPIError("Error while parsing runtime library funciton 0x5!");
  }
  get_subfile_data_func_ = DatExportApiPrivate::GetSubfileDataFunc(get_subfile_data_addr_); 
  
  close_dat_file_addr_ = GetProcAddress(datexport_dll_, "CloseDatFile"); 
  if (close_dat_file_addr_ == NULL) {
    ReportWinAPIError("Error while parsing runtime library funciton 0x6!");
  }
  close_dat_file_func_ = DatExportApiPrivate::CloseDatFileFunc(close_dat_file_addr_); 
  
  purge_subfile_data_addr_ = GetProcAddress(datexport_dll_, "PurgeSubfileData"); 
  if (purge_subfile_data_addr_ == NULL) {
    ReportWinAPIError("Error while parsing runtime library funciton 0x7!");
  }
  purge_subfile_data_func_ = DatExportApiPrivate::PurgeSubFileDataFunc(purge_subfile_data_addr_);

  put_subfile_data_addr_ = GetProcAddress(datexport_dll_, "PutSubfileData"); 
  if (open_dat_file_addr_ == NULL) {
    ReportWinAPIError("Error while parsing runtime library funciton 0x8!");
  }
  put_subfile_data_func_ = DatExportApiPrivate::PutSubFileDataFunc(put_subfile_data_addr_); 
  
  flush_addr_ = GetProcAddress(datexport_dll_, "Flush"); 
  if (open_dat_file_addr_ == NULL) {
    ReportWinAPIError("Error while parsing runtime library funciton 0x9!");
  }
  flush_func_ = DatExportApiPrivate::FlushFunc(flush_func_);
}

int DatExportApi::OpenDatFile(int handle, const char* filename, unsigned int flags) {
  int did_master_map = 0;
  int block_size = 0;
  int vnum_dat_file = 0;
  int vnum_game_data = 0;
  unsigned long dat_file_id = 0;
  void* dat_id_stamp = malloc(64);
  void* first_iter_guid = malloc(64);

  int result = open_dat_file_func_(handle, filename, flags, &did_master_map, &block_size,
      &vnum_dat_file, &vnum_game_data, &dat_file_id, dat_id_stamp, first_iter_guid);

  free(dat_id_stamp);
  free(first_iter_guid);
  return result;
}

int DatExportApi::GetNumSubfiles(int handle) {
  return get_num_subfiles_func_(handle);
}

void DatExportApi::GetSubfileSizes(int handle, int* file_ids, int* size, int* iteration, int offset, int count) {
  get_subfile_sizes_func_(handle, file_ids, size, iteration, offset, count);
}

int DatExportApi::GetSubfileVersion(int handle, int file_id) {
  return get_subfile_version_func_(handle, file_id);
}

int DatExportApi::GetSubfileData(int handle, int file_id, void* target_buf, int& version) {
  return get_subfile_data_func_(handle, file_id, target_buf, 0, &version); // returns size of file in bytes
}

void DatExportApi::CloseDatFile(int handle) {
  close_dat_file_func_(handle);
}

int DatExportApi::PurgeSubfileData(int handle, int file_id) {
  return purge_subfile_data_func_(handle, file_id); // returns 0 on successfull
}

int DatExportApi::PutSubfileData(int handle, int file_id, void* data, int offset, int size, int version, int iteration, bool compress) {
  return put_subfile_data_func_(handle, file_id, data, offset, size, version, iteration, compress);
}

void DatExportApi::Flush(int handle) {
  flush_func_(handle);
}

DatExportApi::~DatExportApi() {
  FreeLibrary(datexport_dll_);
}