//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
//=============================================================================//
#include "ControlPanel.h"
#include "ViewerSettings.h"
#include "StudioModel.h"
#include "IStudioRender.h"
#include "MatSysWin.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mxtk/mx.h>
#include <mxtk/mxBmp.h>
#include "FlexPanel.h"
#include "mxExpressionTray.h"
#include "PhonemeEditor.h"
#include "hlfaceposer.h"
#include "expclass.h"
#include "mxExpressionTab.h"
#include "ExpressionTool.h"
#include "MDLViewer.h"
#include "choreowidgetdrawhelper.h"
#include "faceposer_models.h"
#include "ifaceposerworkspace.h"
#include "choreoview.h"
#include "GestureTool.h"
#include "RampTool.h"
#include "SceneRampTool.h"
#include "phonemeextractor/PhonemeExtractor.h"
#include "tier1/KeyValues.h"


// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
extern char g_appTitle[];

ControlPanel *g_pControlPanel = 0;

//-----------------------------------------------------------------------------
// Purpose: A simple subclass so we can paint the window background
//-----------------------------------------------------------------------------
class CControlPanelTabWindow : public mxWindow
{
public:
	CControlPanelTabWindow( mxWindow *parent, int x, int y, int w, int h ) :
	  mxWindow( parent, x, y, w, h )
	  {
		  FacePoser_AddWindowStyle( this, WS_CLIPSIBLINGS | WS_CLIPCHILDREN );
	  };
	  
	  virtual bool PaintBackground( void )
	  {
		  CChoreoWidgetDrawHelper drawHelper( this );
		  
		  RECT rc;
		  drawHelper.GetClientRect( rc );
		  
		  drawHelper.DrawFilledRect( GetSysColor( COLOR_BTNFACE ), rc );
		  return false;
	  }
};

ControlPanel::ControlPanel (mxWindow *parent)
: IFacePoserToolWindow( "ControlPanel", "Control Panel" ), mxWindow( parent, 0, 0, 0, 0 ), tab( 0 )
{
	// create tabcontrol with subdialog windows
	tab = new mxTab (this, 0, 0, 0, 0, IDC_TAB);

	CControlPanelTabWindow *wRender = new CControlPanelTabWindow (this, 0, 0, 0, 0);
	tab->add (wRender, "Render");
	cRenderMode = new mxChoice (wRender, 5, 5, 100, 22, IDC_RENDERMODE);
	cRenderMode->add ("Wireframe");
	cRenderMode->add ("Flatshaded");
	cRenderMode->add ("Smoothshaded");
	cRenderMode->add ("Textured");
	cRenderMode->select (3);
	mxToolTip::add (cRenderMode, "Select Render Mode");

	slModelGap = new mxSlider( wRender, 220, 5, 140, 20, IDC_MODELSPACING );
	slModelGap->setRange( 0.0f, 64.0f, 256 );
	slModelGap->setValue( 16 );
	mxToolTip::add (slModelGap, "Select Model Spacing" );
	new mxLabel (wRender, 220, 25, 140, 20, "Model Spacing");

	cbAllWindowsDriveSpeech = new mxCheckBox( wRender, 220, 45, 140, 20, "All tools drive mouth", IDC_TOOLSDRIVEMOUTH );
	cbAllWindowsDriveSpeech->setChecked( g_viewerSettings.faceposerToolsDriveMouth );

	cbGround = new mxCheckBox (wRender, 110, 5, 100, 20, "Ground", IDC_GROUND);
	cbGround->setEnabled( true );
	cbMovement = new mxCheckBox (wRender, 110, 25, 100, 20, "Movement", IDC_MOVEMENT);
	cbMovement->setEnabled( false );
	cbBackground = new mxCheckBox (wRender, 110, 45, 100, 20, "Background", IDC_BACKGROUND);
	cbBackground->setEnabled( false );
	new mxCheckBox (wRender, 110, 65, 100, 20, "Hit Boxes", IDC_HITBOXES);
	new mxCheckBox (wRender, 5, 65, 100, 20, "Bones", IDC_BONES);
	mxCheckBox *cbAttachments = new mxCheckBox (wRender, 5, 45, 100, 20, "Attachments", IDC_ATTACHMENTS);
	cbAttachments->setEnabled( false );

	CControlPanelTabWindow *wSequence = new CControlPanelTabWindow (this, 0, 0, 0, 0);
	tab->add (wSequence, "Sequence");
	cSequence = new mxChoice (wSequence, 5, 5, 200, 22, IDC_SEQUENCE);	
	mxToolTip::add (cSequence, "Select Sequence");


	slSpeedScale = new mxSlider (wSequence, 5, 32, 200, 18, IDC_SPEEDSCALE);
	slSpeedScale->setRange (0.0, 5.0 );
	slSpeedScale->setValue (0.0);
	mxToolTip::add (slSpeedScale, "Speed Scale");
	lSpeedScale = new mxLabel( wSequence, 5, 50, 200, 18 );
	lSpeedScale->setLabel( "Speed scale" );

	CControlPanelTabWindow *wBody = new CControlPanelTabWindow (this, 0, 0, 0, 0);
	tab->add (wBody, "Body");
	cBodypart = new mxChoice (wBody, 5, 5, 100, 22, IDC_BODYPART);
	mxToolTip::add (cBodypart, "Choose a bodypart");
	cSubmodel = new mxChoice (wBody, 110, 5, 100, 22, IDC_SUBMODEL);
	mxToolTip::add (cSubmodel, "Choose a submodel of current bodypart");
	cController = new mxChoice (wBody, 5, 30, 100, 22, IDC_CONTROLLER);	
	mxToolTip::add (cController, "Choose a bone controller");
	slController = new mxSlider (wBody, 105, 32, 100, 18, IDC_CONTROLLERVALUE);
	slController->setRange (0, 45);
	mxToolTip::add (slController, "Change current bone controller value");
	lModelInfo1 = new mxLabel (wBody, 220, 5, 120, 100, "No Model.");
	lModelInfo2 = new mxLabel (wBody, 340, 5, 120, 100, "");
	cSkin = new mxChoice (wBody, 5, 55, 100, 22, IDC_SKINS);
	mxToolTip::add (cSkin, "Choose a skin family");
}

ControlPanel::~ControlPanel()
{
}

bool ControlPanel::CanClose( void )
{
	workspacefiles->StartStoringFiles( IWorkspaceFiles::EXPRESSION );
	for ( int i = 0 ; i < expressions->GetNumClasses(); i++ )
	{
		CExpClass *cl = expressions->GetClass( i );
		if ( cl )
		{
			workspacefiles->StoreFile( IWorkspaceFiles::EXPRESSION, cl->GetFileName() );
		}
	}
	workspacefiles->FinishStoringFiles( IWorkspaceFiles::EXPRESSION );
	// Now close them all, or abort exit if user doesn't want to close any that have changed
	return Closeall();
}

void ControlPanel::OnDelete()
{
}

void ControlPanel::PositionControls( int width, int height )
{
	if ( tab )
	{
		tab->setBounds( 0, GetCaptionHeight(), width, height );
	}
}

void ControlPanel::redraw()
{
	if ( !ToolCanDraw() )
		return;

	CChoreoWidgetDrawHelper helper( this, GetSysColor( COLOR_BTNFACE ) );
	HandleToolRedraw( helper );

	BaseClass::redraw();
}

int
ControlPanel::handleEvent (mxEvent *event)
{
	MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );

	int iret = 0;
	
	if ( HandleToolEvent( event ) )
	{
		return iret;
	}

	switch ( event->event )
	{
	case mxEvent::Size:
		{
			PositionControls( event->width, event->height );
			iret = 1;
		}
		break;
	case mxEvent::Action:
		{
			iret = 1;
			switch (event->action)
			{
			case IDC_TOOLSDRIVEMOUTH:
				{
					g_viewerSettings.faceposerToolsDriveMouth = ((mxCheckBox *)event->widget)->isChecked();
				}
				break;
			case IDC_TAB:
				{
					g_viewerSettings.showTexture = (tab->getSelectedIndex() == 3);
				}
				break;
				
			case IDC_RENDERMODE:
				{
					int index = cRenderMode->getSelectedIndex();
					if (index >= 0)
					{
						setRenderMode (index);
					}
				}
				break;
				
			case IDC_GROUND:
				setShowGround (((mxCheckBox *) event->widget)->isChecked());
				break;
				
			case IDC_MOVEMENT:
				setShowMovement (((mxCheckBox *) event->widget)->isChecked());
				break;
				
			case IDC_BACKGROUND:
				setShowBackground (((mxCheckBox *) event->widget)->isChecked());
				break;
				
			case IDC_HITBOXES:
				g_viewerSettings.showHitBoxes = ((mxCheckBox *) event->widget)->isChecked();
				break;
				
			case IDC_PHYSICSMODEL:
				g_viewerSettings.showPhysicsModel = ((mxCheckBox *) event->widget)->isChecked();
				break;
				
			case IDC_BONES:
				g_viewerSettings.showBones = ((mxCheckBox *) event->widget)->isChecked();
				break;
				
			case IDC_ATTACHMENTS:
				g_viewerSettings.showAttachments = ((mxCheckBox *) event->widget)->isChecked();
				break;
				
			case IDC_SEQUENCE:
				{
					int index = cSequence->getSelectedIndex();
					if (index >= 0)
					{
						setSequence ( index );
					}
				}
				break;
			
			case IDC_SPEEDSCALE:
				{
					g_viewerSettings.speedScale = ((mxSlider *) event->widget)->getValue();
					lSpeedScale->setLabel( va( "Speed scale %.2f", g_viewerSettings.speedScale ) );
				}
				break;
				
			case IDC_PRIMARYBLEND:
				{
					setBlend( 0, ((mxSlider *) event->widget)->getValue() );
				}
				break;
				
			case IDC_SECONDARYBLEND:
				{
					setBlend( 1, ((mxSlider *) event->widget)->getValue() );
				}
				break;
				
			case IDC_BODYPART:
				{
					int index = cBodypart->getSelectedIndex();
					if (index >= 0)
					{
						setBodypart (index);
						
					}
				}
				break;
				
			case IDC_SUBMODEL:
				{
					int index = cSubmodel->getSelectedIndex();
					if (index >= 0)
					{
						setSubmodel (index);
						
					}
				}
				break;
				
			case IDC_CONTROLLER:
				{
					int index = cController->getSelectedIndex();
					if (index >= 0)
						setBoneController (index);
				}
				break;
				
			case IDC_CONTROLLERVALUE:
				{
					int index = cController->getSelectedIndex();
					if (index >= 0)
						setBoneControllerValue (index, slController->getValue());
				}
				break;
				
			case IDC_SKINS:
				{
					int index = cSkin->getSelectedIndex();
					if (index >= 0)
					{
						models->GetActiveStudioModel()->SetSkin (index);
						g_viewerSettings.skin = index;
						g_pMatSysWindow->redraw();
					}
				}
				break;
			default:
				iret = 0;
				break;
			}
		}
	}

	return iret;
}

void ControlPanel::dumpModelInfo() { }

void ControlPanel::ChangeModel( const char *filename )
{
	HCURSOR hPrevCursor = SetCursor( LoadCursor( NULL, IDC_WAIT ) );

	// init all the selection tabs based on the current model
	initSequenceChoices();
	initBodypartChoices();
	initBoneControllerChoices();
	initSkinChoices();

	setModelInfo();

	SetCloseCaptionLanguageId( g_viewerSettings.cclanguageid, true );

	g_viewerSettings.m_iEditAttachment = -1;

	g_viewerSettings.enableIK = true;
	g_viewerSettings.enableTargetIK = false;

	setSequence( models->GetActiveStudioModel()->GetSequence() );
	setSpeed( g_viewerSettings.speedScale );

	mx_setcwd (mx_getpath (filename));

	g_pFlexPanel->initFlexes();
	
	//	centerView();
	//	CenterOnFace();

	IFacePoserToolWindow::ModelChanged();
	
	CExpClass *cl = expressions->GetActiveClass();
	if ( cl )
	{
		cl->SelectExpression( cl->GetSelectedExpression() );
	}

	SetSuffix( va( " - %s.mdl", models->GetActiveModelName() ) );
	redraw();

	SetCursor( hPrevCursor );
}



void
ControlPanel::setRenderMode (int mode)
{
	g_viewerSettings.renderMode = mode;
	g_pMatSysWindow->redraw();
}


void 
ControlPanel::setHighlightBone( int index )
{
	g_viewerSettings.highlightPhysicsBone = index;
}

void
ControlPanel::setShowGround (bool b)
{
	g_viewerSettings.showGround = b;
	cbGround->setChecked (b);
}



void
ControlPanel::setShowMovement (bool b)
{
	g_viewerSettings.showMovement = b;
	cbMovement->setChecked (b);
}



void
ControlPanel::setShowBackground (bool b)
{
	g_viewerSettings.showBackground = b;
	cbBackground->setChecked (b);
}



void
ControlPanel::initSequenceChoices()
{
	CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
	if (hdr)
	{
		cSequence->removeAll();
		for (int i = 0; i < hdr->GetNumSeq(); i++)
		{
			cSequence->add (hdr->pSeqdesc(i).pszLabel());
		}

		cSequence->select (0);
	}
}



void
ControlPanel::setSequence (int index)
{
	cSequence->select (index);
	models->GetActiveStudioModel()->SetSequence(index);

	initPoseParameters( );
}


void
ControlPanel::setSpeed( float value )
{
	g_viewerSettings.speedScale = value;
	slSpeedScale->setValue( value );
}


void ControlPanel::setBlend(int index, float value )
{
	models->GetActiveStudioModel()->SetPoseParameter( index, value );
}


void
ControlPanel::initBodypartChoices()
{
	CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
	if (hdr)
	{
		int i;
		mstudiobodyparts_t *pbodyparts = hdr->pBodypart(0);

		cBodypart->removeAll();
		if (hdr->numbodyparts() > 0)
		{
			for (i = 0; i < hdr->numbodyparts(); i++)
				cBodypart->add (pbodyparts[i].pszName());

			cBodypart->select (0);

			cSubmodel->removeAll();
			for (i = 0; i < pbodyparts[0].nummodels; i++)
			{
				char str[64];
				sprintf (str, "Submodel %d", i + 1);
				cSubmodel->add (str);
			}
			cSubmodel->select (0);
		}
	}
}



void
ControlPanel::setBodypart (int index)
{
	CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
	if (hdr)
	{
		//cBodypart->setEn
		cBodypart->select (index);
		if (index < hdr->numbodyparts())
		{
			mstudiobodyparts_t *pbodyparts = hdr->pBodypart(0);
			cSubmodel->removeAll();
		
			for (int i = 0; i < pbodyparts[index].nummodels; i++)
			{
				char str[64];
				sprintf (str, "Submodel %d", i + 1);
				cSubmodel->add (str);
			}
			cSubmodel->select (0);
			//models->GetActiveStudioModel()->SetBodygroup (index, 0);
		}
	}
}



void
ControlPanel::setSubmodel (int index)
{
	models->GetActiveStudioModel()->SetBodygroup (cBodypart->getSelectedIndex(), index);
}



void
ControlPanel::initBoneControllerChoices()
{
	CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
	if (hdr)
	{
		cController->setEnabled (hdr->numbonecontrollers() > 0);
		slController->setEnabled (hdr->numbonecontrollers() > 0);
		cController->removeAll();

		for (int i = 0; i < hdr->numbonecontrollers(); i++)
		{
			mstudiobonecontroller_t *pbonecontroller = hdr->pBonecontroller(i);
			char str[32];
			sprintf (str, "Controller %d", pbonecontroller->inputfield);
			cController->add (str);
		}

		if (hdr->numbonecontrollers() > 0)
		{
			mstudiobonecontroller_t *pbonecontrollers = hdr->pBonecontroller(0);
			cController->select (0);
			slController->setRange (pbonecontrollers->start, pbonecontrollers->end);
			slController->setValue (0);
		}
	}
}



void
ControlPanel::setBoneController (int index)
{
	CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
	if (hdr)
	{
		mstudiobonecontroller_t *pbonecontroller = hdr->pBonecontroller(index);
		slController->setRange ( pbonecontroller->start, pbonecontroller->end);
		slController->setValue (0);
	}
}



void
ControlPanel::setBoneControllerValue (int index, float value)
{
	CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
	if (hdr)
	{
		mstudiobonecontroller_t *pbonecontrollers = hdr->pBonecontroller(index);
		models->GetActiveStudioModel()->SetController (pbonecontrollers->inputfield, value);
	}
}



void
ControlPanel::initPoseParameters()
{
	CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
	if (hdr)
	{
		for (int i = 0; i < hdr->GetNumPoseParameters(); i++)
		{
			setBlend( i, 0.0 );
		}
	}
}

void
ControlPanel::initSkinChoices()
{
	CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
	if (hdr)
	{
		cSkin->setEnabled (hdr->numskinfamilies() > 0);
		cSkin->removeAll();

		for (int i = 0; i < hdr->numskinfamilies(); i++)
		{
			char str[32];
			sprintf (str, "Skin %d", i + 1);
			cSkin->add (str);
		}

		cSkin->select (0);
		models->GetActiveStudioModel()->SetSkin (0);
		g_viewerSettings.skin = 0;
	}
}



void
ControlPanel::setModelInfo()
{
	static char str[2048];
	CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();

	if (!hdr)
		return;

	int hbcount = 0;
	for ( int s = 0; s < hdr->numhitboxsets(); s++ )
	{
		hbcount += hdr->iHitboxCount( s );
	}

	sprintf (str,
		"Bones: %d\n"
		"Bone Controllers: %d\n"
		"Hit Boxes: %d in %d sets\n"
		"Sequences: %d\n",
		hdr->numbones(),
		hdr->numbonecontrollers(),
		hbcount,
		hdr->numhitboxsets(),
		hdr->GetNumSeq()
		);

	lModelInfo1->setLabel (str);

	sprintf (str,
		"Textures: %d\n"
		"Skin Families: %d\n"
		"Bodyparts: %d\n"
		"Attachments: %d\n",
		hdr->numtextures(),
		hdr->numskinfamilies(),
		hdr->numbodyparts(),
		hdr->GetNumAttachments());

	lModelInfo2->setLabel (str);
}


void ControlPanel::CenterOnFace( void )
{
	if ( !models->GetActiveStudioModel() )
		return;

	StudioModel *mdl = models->GetActiveStudioModel();
	if ( !mdl )
		return;

	CStudioHdr *hdr = mdl->GetStudioHdr();
	if ( !hdr )
		return;

	setSpeed( 1.0f );

	int oldSeq = models->GetActiveStudioModel()->GetSequence();

	int seq = models->GetActiveStudioModel()->LookupSequence( "idle_suble" );
	if ( seq == -1 )
		seq = 0;

	if ( seq != oldSeq )
	{
		Con_Printf( "Centering changed model sequence # to %d\n", seq );
	}

	setSequence( seq );
	initPoseParameters( );

	mdl->m_angles.Init();
	mdl->m_origin.Init();

	Vector size;
	VectorSubtract( hdr->hull_max(), hdr->hull_min(), size );

	float eyeheight = hdr->hull_min().z + 0.9 * size.z;

	if ( hdr->GetNumAttachments() > 0 )
	{
		for (int i = 0; i < hdr->GetNumAttachments(); i++)
		{
			const mstudioattachment_t &attachment = hdr->pAttachment( i );
			int iBone = hdr->GetAttachmentBone( i );

			if ( Q_stricmp( attachment.pszName(), "eyes" ) )
				continue;

			mstudiobone_t *bone = hdr->pBone( iBone );
			if ( !bone )
				continue;

			matrix3x4_t boneToPose;
			MatrixInvert( bone->poseToBone, boneToPose );

			matrix3x4_t attachmentPoseToLocal;
			ConcatTransforms( boneToPose, attachment.local, attachmentPoseToLocal );

			Vector localSpaceEyePosition;
			VectorITransform( vec3_origin, attachmentPoseToLocal, localSpaceEyePosition );

			// Not sure why this must be negative?
			eyeheight = -localSpaceEyePosition.z + hdr->hull_min().z;
			break;
		}
	}

	KeyValues *seqKeyValues = new KeyValues("");
	if ( seqKeyValues->LoadFromBuffer( mdl->GetFileName( ), mdl->GetKeyValueText( seq ) ) )
	{
		// Do we have a build point section?
		KeyValues *pkvAllFaceposer = seqKeyValues->FindKey("faceposer");
		if ( pkvAllFaceposer )
		{
			float flEyeheight = pkvAllFaceposer->GetFloat( "eye_height", -9999.0f );
			if ( flEyeheight != -9999.0f )
			{
				eyeheight = flEyeheight;
			}
		}
	}

	seqKeyValues->deleteThis();

	mdl->m_origin.x = size.z * .65f;
	mdl->m_origin.z += eyeheight;

	CUtlVector< StudioModel * > modellist;

	modellist.AddToTail( models->GetActiveStudioModel() );

	int i;
	if ( models->CountVisibleModels() > 0 )
	{
		modellist.RemoveAll();
		for ( i = 0; i < models->Count(); i++ )
		{
			if ( models->IsModelShownIn3DView( i ) )
			{
				modellist.AddToTail( models->GetStudioModel( i ) );
			}
		}
	}

	int modelcount = modellist.Count();
	int countover2 = modelcount / 2;
	int ydelta = GetModelGap();
	int yoffset = -countover2 * ydelta;
	for ( i = 0 ; i < modelcount; i++ )
	{
		if ( models->GetStudioHeader( i ) == hdr )
		{
			mdl->m_origin.y = -yoffset;
		}
		yoffset += ydelta;
	}

	g_pMatSysWindow->redraw();
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : float
//-----------------------------------------------------------------------------
float ControlPanel::GetModelGap( void )
{
	return slModelGap->getValue();
}

void
ControlPanel::centerView()
{
	StudioModel *pModel = models->GetActiveStudioModel();
	if ( !pModel )
		return;

	Vector min, max;
	models->GetActiveStudioModel()->ExtractBbox (min, max);

	float dx = max[0] - min[0];
	float dy = max[1] - min[1];
	float dz = max[2] - min[2];
	float d = dx;
	if (dy > d)
		d = dy;
	if (dz > d)
		d = dz;
	pModel->m_origin[0] = d * 1.0f;
	pModel->m_origin[1] = 0;
	pModel->m_origin[2] = min[2] + dz / 2;
	pModel->m_angles[0] = 0.0f;
	pModel->m_angles[1] = 0.0f;
	pModel->m_angles[2] = 0.0f;
	g_viewerSettings.lightrot.x = 0.f;
	g_viewerSettings.lightrot.y = -180.0f;
	g_viewerSettings.lightrot.z = 0.0f;
	g_pMatSysWindow->redraw();
}

bool ControlPanel::Close()
{
	int index = g_pExpressionClass->getSelectedIndex();
	CExpClass *cl = expressions->GetClass( index );
	if ( !cl )
		return true;

	return expressions->CloseClass( cl );

}

bool ControlPanel::Closeall() 
{
	bool retval = true;

	while ( expressions->GetNumClasses() > 0 )
	{
		CExpClass *cl = expressions->GetClass( 0 );
		if ( !cl )
			break;

		if ( !expressions->CloseClass( cl ) )
		{
			return false;
		}
	}
	return retval;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void ControlPanel::Copy( void )
{
	g_pFlexPanel->CopyControllerSettings();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void ControlPanel::Paste( void )
{
	g_pFlexPanel->PasteControllerSettings();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void ControlPanel::Undo( void )
{
	CExpClass *active = expressions->GetActiveClass();
	if ( !active )
		return;
	int index = active->GetSelectedExpression();
	if ( index != -1 )
	{
		UndoExpression( index );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void ControlPanel::Redo( void )
{
	CExpClass *active = expressions->GetActiveClass();
	if ( !active )
		return;
	int index = active->GetSelectedExpression();
	if ( index != -1 )
	{
		RedoExpression( index );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void ControlPanel::UndoExpression( int index )
{
	if ( index == -1 )
		return;

	CExpClass *active = expressions->GetActiveClass();
	if ( !active )
		return;

	CExpression *exp = active->GetExpression( index );
	if ( exp )
	{
		exp->Undo();
		// Show the updated data
		active->SelectExpression( index );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void ControlPanel::RedoExpression( int index )
{
	if ( index == -1 )
		return;
	
	CExpClass *active = expressions->GetActiveClass();
	if ( !active )
		return;

	CExpression *exp = active->GetExpression( index );
	if ( exp )
	{
		exp->Redo();
		// Show the updated data
		active->SelectExpression( index );
	}
}

void ControlPanel::DeleteExpression( int index )
{
	CExpClass *active = expressions->GetActiveClass();
	if ( !active )
		return;

	CExpression *exp = active->GetExpression( index );
	if ( exp )
	{
		Con_Printf( "Deleting expression %s : %s\n", exp->name, exp->description );
			
		g_pFlexPanel->DeleteExpression( index );

		active->SelectExpression( max( 0, index - 1 ) );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : dt - 
//-----------------------------------------------------------------------------
void ControlPanel::Think( float dt )
{
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool ControlPanel::AllToolsDriveSpeech( void )
{
	return cbAllWindowsDriveSpeech->isChecked();
}