//////////////////////////////////////////////////////////////////////
//
// Project  : BRender Demo Framework
//            Copyright (c) 1996 Argonaut Technologies Ltd.
//            All rights reserved.
//
// Author   : Steve Williams, October 1996
//
// File     : WIN_BITS.C
//
// Requires : BRender V1.3
//            Microsoft DirectX 3, Win95 / NT.
//
// Comments : Windows demo framework main implementation file.
//            See also demomenu.c, timing.c, winkey.c
//
//////////////////////////////////////////////////////////////////////

#include "br_demo.h"

// Device from device menu to use initially.

#define BR_DEMO_INITIAL_DEVICE 0

// Structure to keep track of all framework data.

static WIN_BITS BrWinApp ;

// Keep track of the state of the mouse buttons

static br_boolean bLeftButtonDown = BR_FALSE, bRightButtonDown = BR_FALSE ; 

// These are filled in during the device query functions in demomenu.c

char BrDemo_Device_Name[MAX_DEVICES][20];

// Arguments for DOS parameter passing
int argc;
char *argv[10];

// -----------------------------------------------------------------
// Function : BrDemo_Allocate_Pixelmaps_Int 
//
// Description : Starts up BRender device and allocates necessary
//               pixelmaps. Loads and sets palette.
// -----------------------------------------------------------------

br_error BrDemo_Allocate_Pixelmaps_Int( WIN_BITS_PTR pBrApp )
{
	br_error r ;
	
	// Create BRender device
	
    if ( pBrApp->bFullScreen )
    {
			r = BrDevBeginVar( &pBrApp->screen, BrDemo_Device_Name[pBrApp->Current_Device],
                  BRT_WINDOW_HANDLE_H, pBrApp->hWnd,
                  BRT_WINDOW_FULLSCREEN_B, pBrApp->bFullScreen,
				  BRT_WIDTH_I32, pBrApp->Width,
				  BRT_HEIGHT_I32, pBrApp->Height,
                  BRT_PIXEL_BITS_I32, pBrApp->Fullscreen_BPP,
                  BR_NULL_TOKEN );
	
		if ( r != BRE_OK )
		{
			BR_ERROR4("Could not start device '%s' full screen %dx%dx%d.", BrDemo_Device_Name[pBrApp->Current_Device], pBrApp->Width, pBrApp->Height, pBrApp->Fullscreen_BPP  );
    		return r ;
		}
	}
	else
	{
		r = BrDevBeginVar( &pBrApp->screen, BrDemo_Device_Name[pBrApp->Current_Device],
                 BRT_WINDOW_HANDLE_H, pBrApp->hWnd,
                 BRT_WINDOW_FULLSCREEN_B, BR_FALSE,
                 BR_NULL_TOKEN );

		if ( r != BRE_OK )
		{
			BR_ERROR2( "Could not start device '%s' windowed, %d bpp", BrDemo_Device_Name[pBrApp->Current_Device], pBrApp->Windowed_BPP );
    		return r ;
		}
	}

   if ( pBrApp->bUsePalette )
   {
        pBrApp->palette = BrPixelmapLoad(pBrApp->palette_filename);

        if ( pBrApp->palette )
        {
           BrPixelmapPaletteSet( pBrApp->screen, pBrApp->palette );
           pBrApp->back_buffer = pBrApp->palette ;
        }
        else
        {
           pBrApp->palette = NULL ;
           BR_ERROR1("Could not load palette %s", pBrApp->palette_filename );
           return BRE_FAIL ;
        }
	}

    
	// Allocate other buffers

   if ( pBrApp->bStretch )
		pBrApp->back_buffer = BrPixelmapMatchSized( pBrApp->screen, BR_PMMATCH_OFFSCREEN, BrWinApp.Offscreen_Width, BrWinApp.Offscreen_Height );
	else
	    pBrApp->back_buffer = BrPixelmapMatch( pBrApp->screen, BR_PMMATCH_OFFSCREEN );

   if ( !pBrApp->back_buffer )
   {
      BR_ERROR0("Could not allocate back buffer");
      return BRE_FAIL ;
   }

	if ( BrWinApp.bUseZBuffer )
   {
      pBrApp->depth_buffer = BrPixelmapMatch( pBrApp->back_buffer, BR_PMMATCH_DEPTH_16 );
  
      if ( !pBrApp->depth_buffer )
      {
         BR_ERROR0("Could not allocate depth buffer");
         return BRE_FAIL ;
      }
   }
   else 
      pBrApp->depth_buffer = NULL ;

   return BRE_OK ;
}


// -----------------------------------------------------------------
// Function : BrDemo_Refresh_Pointers
//
// Description : Updates app's copy of the pixelmap pointers.
// -----------------------------------------------------------------


br_error BrDemo_Refresh_Pointers( br_pixelmap **screen, 
							   br_pixelmap **back_buffer,
							   br_pixelmap **depth_buffer )
{
    // Load up the app's copy
	
	*screen = BrWinApp.screen ;
    *back_buffer = BrWinApp.back_buffer ;

    if ( depth_buffer )
    	*depth_buffer = BrWinApp.depth_buffer ;

   return BRE_OK ;
}


// -----------------------------------------------------------------
// Function : BrDemo_PaletteSet
//
// Description : Sets the palette, and keeps an internal pointer to
// the palette, so we can set it when Windows asks us to.
// -----------------------------------------------------------------

br_error BrDemo_PaletteSet( br_pixelmap *pNewPal )
{
   BrWinApp.palette = pNewPal ;

	BrPixelmapPaletteSet( BrWinApp.screen, BrWinApp.palette );
   BrWinApp.back_buffer = BrWinApp.palette ;
    
	return BRE_OK ;
}


// -----------------------------------------------------------------
// Function : BrDemo_CreateWindow
//
// Description : Creates the demo's window, attaches the menu,
// which has to be modified first by calling BRender' device query
// functions.
// -----------------------------------------------------------------

static br_error BrDemo_CreateWindow( WIN_BITS_PTR pBrApp )
{
   	br_error r ;
	
	BrWinApp.hWnd = CreateWindowEx(0, "BrDemoClass", pBrApp->name, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, GetModuleHandle(NULL), NULL);

   if ( pBrApp->hWnd == NULL )
       return BRE_FAIL ;

   // Attach demo menu, query BRender for devices and output
   // facilities, and add these to the menu.
   
   r = BrDemo_Attach_Menu(pBrApp);

   if ( r != BRE_OK )
	   return r ;

   // Show and update window

   ShowWindow( pBrApp->hWnd, SW_SHOWDEFAULT );
   UpdateWindow( pBrApp->hWnd );
  
   return BRE_OK ;
}


// -----------------------------------------------------------------
// Function : BrDemo_Allocate_Pixelmaps
//
// Description : Creates device using BrDemo_Allocate_Pixelmaps_Int,
// sets app's pointers, and marks the app as active.
// -----------------------------------------------------------------


br_error BrDemo_Allocate_Pixelmaps( char *AppName,
								   br_pixelmap **screen, 
							       br_pixelmap **back_buffer,
							       br_pixelmap **depth_buffer, 
							       br_pixelmap **palette, 
							       char *palette_filename )
{
    br_error r ;
	static bFirstTime = BR_TRUE ;
	
	
	BrWinApp.bUsePalette = palette != NULL ;
	BrWinApp.bUseZBuffer = depth_buffer != NULL ;
	
	if ( AppName )
	   BrWinApp.name = AppName ;
	else
	   BrWinApp.name = "BRender Demo" ;

	if ( bFirstTime )
	{
		r = BrDemo_CreateWindow( &BrWinApp );
			
		if ( r != BRE_OK )
			return r ;

		bFirstTime = BR_FALSE ;
	}
   
    if ( palette_filename  )
	    BrWinApp.palette_filename = BrMemStrDup( palette_filename );
	else
		BrWinApp.palette_filename = NULL ;
		
	r =  BrDemo_Allocate_Pixelmaps_Int(&BrWinApp);
	
	if ( r != BRE_OK )
		return r ;

	BrDemo_Refresh_Pointers( screen, back_buffer, depth_buffer );

	if ( palette )
  	   *palette = BrWinApp.palette ;

	BrWinApp.bActive = BR_TRUE ;
	
	return BRE_OK ;
}


// -----------------------------------------------------------------
// Function : BrDemo_Remove_Device
//
// Description : Closes down app and current device.
// -----------------------------------------------------------------

br_error BrDemo_Remove_Device()
{
	br_error r ;
	
	r = BrDemo_ShutDown();
	BrEnd();

	BrWinApp.screen = NULL ;

	return r ;
}


// -----------------------------------------------------------------
// Function : BrDemo_Restart
//
// Description : Allows the app to restart itself by shutting it
//               down, then firing it up again. Currently only
//               used in Forest.
// -----------------------------------------------------------------

br_error BrDemo_Restart()
{
    BrDemo_Remove_Device();
	BrDemo_ClearAppMenuItems();
	BrDemo_Initiallise_BRender();

	return BRE_OK ;
}

// -----------------------------------------------------------------
// Function : BrDemo_ReleasePixelmaps
//
// Description : Frees up device's pixelmaps.
// -----------------------------------------------------------------

void BrDemo_ReleasePixelmaps()
{
	 // Free pixelmaps
	
	if ( BrWinApp.depth_buffer )
	   BrPixelmapFree( BrWinApp.depth_buffer );
	
    if ( BrWinApp.back_buffer )	
       BrPixelmapFree( BrWinApp.back_buffer );
	
	if ( BrWinApp.screen )
	   BrPixelmapFree( BrWinApp.screen );

	// Clear pointers

	BrWinApp.depth_buffer = NULL;
	BrWinApp.back_buffer = NULL;
	BrWinApp.screen = NULL;
}



// -----------------------------------------------------------------
// Function : BrDemo_WindowProc
//
// Description : Windows message handling function.
// -----------------------------------------------------------------

   
static LRESULT CALLBACK BrDemo_WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
   int client_width, client_height;
	
   switch(message) 
   {
       case WM_COMMAND:
            BrDemo_HandleMenuSelection( &BrWinApp, LOWORD(wParam) );
			break ;
   
      case WM_CLOSE:
         BrDemo_Remove_Device();
         DestroyWindow(hWnd);
         PostQuitMessage(0);
         break ;


      case WM_LBUTTONDOWN: bLeftButtonDown = BR_TRUE ;
                           SetCapture(hWnd);
                           break ;

      case WM_RBUTTONDOWN: bRightButtonDown = BR_TRUE ;
                           SetCapture(hWnd);
                           break ;


      case WM_LBUTTONUP: bLeftButtonDown = BR_FALSE ;

                         if ( !bRightButtonDown)
                            ReleaseCapture();
                         break ;

      case WM_RBUTTONUP: bRightButtonDown = BR_FALSE ;

                         if ( !bLeftButtonDown)
                           ReleaseCapture();
                         break ;

      case WM_MOUSEMOVE : 
     
        BrWinApp.xMousePos = LOWORD(lParam);  // horizontal position of cursor 
        BrWinApp.yMousePos = HIWORD(lParam);  // vertical position of cursor 
 	    break ;

       case WM_SYSKEYDOWN :	 if ( wParam == VK_RETURN )
							 {
								 BrDemo_Toggle_Fullscreen(&BrWinApp);
								 return 0 ;
							 }
	   
	   case WM_KEYDOWN : BrWinRegisterKeyState( (br_uint_16) wParam, 1, (br_uint_16) (lParam&0xFFFF) );
	                     return 0 ;              
	                 

       case WM_KEYUP   : BrWinRegisterKeyState( (br_uint_16) wParam, 0, (br_uint_16) (lParam&0xFFFF) );
	                     return 0 ;
	                 
       case WM_PALETTECHANGED:
          if((HWND)wParam == hWnd)
             break;

       // Realise palette

       case WM_QUERYNEWPALETTE:
          if( BrWinApp.screen && BrWinApp.palette )
             BrPixelmapPaletteSet( BrWinApp.screen, BrWinApp.palette );
          break;

	   // Window size has changed

	   case WM_SIZE:

		   // Ignore message : On window creation,
		   // if we haven't initiallised yet, and when we are in
		   // full screen mode.

		if ( (lParam == 0) || !BrWinApp.bActive || BrWinApp.bFullScreen )
			break;

		// Extract client area dimensions

		client_width  = (int)LOWORD(lParam);
		client_height = (int)HIWORD(lParam);

		// Resize screen pixelmap

		if ( BrWinApp.screen )
			BrPixelmapResize( BrWinApp.screen, client_width, client_height );

		// If we're not doing a stretch blit, the offscreen and
		// Z buffer will need resizing too.
		
		if ( !BrWinApp.bStretch ) 
		{
			// Resize colour_buffer pixelmap

			if ( BrWinApp.back_buffer )
                BrPixelmapResize( BrWinApp.back_buffer, client_width, client_height );
	
		    // Resize depth pixelmap

		    if ( BrWinApp.depth_buffer )
			   BrPixelmapResize( BrWinApp.depth_buffer, client_width, client_height);
   	    }

	    BrDemo_OnResize();

   	    break;

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

   return 0;
}


// -----------------------------------------------------------------
// Function : BrWinError, BrWinWarning
//
// Description : BRender Windows error handler. 
// -----------------------------------------------------------------

static void BR_CALLBACK BrWinError(char *message)
{
    //OutputDebugString( message );
    MessageBox( NULL, message, BrWinApp.name, MB_OK );
    PostQuitMessage(0);
}


static void BR_CALLBACK BrWinWarning(char *message)
{
    //OutputDebugString( message );
    MessageBox( NULL, message, BrWinApp.name, MB_OK );
}

// Brender diagnostic handler that uses Windows

br_diaghandler BrWinErrorHandler = 
{
	"Windows BRender Error Handler",
	BrWinWarning,
	BrWinError, 
};



// -----------------------------------------------------------------
// Function : BrDemo_Generate_Valid_Materials_Callback
//
// Description : Makes sure that all loaded materials are valid on 
// the current device.
// -----------------------------------------------------------------

br_boolean BrDemo_Generate_Valid_Materials_Callback( br_material *pMat, void *user )
{
   if ( ( BrWinApp.back_buffer->type == BR_PMT_RGB_555 ) ||
        ( BrWinApp.back_buffer->type == BR_PMT_RGB_565 ) )
   {
      if ( pMat->colour_map )
      {
         if ( pMat->colour_map->type == BR_PMT_INDEX_8 )
         {
            pMat->flags |= BR_MATF_LIGHT ;
            BrMaterialUpdate( pMat, BR_MATU_ALL );
         }
      }
   }

   return 0 ;
}


// -----------------------------------------------------------------
// Function : BrDemo_Initiallise_BRender
//
// Description : Starts up BRender, initillises error handler,
// keyboard, initialises demo, and makes sure all materials are valid.
// -----------------------------------------------------------------


br_error BrDemo_Initiallise_BRender()
{     
   BrBegin();

   BrErrorHandlerSet(&BrWinErrorHandler);
 
   WinKeyBegin();
  
   // *********************************************************

   // To do : Simulate DOS argc and argv, then send to Init_App

   // *********************************************************
   

   if ( BrDemo_InitApp( argc, argv ) != BRE_OK )
   {
	   BR_WARNING0( "Could not initialize application" );
	   return BRE_FAIL ;
   }

   BrMaterialEnum( NULL, BrDemo_Generate_Valid_Materials_Callback, NULL  );
}


// -----------------------------------------------------------------
// Function : BrDemo_Toggle_Fullscreen
//
// Description : Toggles full screen mode on / off 
//   - via ALT-ENTER keypress.
// -----------------------------------------------------------------


br_error BrDemo_Toggle_Fullscreen( WIN_BITS_PTR pBrApp )
{
 	br_error r ;

	// Toggle fullscreen flag

	pBrApp->bFullScreen ^= 1 ;

	// Release existing pixelmaps
 
	BrDemo_ReleasePixelmaps(pBrApp);

	// Create new pixelmaps
			 
    r = BrDemo_Allocate_Pixelmaps_Int(pBrApp);
		  
	if ( r != BRE_OK )
	   return r ;

    // Let the app know we've resized.
	
	r = BrDemo_OnResize();

	// Check the appropriate device menu entries
	
	BrDemo_SetDeviceMenuChecks(pBrApp);

	return r ;
}


// -----------------------------------------------------------------
// Function : WinMain
//
// Description : It all starts here ...
// -----------------------------------------------------------------

int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPSTR lpCmdLine, int nCmdShow)

{
   MSG msg ;
   WNDCLASS wclass;
   br_error r ;

   BrWinApp.bActive = BR_FALSE ;
   BrWinApp.Offscreen_Height = 100 ;
   BrWinApp.Offscreen_Width = 100 ;
   BrWinApp.bStretch = BR_FALSE ;	 
   BrWinApp.Width = 0 ;
   BrWinApp.Height = 0 ;

   // Parse Command Line

   argv[0] = "DUMMY.EXE";
   argv[1] = strtok(lpCmdLine," ");
   argc = 2;
   
   if (argv[1]) {
      while (argv[argc] = strtok(NULL," ")) {
         argc++;
      }
   }
   else argc--;

   // This will be overriden in BrDemoOutputFacilityEnumCb if the
   // current device supports windowed mode.

   BrWinApp.bFullScreen = BR_TRUE ;
   
   // Set initial device to software only.
   
   BrWinApp.Current_Device = BR_DEMO_INITIAL_DEVICE ;

   // Register a new window class for BRender output.

   wclass.style = CS_HREDRAW | CS_VREDRAW;
   wclass.lpfnWndProc = BrDemo_WindowProc;
   wclass.cbClsExtra = 0;
   wclass.cbWndExtra = 0;
   wclass.hInstance = hInstance;
   wclass.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
   wclass.hCursor = LoadCursor(NULL, IDC_ARROW);
   wclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
   wclass.lpszMenuName = NULL;
   wclass.lpszClassName = "BrDemoClass";
   RegisterClass(&wclass);
 
   r = BrDemo_Initiallise_BRender();

   if ( r != BRE_OK )
	   return 0 ;

   // Redraw menu bar incase the app has added any items
   //  - Currently only done in Forest.

   DrawMenuBar( BrWinApp.hWnd );
   
   while (1)
   {
        if ( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) )
        {
            if( !GetMessage( &msg, NULL, 0, 0 ) )
            {
               return msg.wParam;
            }   

            TranslateMessage(&msg); 
            DispatchMessage(&msg);
        }

      if ( BrDemo_Render_Loop() != BRE_OK )
		  PostQuitMessage(0);

      BrDemo_DoTiming( &BrWinApp );
   }

   return 0 ;
}


// -----------------------------------------------------------------
// Function : WinMouseRead
//
// Description : Emulation of the DOSMouseRead call
// -----------------------------------------------------------------

br_error WinMouseRead( br_int_32 *mouse_x, br_int_32 *mouse_y, br_uint_32 *Buttons )
{
    static br_boolean bFirstTime = BR_TRUE ;
	static br_int_32 ox = 0, oy = 0 ;

	*Buttons = 0 ;
	
	if ( bLeftButtonDown )
		*Buttons |= BR_MSM_BUTTONL ;

	if ( bRightButtonDown )
		*Buttons |= BR_MSM_BUTTONR ;

	if ( bFirstTime )
	{
	   *mouse_x = 0 ;
	   *mouse_y = 0 ;
	   bFirstTime = BR_FALSE ;
	}
	else
	{
	   *mouse_x += BrWinApp.xMousePos - ox;
	   *mouse_y += BrWinApp.yMousePos - oy;
	}

	ox = BrWinApp.xMousePos ;
	oy = BrWinApp.yMousePos ;

	return BRE_OK ;
}


// -----------------------------------------------------------------
// Function : BrDemo_ResizeOffScreenBuffers
//
// Description : Resizes offscreen buffers, as a result of selecting
// one of the View->Stretch menu items.
// -----------------------------------------------------------------


br_error BrDemo_ResizeOffScreenBuffers( WIN_BITS_PTR pBrApp, 
                               br_uint_16 w, br_uint_16 h )
{
   if ( pBrApp->back_buffer )
	   BrPixelmapResize( pBrApp->back_buffer, w, h );

   if ( pBrApp->depth_buffer )
	   BrPixelmapResize( pBrApp->depth_buffer, w, h );

   BrDemo_OnResize();

   return BRE_OK ;
}

// -----------------------------------------------------------------
// Function : WinMouseReadAbsolute
//
// Description : Gets the absolute mouse coords.
// -----------------------------------------------------------------

void WinMouseReadAbsolute( br_pixelmap *screen, br_int_32 *mouse_x, br_int_32 *mouse_y )
{
 	*mouse_x = BrWinApp.xMousePos ;
	*mouse_y = BrWinApp.yMousePos ;
}

// -----------------------------------------------------------------
// Function : WinClockRead
//
// Description : Emulation of DOSClockRead.
// -----------------------------------------------------------------

br_uint_32 WinClockRead()
{
    LARGE_INTEGER This_Time ;
	
	QueryPerformanceCounter( &This_Time );
    
	return This_Time.LowPart ;
}

// -----------------------------------------------------------------
// Function : WinGetClockFreq
//
// Description : Gets frequency of processor's performance counter
//  - only works on Pentium and above.
// -----------------------------------------------------------------

br_uint_32 WinGetClockFreq()
{
	static br_boolean bFirstTime = BR_TRUE ;
	static LARGE_INTEGER Freq ;

	if ( bFirstTime )
    {
        bFirstTime = BR_FALSE ;
        QueryPerformanceFrequency( &Freq );
    }

	return Freq.LowPart ;

}


// -----------------------------------------------------------------
// Function : BrDemo_SetRootActor
//
// Description : Sets the root actor used by some of the Options menu
// callbacks.
// -----------------------------------------------------------------

br_error BrDemo_SetRootActor( br_actor *pActor )
{
   BrWinApp.pRoot = pActor ;
   return BRE_OK ;
}
