#include "main.h"
#include <d3dx9.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[256]; /* used by log_debug() and ini_load() */
FILE     *log_file = NULL;


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


   if(log_file == NULL)
   {
      snprintf(filename, sizeof(filename), "%s\\%s", dll_working_dir, "s0beit_hack.log");  /* dll_working_dir is 255 chars max */

      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);
}


IDirect3D9 *WINAPI sys_Direct3DCreate9(UINT SDKVersion)
{
   log_debug("Direct3DCreate9() called.");

   pDirect3D9 = new proxyIDirect3D9(orig_Direct3DCreate9(SDKVersion));

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

   return pDirect3D9;
}


BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
   char filename[MAX_PATH];

   lpReserved = lpReserved;

   switch(ul_reason_for_call)
   {
   case DLL_PROCESS_ATTACH:
      //DisableThreadLibraryCalls(hModule);

      hDllModule = hModule;

      if(hOrigDll == NULL)
      {
         /* get path to ourselves */
		   if(GetModuleFileName(hModule, 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, crashing.", sizeof(struct vehicle_info));
            return FALSE;
         }

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

         ini_load();
         ini_safe_haven_load();

         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 FALSE;
         }

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

         log_debug("Done.");
      }
      break;

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

   return TRUE;
}
