#include "main.h"
#include <shellapi.h>
#include "proxyIDirect3D9.h"
#include "proxyIDirect3DDevice9.h"
#include "proxyIDirect3DSwapChain9.h"


typedef proxyIDirect3D9 *(WINAPI *D3DC9)(UINT);
typedef HRESULT (WINAPI *HRESULT_VOID)(void);
typedef HRESULT (WINAPI *HRESULT_BOOL)(BOOL);

#pragma data_seg(".d3d9_shared")
proxyIDirect3DDevice9      *pDirect3DDevice9 = NULL;
proxyIDirect3D9            *pDirect3D9 = NULL;
proxyIDirect3DSwapChain9   *pDirect3DSwapChain9 = NULL;
HINSTANCE                  hOrigDll = NULL;
HMODULE                    hDllModule = NULL;
D3DC9                      orig_Direct3DCreate9 = NULL;
HRESULT_VOID               orig_Direct3DShaderValidatorCreate9 = NULL;
#pragma data_seg()

char     dll_working_dir[MAX_PATH]; /* used by log_debug() and ini_load() */
FILE     *log_file = NULL;


void log_debug(const char *fmt, ...)
{
   SYSTEMTIME time;
   va_list ap;


   if(log_file == NULL)
   {
      char filename[512], filename_old[512];

      snprintf(filename, sizeof(filename), "%s\\%s", dll_working_dir, "s0beit_hack.log");
      snprintf(filename_old, sizeof(filename_old), "%s\\%s", dll_working_dir, "s0beit_hack_old.log");

      _unlink(filename_old);
      rename(filename, filename_old);

      log_file = fopen(filename, "a");
      if(log_file == NULL)
         return;
   }

   GetLocalTime(&time);

   fprintf(log_file, "[%02d:%02d.%-3d] ", time.wMinute, time.wSecond, time.wMilliseconds);

   va_start(ap, fmt);
   vfprintf(log_file, fmt, ap);
   va_end(ap);

   fprintf(log_file, "\r\n");

   fflush(log_file);
}


static int init(void)
{
   char filename[MAX_PATH];

   if(hOrigDll == NULL)
   {
      srand(GetTickCount());

      /* get path to ourselves */
      if(GetModuleFileName(hDllModule, dll_working_dir, sizeof(dll_working_dir) - 32) != 0)
      {
         if(strrchr(dll_working_dir, '\\') != NULL)
            *strrchr(dll_working_dir, '\\') = 0;
         else
            strcpy(dll_working_dir, ".");
      }
      else
      {
         strcpy(dll_working_dir, ".");
      }

      log_debug("Initializing.");

      log_debug("Working directory: %s", dll_working_dir);

      #pragma warning(disable:4127)
      if(sizeof(struct vehicle_info) != 2584)
      {
         log_debug("sizeof(struct vehicle_info) == %d, aborting.", sizeof(struct vehicle_info));
         return 0;
      }

      if(sizeof(struct actor_info) != 1988)
      {
         log_debug("sizeof(struct actor_info) == %d, aborting.", sizeof(struct actor_info));
         return 0;
      }
      #pragma warning(default:4127)

      ini_load();
      ini_safe_haven_load();

      if(!set.i_have_edited_the_ini_file)
      {
         MessageBox(0,
            "Looks like you've not edited the .ini file like you were told to!\n"
            "\n"
            "Before you can use m0d_s0beit, you have to set \"i_have_edited_the_ini_file\" to true.\n",
            "You're a retard.", 0);
         ShellExecute(0, "open", "notepad", "s0beit_hack.ini", dll_working_dir, SW_SHOW);
         return 0;
      }

      GetSystemDirectory(filename, (UINT)(MAX_PATH - strlen("\\d3d9.dll") - 1));
      strlcat(filename, "\\d3d9.dll", sizeof(filename));

      log_debug("Loading library: %s", filename);

      hOrigDll = LoadLibrary(filename);
      if(hOrigDll == NULL)
      {
         log_debug("Failed to load %s", filename);
         return 0;
      }

      orig_Direct3DCreate9 = (D3DC9)GetProcAddress(hOrigDll, "Direct3DCreate9");
      if(orig_Direct3DCreate9 == NULL)
      {
         log_debug("%s does not export Direct3DCreate9!?", filename);
         FreeLibrary(hOrigDll);
         return 0;
      }

      log_debug("Done.");
   }

   return 1;
}


IDirect3D9 *WINAPI sys_Direct3DCreate9(UINT SDKVersion)
{
   pDirect3D9 = NULL;

   if(init())
      pDirect3D9 = new proxyIDirect3D9(orig_Direct3DCreate9(SDKVersion));

   log_debug("Direct3DCreate9() called. Returning 0x%p.", pDirect3D9);

   return pDirect3D9;
}


BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
   lpReserved = lpReserved;

   switch(ul_reason_for_call)
   {
   case DLL_PROCESS_ATTACH:
      DisableThreadLibraryCalls(hModule);
      hDllModule = hModule;
      break;

   case DLL_PROCESS_DETACH:
      if(hOrigDll != NULL)
      {
         log_debug("Detached from process. Freeing d3d9.dll...");
         FreeLibrary(hOrigDll);
         hOrigDll = NULL;
         log_debug("Freeing memory...");
         menu_free_all();
         ini_free();
      }
      if(log_file != NULL)
      {
         log_debug("Closing log file.");
         fclose(log_file);
         log_file = NULL;
      }
      break;
   }

   return TRUE;
}
