/*
======================================================================
qv.c

Ernie Wright  17 Apr 05
MSVC 4.0

Render display plug-in for the Windows version of LightWave 3D.  Link
with a thread-aware version of the runtime (LIBCMT.LIB, LIBCMTD.LIB).
====================================================================== */

#include "qv.h"
#include <process.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <lwdisplay.h>
#include <lwframbuf.h>


static HWND hwnd;
static HINSTANCE hinst;
static HostDisplayInfo *hdi;
static GlobalFunc *lwglobal;

/*
This was inserted 02 Sep 05 to fix a problem with corrupt downloads
of the zipped version of the .p file.  The zip, by chance, apparently
contained a sequence of bytes that, somewhere, caused a newline
translation during download, which of course corrupted the file.
For newer versions, we'll comment this out until we need it again. */

// static int junk = 0x01020304;


/*
======================================================================
DllMain()

Entry point called by Windows when LW loads or unloads the module.
On load, get the instance handle and do one-time Windows-related
initialization.  On unload, free Windows-related resources.  Certain
other things, like writing the QV config file, are done at unload time
too, but this should only be done if Handler() was called first, which
is checked by testing whether lwglobal is NULL.
====================================================================== */

BOOL WINAPI DllMain( HANDLE hInstance, ULONG reason, LPVOID reserved )
{
   if ( reason == DLL_PROCESS_ATTACH ) {
      hinst = hInstance;
      imvInit( hInstance );
   }

   if ( reason == DLL_PROCESS_DETACH && lwglobal != NULL ) {
      imvCleanup();
   }

   return TRUE;
}


/*
======================================================================
Create(), Destroy()

Standard instance handler functions.  Allocate and free instance data.
====================================================================== */

XCALL_( static LWInstance )
Create( void *priv, void *context, LWError *emsg )
{
   QVInstance *qvi;

   qvi = calloc( 1, sizeof( QVInstance ));
   if ( !qvi ) *emsg = "QV couldn't allocate instance.";
   return ( LWInstance ) qvi;
}


XCALL_( static void )
Destroy( LWInstance lwi )
{
   if ( lwi ) free( lwi );
}


/*
======================================================================
Copy()

Not supported.
====================================================================== */

XCALL_( static LWError )
Copy( LWInstance to, LWInstance from ) { return NULL; }


/*
======================================================================
Load(), Save()

Load or save QV settings in a scene file.
====================================================================== */

XCALL_( static LWError )
Load( LWInstance lwi, const LWLoadState *ls )
{
   loadQVConfig( imvGetConfig(), ls );
   return NULL;
}


XCALL_( static LWError )
Save( LWInstance lwi, const LWSaveState *ss )
{
   saveQVConfig( imvGetConfig(), ss );
   return NULL;
}


/*
======================================================================
Open()

Store the image width and height.
====================================================================== */

XCALL_( static LWError )
Open( QVInstance *qvi, int w, int h )
{
   LWInterfaceInfo *iinfo;

   qvi->width = w;
   qvi->height = h;
   qvi->starttime = ( int ) clock();

   if ( iinfo = lwglobal( LWINTERFACEINFO_GLOBAL, GFUSE_TRANSIENT ))
      qvi->frametime = ( float ) iinfo->curTime;
   else
      qvi->frametime = 0.0F;

   return NULL;
}


/*
======================================================================
Close()

If the user left QV on during an automatic render, we need to free the
image buffer allocated by Begin(), since Pause() was never called.
====================================================================== */

XCALL_( static void )
Close( QVInstance *qvi )
{
   if ( qvi->image ) {
      freeImage( qvi->image );
      qvi->image = NULL;
   }
}


/*
======================================================================
Begin()

Allocate storage for pixel values.  We need this on a per-frame basis.
Initialize scanline index.
====================================================================== */

XCALL_( static LWError )
Begin( QVInstance *qvi )
{
   LWSceneInfo *scene;

   qvi->image = createImage( qvi->width, qvi->height, ++qvi->index,
      10 * (( int ) clock() - qvi->starttime ) / CLOCKS_PER_SEC );

   if ( !qvi->image )
      return "QV couldn't allocate image buffer.";

   qvi->y = 0;

   if ( scene = lwglobal( LWSCENEINFO_GLOBAL, GFUSE_TRANSIENT )) {
      qvi->image->scene = *scene;
      qvi->image->framenum = ( int )
         ( qvi->frametime * scene->framesPerSecond + 0.5 );
   }

   return NULL;
}


/*
======================================================================
Write()

Copy a scanline into image memory.
====================================================================== */

XCALL_( static LWError )
Write( QVInstance *qvi,
   const float *r, const float *g, const float *b, const float *a )
{
   writeImageRow( qvi->image, qvi->y, r, g, b, a );
   ++qvi->y;
   return NULL;
}


/*
======================================================================
dlgthread()

A thread function for managing the QV display.

INPUTS
   p              a Displayable passed as a void *

RESULTS
   Displays the dialog and enters a message loop.  Terminates when the
   window procedure calls PostQuitMessage().

To duplicate the behavior of previous versions of QV, we attempt to
force the keyboard focus to LW's top-level requester window after the
QV dialog closes.  Previously, QV users could press ESC ESC to close
QV and the Abort/Continue requester and continue working in LW.  When
non-modal QV is closed, Windows shifts the keyboard focus to LW's main
window.  We search for a window with the same class and window text as
the Abort/Continue requester (currently "WMgrRequest" and "LW 3D"),
and if we find one, we force it to the foreground.
====================================================================== */

static void dlgthread( void *p )
{
   MSG msg;
   HWND hwnd1;
   char wclass[ 64 ], wname[ 64 ];

   hwnd1 = GetForegroundWindow();
   GetClassName( hwnd1, wclass, sizeof( wclass ));
   GetWindowText( hwnd1, wname, sizeof( wname ));

   if ( hwnd = imvCreateWindow( hdi->window, ( Image * ) p )) {
      while ( GetMessage( &msg, NULL, 0, 0 )) {
         TranslateMessage( &msg );
         DispatchMessage( &msg );
      }
   }

   hwnd = NULL;

   if ( hwnd1 = FindWindow( wclass, wname ))
      SetForegroundWindow( hwnd1 );

   _endthread();
}


/*
======================================================================
closeRenderStatus()
findRenderStatus()

Attempt to automatically close LW's Render Status window.  The find
function is a Windows EnumWindowsProc, the callback used when the
close function calls EnumWindows().

I added this 20 Jun 06, after a report that LW 9 was naming its Render
Status window differently.  Previously I'd just called

   FindWindow( "WMgrBasic", "Render Status" );

but LW 9 has apparently added the scene name to the window title, so
now we have to enumerate the windows and find the one that *begins*
with "Render Status".
====================================================================== */

static BOOL CALLBACK findRenderStatus( HWND hwnd, LPARAM lparam )
{
   char classname[ 32 ];
   char wndname[ 32 ];

   GetClassName( hwnd, classname, sizeof( classname ));
   GetWindowText( hwnd, wndname, sizeof( wndname ));
   if (( !strcmp( classname, "WMgrBasic" )) &&
       ( !strncmp( wndname, "Render Status", 13 ))) {
      HWND *hwndRenderStatus = ( HWND * ) lparam;
      *hwndRenderStatus = hwnd;
      return FALSE;
   }
   return TRUE;
}


static void closeRenderStatus( void )
{
   HWND hwndRenderStatus = 0;
   EnumWindows( findRenderStatus, ( LPARAM ) &hwndRenderStatus );
   if ( hwndRenderStatus )
      SendMessage( hwndRenderStatus, WM_KEYDOWN, VK_ESCAPE,
         ( LPARAM ) 0x0101 );
}


/*
======================================================================
Pause()

Display the dialog.
====================================================================== */

XCALL_( static void )
Pause( QVInstance *qvi )
{
   QVConfig *config = imvGetConfig();
   if ( config->closeRS )
      closeRenderStatus();
   if ( hwnd )
      PostMessage( hwnd, UM_ADDIMAGE, 0, ( LPARAM ) qvi->image );
   else
      qvi->thread = _beginthread( dlgthread, 0, qvi->image );
   qvi->image = NULL;
}


/*
======================================================================
Handler()
====================================================================== */

XCALL_( int )
Handler( long version, GlobalFunc *global, LWFrameBufferHandler *local,
   void *serverData )
{
   if ( version != LWFRAMEBUFFER_VERSION )
      return AFUNC_BADVERSION;

   hdi = global( "Host Display Info", GFUSE_TRANSIENT );
   if ( !hdi ) return AFUNC_BADGLOBAL;
   if ( !imvInitLW( global )) return AFUNC_BADGLOBAL;

   local->type          = LWFBT_FLOAT;
   local->inst->create  = Create;
   local->inst->destroy = Destroy;
   local->inst->copy    = Copy;
   local->inst->load    = Load;
   local->inst->save    = Save;
   local->open          = Open;
   local->close         = Close;
   local->begin         = Begin;
   local->write         = Write;
   local->pause         = Pause;

   lwglobal = global;
   return AFUNC_OK;
}


/*
======================================================================
Options()

Interface callback.
====================================================================== */

XCALL_( static LWError )
Options( QVInstance *inst )
{
   hdi = lwglobal( "Host Display Info", GFUSE_TRANSIENT );
   aboutDialog( hinst, hdi->window, 3000 );
   return NULL;
}


/*
======================================================================
Interface()

Interface activation function.
====================================================================== */

XCALL_( int )
Interface( long version, GlobalFunc *global, LWInterface *local,
   void *serverData )
{
   if ( version != LWINTERFACE_VERSION )
      return AFUNC_BADVERSION;

   local->panel   = NULL;
   local->options = Options;
   local->command = NULL;

   lwglobal = global;
   return AFUNC_OK;
}


ServerRecord ServerDesc[] = {
   { "FrameBufferHandler", "QV40", Handler },
   { "FrameBufferInterface", "QV40", Interface },
   { NULL }
};
