#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <assert.h>

#if defined(MSGLIDE) || defined (WTGLIDE)
#include <glide.h>
#elif defined (MSDD) || defined (WTDD)
#include <wgt95.h>
#endif

#define DIRECTINPUT_VERSION 0x300
#include <dinput.h>

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
#include "winutil.h"


#define TITLE "Power Render 2.4"

/* Forward declarations */
HWND hWndMain;
HINSTANCE  hInstance;    /* My instance handle */

char ** commandLineToArgv(LPSTR lpCmdLine, int *pArgc);
void PRGUI_InitPath (char *startpath);

/* The following variables are used to simulate the input routines
   from WGT 5.1 */
char kbdon[256];
static int keyhead = 0;
static int keytail = 0;
static int keyqueue[256] = {0};

PR_MouseCallback_data mouse;
int PR_mx0, PR_my0, PR_mx1, PR_my1;

#if defined (MSDD) || defined (WTDD)
int PR_LostSurface = 0;
#endif

/* Direct Input */
#define WM_SYNCACQUIRE      (WM_USER + 0)
LPDIRECTINPUT          g_pdi = NULL;
LPDIRECTINPUTDEVICE    g_pMouse;
LPDIRECTINPUTDEVICE    g_pKeyboard;
HANDLE                 g_hevtMouse;
int                    PR_AppActive;
int                    fKeybdAcquired;


int minit (void)
{
HRESULT  err;
GUID        guid = GUID_SysMouse;


  if (g_pdi == NULL)
  {
   err = DirectInputCreate(hInstance, DIRECTINPUT_VERSION, &g_pdi, NULL);
   if(FAILED(err))
   {
    MessageBox(NULL, "Unable to Create DirectInput Object",
                 TITLE, MB_OK);
      return FALSE;
   }
  }


  /* Obtain an interface to the system mouse device. */
  err = g_pdi->lpVtbl->CreateDevice(g_pdi, &guid, &g_pMouse, NULL);

  if (FAILED(err)) 
    {
     MessageBox(NULL, "Unable to Create DirectInput Mouse Device",
                TITLE, MB_OK);
     return FALSE;
    }

  /* Set the data format to "mouse format". */
  err = g_pMouse->lpVtbl->SetDataFormat(g_pMouse, &c_dfDIMouse);

  if (FAILED(err)) 
    {
     MessageBox(NULL, "Unable to Set Mouse Format",
                TITLE, MB_OK);
         return FALSE;
    }


  /* Set the cooperativity level. */
  err = g_pMouse->lpVtbl->SetCooperativeLevel(g_pMouse, hWndMain,
              DISCL_EXCLUSIVE | DISCL_FOREGROUND);

  if (FAILED(err)) 
    {
     MessageBox(NULL, "Unable to Set Cooperative Level",
                TITLE, MB_OK);
     return FALSE;
    }

  PR_mx0 = 0;
  PR_my0 = 0;
  PR_mx1 = 639;
  PR_my1 = 479;
  mouse.mx = 320;
  mouse.my = 240;
  mouse.but = 0;
  return TRUE;
}




void mdeinit (void)
{
  if (g_pdi)      g_pdi   ->lpVtbl->Release(g_pdi), g_pdi    = NULL;
  if (g_pMouse)   g_pMouse->lpVtbl->Release(g_pMouse), g_pMouse = NULL;
}


void msetbounds (int x0, int y0, int x1, int y1)
{
  PR_mx0 = x0;
  PR_my0 = y0;
  PR_mx1 = x1;
  PR_my1 = y1;
}


void msetxy (int x0, int y0)
{
  mouse.mx = x0;
  mouse.my = y0;
}


void noclick (void)
{
  while (mouse.but)
    UpdateMessages ();
}


void PR_ReadMouse (void)
{
DIMOUSESTATE diMouseState;

  if (PR_AppActive)
    {
     if (g_pMouse->lpVtbl->GetDeviceState(g_pMouse,
                        sizeof(diMouseState), &diMouseState) == DI_OK) 
       {
        mouse.mx += (int)diMouseState.lX;
        mouse.my += (int)diMouseState.lY;
        mouse.but = (diMouseState.rgbButtons[0]>0) + 
                   ((diMouseState.rgbButtons[1]>0)<<1) +
                   ((diMouseState.rgbButtons[2]>0)<<2);
       }
    }

  /* Clip the cursor to our client area */
  if (mouse.mx < PR_mx0)
      mouse.mx = PR_mx0;
  if (mouse.mx > PR_mx1) 
      mouse.mx = PR_mx1;

  if (mouse.my < PR_my0)
      mouse.my = PR_my0;
  if (mouse.my > PR_my1) 
      mouse.my = PR_my1;
}


void PR_MouseSyncAcquire (HWND hwnd)
{
    if (PR_AppActive) {
        if (g_pMouse) 
                        g_pMouse->lpVtbl->Acquire(g_pMouse);
    } else {
        if (g_pMouse) 
                        g_pMouse->lpVtbl->Unacquire(g_pMouse);
    }
//    InvalidateCursorRect(hwnd);
}



int installkbd (void)
{
GUID     guid = GUID_SysKeyboard;
HRESULT  err;


  if (g_pdi == NULL)
  {
   err = DirectInputCreate(hInstance, DIRECTINPUT_VERSION, &g_pdi, NULL);
   if(FAILED(err))
   {
      MessageBox(NULL, "Unable to Create DirectInput Object",
                 TITLE, MB_OK);
      return FALSE;
   }
  }


  /* Obtain an interface to the system mouse device. */
  err = g_pdi->lpVtbl->CreateDevice(g_pdi, &guid, &g_pKeyboard, NULL);
  if (FAILED(err)) 
    {
     MessageBox(NULL, "Unable to Create DirectInput Keyboard Device",
                TITLE, MB_OK);
     return FALSE;
    }

  /* Set the data format to "mouse format". */
  err = g_pKeyboard->lpVtbl->SetDataFormat(g_pKeyboard, &c_dfDIKeyboard);

  if (FAILED(err)) 
    {
     MessageBox(NULL, "Unable to Set Keyboard Format",
                TITLE, MB_OK);
         return FALSE;
    }


  /* Set the cooperativity level. */
  err = g_pKeyboard->lpVtbl->SetCooperativeLevel(g_pKeyboard, hWndMain,
              DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);

  if (FAILED(err)) 
    {
     MessageBox(NULL, "Unable to Set Keyboard Cooperative Level",
                TITLE, MB_OK);
     return FALSE;
    }


    // try to acquire the keyboard
   err = g_pKeyboard->lpVtbl->Acquire(g_pKeyboard);
   if(SUCCEEDED(err))
   {
      // keyboard was acquired
      fKeybdAcquired = TRUE;
    }
   else
   {
      // keyboard was NOT acquired
      fKeybdAcquired = FALSE;
   }

    // if we get here, all objects were created successfully
    return TRUE;    
}




/*
*
* DI_ReadKeys
*
* Use DirectInput to read game-play keys
*
*/
void PR_ReadKeyboard(void)
{
HRESULT hRes;

  if (g_pKeyboard == NULL)
    return;

   hRes = g_pKeyboard->lpVtbl->GetDeviceState(g_pKeyboard, sizeof(kbdon), kbdon);
   if(hRes != DI_OK)
    {
      if(hRes == DIERR_INPUTLOST)
      {
         // we lost control of the keyboard, reacquire
         fKeybdAcquired = FALSE;
         if(SUCCEEDED(g_pKeyboard->lpVtbl->Acquire(g_pKeyboard)))
         {
            fKeybdAcquired = TRUE;
         }
      }

      // failed to read the keyboard, just return
        return;
    }
}

void uninstallkbd (void)
{
  if(fKeybdAcquired)
  {
   g_pKeyboard->lpVtbl->Unacquire(g_pKeyboard);
   fKeybdAcquired = FALSE;
  }

  if(g_pKeyboard != NULL)
    g_pKeyboard->lpVtbl->Release(g_pKeyboard);
  g_pKeyboard = NULL;
}


BOOL PR_KeyboardSyncAcquire (void)
{
    // try to acquire the keyboard
        if(g_pKeyboard != NULL)
          {
           g_pKeyboard->lpVtbl->Acquire(g_pKeyboard);
          }
        else
          {
                // keyboard device has not been created.
                fKeybdAcquired = FALSE;
                return FALSE;
          }
  
        // if we get here, we are acquired again
        fKeybdAcquired = TRUE;
        return TRUE;
}




long FAR PASCAL MainWndproc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
    PAINTSTRUCT ps;
    HDC         hdc;
    LRESULT     lRc;

    switch (message)
    {

    case WM_SETCURSOR:
         SetCursor(NULL);
         return 0;
         break;

    case WM_KEYDOWN:

         if(!fKeybdAcquired)
           {
            keyqueue[keyhead++] = wParam & 0xFF; 
            keyhead &= 255;
            return 0;
           }
        break;

    case WM_KEYUP:
         return 0;
         break;

	//case WM_ACTIVATEAPP:
		//PR_AppActive = TRUE;
	//break;
    
    case WM_ACTIVATE:
        {
         WORD fActive = LOWORD(wParam);
         BOOL fMinimized = (BOOL) HIWORD(wParam);

         if ( ( fActive == WA_INACTIVE ) || fMinimized ) 
             {
              #if defined(MSGLIDE) || defined(WTGLIDE)
			  grSstControl (GR_CONTROL_DEACTIVATE);
                          PR_AppActive = FALSE;
			  #endif
              PR_AppActive = FALSE;
              PR_MouseSyncAcquire (hWnd);

			  #if defined (MSDD) || defined (WTDD)
			  PR_LostSurface = 1;
			  #endif
             } 
         else if ((fActive == WA_ACTIVE) || (fActive == WA_CLICKACTIVE))
             {
			  #if defined(MSGLIDE) || defined(WTGLIDE)
              grSstControl (GR_CONTROL_ACTIVATE);
                          PR_AppActive = TRUE;
			  #endif

              PR_AppActive = TRUE;
              PR_MouseSyncAcquire(hWnd);
              PR_KeyboardSyncAcquire();
              SetForegroundWindow (hWnd);
             }
            return 0;
       }
       break;



    case WM_ENTERMENULOOP:
    case WM_ENTERSIZEMOVE:
         PR_AppActive = FALSE;
         PR_MouseSyncAcquire(hWnd);
         PR_KeyboardSyncAcquire();
         break;

    case WM_EXITMENULOOP:
    case WM_EXITSIZEMOVE:
         PR_AppActive = GetActiveWindow() == hWnd;
         PostMessage(hWnd, WM_SYNCACQUIRE, 0, 0L);
         break;

    case WM_SYNCACQUIRE:
         PR_MouseSyncAcquire (hWnd);
         PR_KeyboardSyncAcquire();
         break;
                 
    case WM_CREATE:
        break;

    case WM_PAINT:
		hdc = BeginPaint( hWnd, &ps );
        EndPaint( hWnd, &ps );
        return 1;

    case WM_CLOSE:
        keyqueue[keyhead++] = 'q'; keyhead &= 255;
        break;

    case WM_DESTROY:
        PostQuitMessage (0);
        break;

    case WM_MOVE:
		#if defined(MSGLIDE) || defined(WTGLIDE)        
		if (!grSstControl(GR_CONTROL_MOVE)) {
            PostMessage( hWnd, WM_CLOSE, 0, 0 );
            return 0;
        }
		#endif        
		break;

        case WM_SYSCOMMAND:
          switch (GET_WM_COMMAND_ID(wParam, lParam)) {

          case SC_SCREENSAVE:
          break;
        
          default:
          lRc = DefWindowProc(hWnd, message, wParam, lParam);
          break;
          }
        
        if (IsWindow(hWnd)) {
         PR_MouseSyncAcquire (hWnd);
         PR_KeyboardSyncAcquire();
        }
    return lRc;

    case WM_DISPLAYCHANGE:
    case WM_SIZE:
        {
            extern void getWindowSize(float *width, float *height);
            float  width, height;

            getWindowSize(&width, &height);
            
        }
        
		#if defined(MSGLIDE) || defined(WTGLIDE)
		if (!grSstControl(GR_CONTROL_RESIZE)) {
            PostMessage(hWnd, WM_CLOSE, 0, 0 );
            return 0;
        }
		#endif
        break;


    default:
        break;
    }
    return DefWindowProc(hWnd, message, wParam, lParam);

} /* MainWndproc */

/*
 * initApplication
 *
 * Do that Windows initialization stuff...
 */
void PRGUI_LoadCursor3Dx (void);

static BOOL initApplication( HANDLE hInstance, int nCmdShow )
{
    WNDCLASS    wc;
    BOOL        rc;


wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = MainWndproc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon( NULL, IDI_APPLICATION);    /* generic icon */
    wc.hCursor = NULL; //LoadCursor( NULL, IDC_ARROW );
    wc.hbrBackground = NULL; //GetStockObject( BLACK_BRUSH );
    wc.lpszMenuName =  NULL;
    wc.lpszClassName = "WinGlideClass";
    rc = RegisterClass( &wc );
    if( !rc )
    {
        return FALSE;
    }

    hWndMain = CreateWindowEx(
        WS_EX_TOPMOST,
        "WinGlideClass",
        TITLE,
        WS_POPUP,      /* non-app window */

        CW_USEDEFAULT, 
        CW_USEDEFAULT,
        320,                /* GetSystemMetrics(SM_CXSCREEN), */
        40,                /* GetSystemMetrics(SM_CYSCREEN), */
        NULL,
        NULL,
        hInstance,
        NULL );

    if (!hWndMain)
      return FALSE;

    if (!minit ()) 
       {
        DestroyWindow(hWndMain);
        return 0;
       }

    ShowWindow (hWndMain, SW_NORMAL);
    UpdateWindow (hWndMain);
    
    BringWindowToTop (hWndMain);
    SetFocus (hWndMain);
        
    PR_AppActive = TRUE;
    PR_MouseSyncAcquire (hWndMain);
    return TRUE;
} /* initApplication */

/*
 * WinMain
 */
int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, 
    LPSTR lpCmdLine, int nCmdShow )
{
LPSTR realcommand;
    
        if( !initApplication(hInstance, nCmdShow) )
        return FALSE;

    {
        int     argc;
        char    **argv;

#ifdef __WATCOMC__
        extern int WatMain(int argc, char **argv);
#else
        extern int main(int argc, char **argv);
#endif
                realcommand = GetCommandLine ();
        argv = commandLineToArgv(realcommand, &argc);
                PRGUI_InitPath (argv[0]);

#ifdef __WATCOMC__
        WatMain (argc, argv);
#else
        main (argc, argv);
#endif
    }

    mdeinit ();
    DestroyWindow(hWndMain);
   return 0;

} /* WinMain */



/*
 * Converts lpCmdLine to WinMain into argc, argv
 */
static char    *argvbuf[32];
static char    cmdLineBuffer[1024];
char **
commandLineToArgv(LPSTR lpCmdLine, int *pArgc)
{
    char    *p, *pEnd;
    int     argc = 0;

    if (lpCmdLine == NULL) {
        *pArgc = argc;
         return argvbuf;
    }

    strcpy(cmdLineBuffer, lpCmdLine);
    p = cmdLineBuffer;
    pEnd = p + strlen(cmdLineBuffer);
    if (pEnd >= &cmdLineBuffer[1022]) pEnd = &cmdLineBuffer[1022];

    fflush (stdout);

    while (1) {
        /* skip over white space */
        fflush(stdout);

        while (*p == ' ') p++;
        if (p >= pEnd) break;

        while (*p == '\"') p++;
        if (p >= pEnd) break;

        argvbuf[argc++] = p;
        if (argc >= 32) break;

        /* skip till there's a 0 or a white space */
        while (*p && (*p != ' ')) p++;
                

        if (*p == ' ') *p++ = 0;
                if (*p == '\"') *p++ = 0;
    }

    *pArgc = argc;
    return argvbuf;
}



void getWindowSize(float *width, float *height)
{
RECT    rect;

    GetClientRect(hWndMain, &rect);
    *width = (float) rect.right;
    *height = (float) rect.bottom;
}


/* This dispatches any messages waiting and
   waits in a loop if the application is not active. */
void UpdateMessages (void)
{
MSG msg;

  PR_ReadMouse ();
  PR_ReadKeyboard ();

  do {
  while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 
  {
   TranslateMessage(&msg);
   DispatchMessage(&msg);      
  }
  } while (!PR_AppActive);
}



char PR_getch (void)
{
MSG     msg;
char    rv;

  if (keytail != keyhead)
    {
     rv = keyqueue[keytail++];
     keytail &= 255;
     return rv;
    }

  while (GetMessage( &msg, NULL, 0, 0 ))
    {
     TranslateMessage (&msg);
     DispatchMessage (&msg);

     if (keytail != keyhead)
       {
        rv = keyqueue[keytail++];
        keytail &= 255;
        return rv;
       }
    }
   return rv;
}


int PR_kbhit (void)
{
MSG msg;

  if (keyhead != keytail)
    return 1;

  while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
    {
     TranslateMessage (&msg);
     DispatchMessage (&msg);      /* this might change keyhead */
     if (keyhead != keytail)
       return 1;
    }
  return 0;
}

