//////////////////////////////////////////////////////////////////////
//
// Project  : BRender Demo Framework
//            Copyright (c) 1996 Argonaut Technologies Ltd.
//            All rights reserved.
//
// Author   : Steve Williams, October 1996
//
// File     : MENUBITS.C
//
// Requires : BRender V1.3
//            Microsoft DirectX 3, Win95/NT
//
// Comments : Menu handling under Windows.
//
//////////////////////////////////////////////////////////////////////

#include "br_demo.h"
#include "resource.h"

// Don't use any resource IDs between DEVICE_BASE and 
// DEVICE_BASE + MAX_DEVICES*MAX_SCREEN_MODES_PER_DEVICE.
// They are used for the screen modes available under the devices menu.

#define DEVICE_BASE 700

extern char BrDemo_Device_Name[MAX_DEVICES][20];

// Handle to demo parent menu

static HMENU hBrDemoMenu ;

// Handle to Device sub-menu

HMENU hDeviceMenu ;

// Handle to the current device's sub menu.

static HMENU hDeviceModesMenu ; 

// Handle to the special menu, which contains demo specific options,
// currently only used by Forest.

static HMENU hSpecialMenu ;


// Num of BRender devices we have found
static br_int_32 Num_Devices = 0 ;

// Number of display modes found for the current device
static br_uint_16 Num_Display_Modes = 0 ;

// Used for keeping track of ticks in the Device menu.

static br_uint_32 Current_FullScreen_ID = 0;
static br_uint_32 Current_Windowed_ID = 0 ;


// Demo specific menu handling
// ---------------------------
// Only one demo currently uses menu callbacks - Forest,
// for toggling between small and large textures.

#define USER_OPTIONS_BASE 400
#define MAX_USER_OPTIONS 20
static br_uint_16 NumUserMenuItems = 0 ;
static BRDEMO_MENU_CALLBACK UserCB[MAX_USER_OPTIONS];

// The following are just a couple of 'fun' callbacks to allow
// some interaction in the demos.

static br_uint_32 BR_CALLBACK ToggleTextures ( br_material *pMat, void *arg );
static br_uint_32 BR_CALLBACK SetActorRenderStyleCallback(br_actor *a, void *arg );
static br_uint_32 BR_CALLBACK ToggleMaterialSwitch( br_material *pMat, void *arg );

// -----------------------------------------------------------------
// Function : Set_Default_Screen_Mode 
//
// Description : Chooses most appropriate initial full screen mode.
// -----------------------------------------------------------------

void Set_Default_Screen_Mode( br_int_32 width, br_int_32 height, br_int_32 bpp, br_uint_32 id )
{
	// Vars for holding default full screen mode

	static br_int_32 Default_Width = 0 ;
	static br_int_32 Default_Height = 0 ;
	static br_int_32 Default_BPP = 0 ;
	
	if ( ( Default_Width == 0 ) || ( bpp < Default_BPP ) ||
		 ( ( bpp == Default_BPP ) && width < Default_Width ) )
	{
		Default_Width = width ;
		Default_Height = height ;
		Default_BPP = bpp ;
		Current_FullScreen_ID = id ;
	}
}


// -----------------------------------------------------------------
// Function : OutputFacilityEnumCb
//
// Description : Enumerates available output facilities on the current
// device, and adds the description to the appropriate menu. Creates
// sub-menus where required.
// -----------------------------------------------------------------


br_boolean BR_CALLBACK BrDemoOutputFacilityEnumCb(char *identifier,
    br_outfcty_desc *desc, void *args)
{
    char MenuText[100];
	static HMENU h8BitMenu, h15BitMenu, h16BitMenu, h24BitMenu ;
	HMENU *pMenu ;
	br_uint_16 id ;
	WIN_BITS_PTR pBrApp = (WIN_BITS_PTR) args ;
	
	id = DEVICE_BASE+Num_Devices*MAX_SCREEN_MODES_PER_DEVICE+Num_Display_Modes ;
	
	if ( !desc->fullscreen )
	{
		 BrSprintf( MenuText, "Windowed ( %d bpp )", desc->pmbits );

		 pBrApp->Windowed_BPP = (br_uint_8) desc->pmbits ;

		 AppendMenu( hDeviceModesMenu, MF_STRING ,id, MenuText );

		 if ( Num_Devices == pBrApp->Current_Device )
		 {
			 Current_Windowed_ID = id ;
			 
			 // If current device supports windowed mode, set this
			 // as the initial mode.
			 
			 pBrApp->bFullScreen = BR_FALSE ;
		 }
	}
	else
	{
		BrSprintf( MenuText, "%dx%dx%d", desc->width, desc->height, desc->pmbits );
	
		if ( Num_Display_Modes == 0 )
		{
			h8BitMenu = NULL ;
			h15BitMenu = NULL ;
			h16BitMenu = NULL ;
			h24BitMenu = NULL ;
			hDeviceModesMenu = CreateMenu();
		}
		
		switch( desc->pmbits )
		{
		   case 8 : pMenu = &h8BitMenu ; break ;
		   case 15 : pMenu = &h15BitMenu ; break ;
		   case 16 : pMenu = &h16BitMenu ; break ;
		   case 24 : pMenu = &h24BitMenu ; break ;
		   default : return 0 ;  // Ignore any other modes, we don't support them.
		}

		if ( *pMenu == NULL )
		{
			char MenuName[30];

			BrSprintf( MenuName, "%d bits per pixel", desc->pmbits );
			*pMenu = CreateMenu();
			AppendMenu( hDeviceModesMenu, MF_POPUP, (unsigned int) *pMenu, MenuName );
		}

		if ( Num_Devices == pBrApp->Current_Device )
		   Set_Default_Screen_Mode( desc->width, desc->height, desc->pmbits, id );
		
		AppendMenu( *pMenu, MF_STRING , (unsigned int) id, MenuText );
	}
	
	Num_Display_Modes ++ ;

	if ( Num_Display_Modes == MAX_SCREEN_MODES_PER_DEVICE )
		return 1 ;

	return 0 ;
}


// -----------------------------------------------------------------
// Function : BrDemoDeviceCallback
//
// Description : Enumerates available BRender devices, creating a
// sub menu for this devices, and enumerates output facilities 
// the device ( which in turn adds sub menus to hDeviceMenu ). 
// -----------------------------------------------------------------


br_boolean BR_CALLBACK BrDemoDeviceCallback( char *identifier,
    br_uint_32 version, char *creator, char *title, char *product,
    char *product_version, void *args)
{
	char *name ;
	
	if ( title )
		name = title ;
	else
		name = "Not specified" ;

	strncpy( BrDemo_Device_Name[Num_Devices], identifier, 20 );

	Num_Display_Modes = 0 ;
	
	// Query device for modes supported, these are added to hDeviceModesMenu
	
	BrOutputFacilityEnum( identifier, BrDemoOutputFacilityEnumCb, args );

	// Add hDeviceModesMenu submenu to current device submenu.

	InsertMenu( hDeviceMenu, Num_Devices, MF_BYPOSITION|MF_POPUP,
                (unsigned) hDeviceModesMenu, name );
	
	Num_Devices ++ ;
	
	return 0 ;
}


// -----------------------------------------------------------------
// Function : BrDemo_Attach_Menu
//
// Description : Attaches the demo menu to the app's window. Queries
// BRender devices available in order to generate the Device menu.
// -----------------------------------------------------------------

br_error BrDemo_Attach_Menu( WIN_BITS_PTR pBrApp )
{
	char String[50];
	int w,h,bpp ;
	
	hBrDemoMenu = LoadMenu( GetModuleHandle(NULL), MAKEINTRESOURCE(IDR_BR_DEMO_MENU) );
    
	if ( !hBrDemoMenu )
    {
		BR_ERROR0("Could not load BRender demo framework menu.");
		return BRE_FAIL ;
	}
	
	SetMenu( pBrApp->hWnd, hBrDemoMenu );

	// Create Device sub-menu ...
	
	hDeviceMenu = CreateMenu();

	InsertMenu( hBrDemoMenu, 1, MF_BYPOSITION|MF_POPUP,
                (unsigned) hDeviceMenu, "&Device" );
	
	// Query BRender for devices available. This adds devices and
	// screen modes to the Device menu.

	BrDeviceEnum( BrDemoDeviceCallback, (void*) pBrApp );

	if ( Num_Devices == 0 )
	{
		BR_ERROR0("Could not load any BRender devices. Cannot continue.");
		return BRE_FAIL ;
	}

	// Tick initial device.
	
	if ( Current_Windowed_ID )
		CheckMenuItem( hBrDemoMenu, Current_Windowed_ID, MF_CHECKED );
	else
		CheckMenuItem( hBrDemoMenu, Current_FullScreen_ID, MF_CHECKED );

	GetMenuString( hBrDemoMenu, Current_FullScreen_ID,	
                   String, 50, MF_BYCOMMAND );

	// Get current full screen mode settings from menu string

    BrSScanf( String, "%dx%dx%d", &w, &h, &bpp );
	pBrApp->Width = w ;
	pBrApp->Height = h;
	pBrApp->Fullscreen_BPP = bpp ;
	
	return BRE_OK ;
}


// -----------------------------------------------------------------
// Function : BrDemo_SetDeviceMenuChecks
//
// Description : Checks / unchecks the current full screen and
//               and windowed selections appropriatly.
// -----------------------------------------------------------------

br_error BrDemo_SetDeviceMenuChecks( WIN_BITS_PTR pBrApp )
{
   if ( pBrApp->bFullScreen )
   {
       CheckMenuItem( hBrDemoMenu, Current_Windowed_ID, MF_UNCHECKED );
	   CheckMenuItem( hBrDemoMenu, Current_FullScreen_ID, MF_CHECKED );
   }
   else
   {
       CheckMenuItem( hBrDemoMenu, Current_Windowed_ID, MF_CHECKED );
	   CheckMenuItem( hBrDemoMenu, Current_FullScreen_ID, MF_UNCHECKED );
   }

   return BRE_OK ;
}


// -----------------------------------------------------------------
// Function : BrDemoMenu_Select_Device
//
// Description : Handles selection of devices from the menu.
// -----------------------------------------------------------------


br_error BrDemoMenu_Select_Device( WIN_BITS_PTR pBrApp, br_uint_32 Menu_ID )
{
	char String[50];
	int w, h, new_bpp, old_bpp ;
	br_error r ;
	br_uint_8 Selected_Device = (br_uint_8) (Menu_ID - DEVICE_BASE ) / MAX_SCREEN_MODES_PER_DEVICE ;
	static br_uint_32 Last_Menu_ID ;
 	static br_boolean bFirstTime = BR_TRUE ;

	GetMenuString( hBrDemoMenu, Menu_ID, String, 50, MF_BYCOMMAND );

   if ( pBrApp->bFullScreen )
   	   old_bpp = pBrApp->Windowed_BPP ;
	else
	   old_bpp = pBrApp->Fullscreen_BPP ;
	
	// Get new mode dimensions from setup string

	if ( !BrStrNICmp( String, "Window", 6 ) )
	{    
		pBrApp->bFullScreen = BR_FALSE ;
		Current_Windowed_ID = Menu_ID ;
	    new_bpp = pBrApp->Windowed_BPP  ;
	}
	else
	{
		BrSScanf( String, "%dx%dx%d", &w, &h, &new_bpp );
		pBrApp->Width = w ;
		pBrApp->Height = h;
		pBrApp->Fullscreen_BPP = new_bpp ;
		pBrApp->bFullScreen = BR_TRUE ;
		Current_FullScreen_ID = Menu_ID ;
	}

   // If the new mode is on the same device and has the same bit depth,
   // we only need to reallocate the pixelmaps, otherwise, we must restart 
   // the application.

   if ( ( pBrApp->Current_Device == Selected_Device ) && ( old_bpp == new_bpp ) )
   {
	   // If we are changing between fullscreen and windowed
      // modes, we must reallocate the pixelmaps, because they
	   // may be internally of different types.
		   
	   // 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();
 
      if ( r != BRE_OK )
  	   {
          BR_ERROR0("Error occurred in app resize!");
          return r ;
	   }
   }
   else
   {
      // Set new device

   	pBrApp->Current_Device = Selected_Device ;

   	// Remove old device. and kill app

   	BrDemo_Remove_Device(pBrApp);
   	BrDemo_ClearAppMenuItems();

   	// Restart app using new device.

      r = BrDemo_Initiallise_BRender();

      if ( r != BRE_OK )
  		{
         BR_ERROR0("Could not start device !");
         return r ;
      }
   }

   // Uncheck last device

  	if ( !bFirstTime )
		CheckMenuItem( hBrDemoMenu, Last_Menu_ID, MF_UNCHECKED );
	else
	   bFirstTime = BR_FALSE ;
		
	Last_Menu_ID = Menu_ID ;

   BrDemo_SetDeviceMenuChecks( pBrApp );
	
	return r ;
}


// -----------------------------------------------------------------
// Function : BrDemo_HandleMenuSelection
//
// Description : Handles user menu item selection.
// -----------------------------------------------------------------


br_error BrDemo_HandleMenuSelection( WIN_BITS_PTR pBrApp, br_uint_16 Menu_Selection )
{
	br_error r ;

	// Handle user specific options
	
	if ( ( Menu_Selection >= USER_OPTIONS_BASE ) && ( Menu_Selection <= USER_OPTIONS_BASE + MAX_USER_OPTIONS ) )
	{
	    if ( UserCB[Menu_Selection-USER_OPTIONS_BASE] )
			r = UserCB[Menu_Selection-USER_OPTIONS_BASE]();

		return r ;
	}

    else if ( ( Menu_Selection >= DEVICE_BASE ) && (Menu_Selection < ( DEVICE_BASE + MAX_SCREEN_MODES_PER_DEVICE * MAX_DEVICES ) ) )
	{
	    r = BrDemoMenu_Select_Device( pBrApp, Menu_Selection );
	    return r ;
	}

	else if ( Menu_Selection == ID_VIEW_NORMAL )
	{
		pBrApp->bStretch = BR_TRUE ;
		BrDemo_ResizeOffScreenBuffers( pBrApp, pBrApp->screen->width, pBrApp->screen->height );
	}
	else if ( Menu_Selection == ID_STRETCH100X100 )
	{
		pBrApp->bStretch = BR_TRUE ;
       	BrDemo_ResizeOffScreenBuffers( pBrApp, 100, 100 );
	}
	else if ( Menu_Selection == ID_STRETCH200X200 )
   	{
		pBrApp->bStretch = BR_TRUE ;
	   	BrDemo_ResizeOffScreenBuffers( pBrApp, 200, 200 );
	}
    else if ( Menu_Selection == ID_STRETCH300X300 )
    {
		pBrApp->bStretch = BR_TRUE ;
		BrDemo_ResizeOffScreenBuffers( pBrApp, 300, 300 );
	}
    else if ( Menu_Selection == ID_FILE_EXIT )
	{
		BrDemo_Remove_Device();
        DestroyWindow(pBrApp->hWnd);
        PostQuitMessage(0);
	}
	else if ( Menu_Selection == IDR_HELP  )
	{
		MessageBox( NULL, "Press ALT-ENTER to toggle windowed/full screen", "BRender Demo Framework", MB_OK );
	}

	else if ( ( Menu_Selection == ID_FEATURES_SOLID ) && pBrApp->pRoot )
	{
		BrActorEnum( pBrApp->pRoot, SetActorRenderStyleCallback, (void*) BR_RSTYLE_FACES ); 
		CheckMenuItem( hBrDemoMenu, ID_FEATURES_SOLID, MF_CHECKED );
		CheckMenuItem( hBrDemoMenu, ID_FEATURES_WIREFRAME, MF_UNCHECKED );
		CheckMenuItem( hBrDemoMenu, ID_FEATURES_POINTS, MF_UNCHECKED );
	}
		
	else if ( ( Menu_Selection == ID_FEATURES_WIREFRAME ) && pBrApp->pRoot )
	{
		BrActorEnum( pBrApp->pRoot, SetActorRenderStyleCallback, (void*) BR_RSTYLE_EDGES ); 
	    CheckMenuItem( hBrDemoMenu, ID_FEATURES_SOLID, MF_UNCHECKED );
		CheckMenuItem( hBrDemoMenu, ID_FEATURES_WIREFRAME, MF_CHECKED );
		CheckMenuItem( hBrDemoMenu, ID_FEATURES_POINTS, MF_UNCHECKED );
	}
	else if ( ( Menu_Selection == ID_FEATURES_POINTS ) && pBrApp->pRoot )
	{
		BrActorEnum( pBrApp->pRoot, SetActorRenderStyleCallback, (void*) BR_RSTYLE_POINTS ); 
		CheckMenuItem( hBrDemoMenu, ID_FEATURES_SOLID, MF_UNCHECKED );
		CheckMenuItem( hBrDemoMenu, ID_FEATURES_WIREFRAME, MF_UNCHECKED );
		CheckMenuItem( hBrDemoMenu, ID_FEATURES_POINTS, MF_CHECKED );
	}
    else if ( Menu_Selection == ID_FEATURES_BILINEAR )
	{
		 BrMaterialEnum( NULL, ToggleMaterialSwitch, (void*) BR_MATF_MAP_INTERPOLATION );
    } 
	else if ( Menu_Selection == ID_FEATURES_TWO_SIDED )
	{
		 BrMaterialEnum( NULL, ToggleMaterialSwitch, (void*) BR_MATF_TWO_SIDED );
    } 
	else if ( Menu_Selection == ID_FEATURES_TEXTURE_MAPPING )
	{
		static bEnabled = BR_TRUE ;
		
		bEnabled ^= 1;
		BrMaterialEnum( NULL, ToggleTextures, (void*) bEnabled );
    } 

	return BRE_OK ;
}


// -----------------------------------------------------------------
// Function : BrDemo_ClearAppMenuItems
//
// Description : Tidied up menu ready for restart.
// -----------------------------------------------------------------

br_error BrDemo_ClearAppMenuItems()
{
 	int i ;
	
	// Remove user menu options
	
	for ( i = 0 ; i < NumUserMenuItems ; i ++ )
	{
	   RemoveMenu( hBrDemoMenu, USER_OPTIONS_BASE+i, MF_BYCOMMAND );
	}

	NumUserMenuItems = 0 ;

    return BRE_OK ;
}


// -----------------------------------------------------------------
// Function : BrDemo_AddMenuItem
//
// Description : Adds demo specific option to the Special menu.
//               Records a callback function which is called when
//               that menu option is selected.
// -----------------------------------------------------------------


br_error BrDemo_AddMenuItem( char *Name, BRDEMO_MENU_CALLBACK pCallback )
{
	static BOOL bFirstTime = BR_TRUE ;
	
	if ( bFirstTime )
	{
		hSpecialMenu = CreateMenu();

	    InsertMenu( hBrDemoMenu, 4, MF_BYPOSITION|MF_POPUP,
                    (unsigned) hSpecialMenu, "&Special" );
        bFirstTime = BR_FALSE;
	}
	
	AppendMenu(	hSpecialMenu, MF_STRING , USER_OPTIONS_BASE+NumUserMenuItems, Name );

	UserCB[NumUserMenuItems] = pCallback ;

	if ( NumUserMenuItems > MAX_USER_OPTIONS )
	{
		BR_ERROR0("Too many user options");
		return BRE_FAIL ;
	}
	else
	   NumUserMenuItems++ ;

    return BRE_OK ;
}



// -----------------------------------------------------------------
// Function : SetActorRenderStyleCallback
//
// Description : Sets current actor and it's children's render style
// to specified style. 
// -----------------------------------------------------------------


static br_uint_32 BR_CALLBACK SetActorRenderStyleCallback(br_actor *a, void *arg )
{
	a->render_style = (br_uint_8) arg ;

	return BrActorEnum(a,SetActorRenderStyleCallback,arg);
}


// -----------------------------------------------------------------
// Function : ToggleMaterialSwitch
//
// Description : Toggles specified material flag on all materials.
// -----------------------------------------------------------------
  

static br_uint_32 BR_CALLBACK ToggleMaterialSwitch( br_material *pMat, void *arg )
{
   br_uint_32 Flag = (br_uint_32) arg ;

   pMat->flags ^= Flag ;	
   BrMaterialUpdate( pMat, BR_MATU_ALL );

   return 0 ;
}


// -----------------------------------------------------------------
// Function : ToggleTexture
//
// Description : Toggles texture on/off on all materials.
// -----------------------------------------------------------------

static br_uint_32 BR_CALLBACK ToggleTextures ( br_material *pMat, void *arg )
{
   br_boolean Flag = (br_boolean) arg ;

   if ( !Flag )
   {
	   if ( pMat->user != NULL )
	   {
		   BR_ERROR0("Cannot perform operation, app uses material 'user' field");
	   	   return 1 ;
	   }

	   pMat->user = pMat->colour_map ;
	   pMat->colour_map = NULL ;
   }
   else
   {
   	   pMat->colour_map  = pMat->user ;
	   pMat->user = NULL ;
   }

   BrMaterialUpdate( pMat, BR_MATU_ALL );

   return 0 ;
}


