// CurveEditor.cpp : implementation file
//

#include "stdafx.h"
#include "pixelwhippro.h"
#include "CurveEditor.h"
#include "EffectEditWin.h"
#include "TreeParamView.h"
#include "DataParamView.h"
#include "CurveSet.h"
#include <GosFx\GosFx.hpp>

extern CPixelWhipProApp theApp;
extern gosFX::Effect *CurrentEffect;

#define LINEDIVS 30
#define HANDLELEN 5

/////////////////////////////////////////////////////////////////////////////
// CCurveEditor

//IMPLEMENT_DYNCREATE(CCurveEditor, CWnd)

CCurveEditor::CCurveEditor()
{
	CurrentEditMode=MOVE;
	EditHan=EditVar=false;
	StartY=-100;
	EndY=100;
	NumCurves=0;
	DCurve=VCurve=NULL;
	CurrentCSet=NULL;
	SelectedKey=-1;
}

CCurveEditor::~CCurveEditor()
{
}


BEGIN_MESSAGE_MAP(CCurveEditor, CWnd)
//{{AFX_MSG_MAP(CCurveEditor)
ON_WM_CLOSE()
ON_WM_PAINT()
ON_COMMAND(ID_EDIT_INSERTMODE, OnEditInsertmode)
ON_COMMAND(ID_EDIT_MOVEMODE, OnEditMovemode)
ON_COMMAND(ID_EDIT_PANMODE, OnEditPanmode)
ON_COMMAND(ID_EDIT_SCALEMODE, OnEditScalemode)
ON_COMMAND(ID_EDIT_ZOOMMODE, OnEditZoommode)
ON_UPDATE_COMMAND_UI(ID_EDIT_INSERTMODE, OnUpdateEditInsertmode)
ON_UPDATE_COMMAND_UI(ID_EDIT_MOVEMODE, OnUpdateEditMovemode)
ON_UPDATE_COMMAND_UI(ID_EDIT_PANMODE, OnUpdateEditPanmode)
ON_UPDATE_COMMAND_UI(ID_EDIT_SCALEMODE, OnUpdateEditScalemode)
ON_UPDATE_COMMAND_UI(ID_EDIT_ZOOMMODE, OnUpdateEditZoommode)
ON_COMMAND(ID_EDIT_EDITVARAINCECURVE, OnEditEditvaraincecurve)
ON_UPDATE_COMMAND_UI(ID_EDIT_EDITVARAINCECURVE, OnUpdateEditEditvaraincecurve)
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
ON_WM_LBUTTONDOWN()
ON_COMMAND(ID_EDIT_DELETEVERTEX, OnEditDeletevertex)
ON_COMMAND(ID_VIEW_ZOOMTOCURVE, OnViewZoomtocurve)
ON_WM_CREATE()
ON_WM_KEYDOWN()
	ON_WM_SIZE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CCurveEditor message handlers

void CCurveEditor::OnClose() 
{
	ShowWindow(SW_HIDE);
	//	CWnd::OnClose();
}


void CCurveEditor::Refresh()
{
	SelectedKey=-1;
	ReDraw();
}


void CCurveEditor::OnPaint() 
{
	
	CPaintDC dc(this); // device context for painting
	
	ReDrawInternal();
}

void CCurveEditor::DrawHandles(CDC *dc,gosFX::Curve *curve)
{
	CRect crct;
	GetClientRect(&crct);
	int hlen=crct.Width()<crct.Height()?crct.Width():crct.Height()/HANDLELEN;
	CPoint lpt;	
	
	if(curve)
	{
		Check_Object(curve);
		
		switch(curve->m_type)
		{
			
		case gosFX::Curve::e_SplineType:
			{
				gosFX::SplineCurve *SCurve=(gosFX::SplineCurve *)curve;
				double ang;
				float slp;
				
				lpt.x=0;
				lpt.y=CurveToClient(SCurve->ComputeValue(0.0f,0.0f),crct.Height());
				dc->MoveTo(lpt);
				slp=SCurve->ComputeSlope(0.0f);
				slp=(float)(CurveToClient(slp,crct.Height())-CurveToClient(0.0f,crct.Height()))/crct.Width();
				ang=atan(slp);
				
				lpt.x+=(int)((float)hlen*cos(ang));
				lpt.y+=(int)((float)hlen*sin(ang));
				H1Pos=lpt;
				dc->LineTo(lpt);
				dc->Ellipse(lpt.x-5,lpt.y-5,lpt.x+5,lpt.y+5);
				
				lpt.x=crct.Width();
				lpt.y=CurveToClient(SCurve->ComputeValue(1.0f,0.0f),crct.Height());
				dc->MoveTo(lpt);
				
				slp=SCurve->ComputeSlope(1.0f);
				slp=(float)(CurveToClient(slp,crct.Height())-CurveToClient(0.0f,crct.Height()))/crct.Width();
				ang=atan(slp);
				lpt.x-=(int)(hlen*cos(ang));
				lpt.y-=(int)(hlen*sin(ang));
				
				H2Pos=lpt;
				dc->LineTo(lpt);
				dc->Ellipse(lpt.x-5,lpt.y-5,lpt.x+5,lpt.y+5);
				
			}
			break;
			
		case gosFX::Curve::e_ComplexType:
			{
				gosFX::ComplexCurve *SCurve=(gosFX::ComplexCurve *)curve;
				Stuff::Scalar tme;
				tme=(*SCurve)[SCurve->GetKeyCount()-1].m_time;
				lpt.x=(int)(tme*crct.Width());
				lpt.y=CurveToClient(SCurve->ComputeValue(tme,0.0f),crct.Height());
				dc->MoveTo(lpt);
				
				lpt.x=crct.Width();
				lpt.y=CurveToClient(SCurve->ComputeValue(1.0f,0.0f),crct.Height());
				H1Pos=lpt;
				dc->LineTo(lpt);
				dc->Ellipse(lpt.x-5,lpt.y-5,lpt.x+5,lpt.y+5);
				
				
			}
			break;
			
			
		default:
			break;
		}
	}
}

void CCurveEditor::DrawCurve(CDC *dc,gosFX::Curve *curve,bool DrawKeys)
{
	CRect crct;
	GetClientRect(&crct);
	
	Check_Object(curve);
	
	switch(curve->m_type)
	{
	case gosFX::Curve::e_ConstantType:
		{
			CPoint lpt;	
			gosFX::ConstantCurve *SCurve=(gosFX::ConstantCurve *)curve;
			
			lpt.x=0;
			lpt.y=CurveToClient(SCurve->m_value,crct.Height());
			if(DrawKeys)
			{
				dc->Rectangle(lpt.x-5,lpt.y-5,lpt.x+5,lpt.y+5);
			}
			
			dc->MoveTo(lpt);
			lpt.x=crct.Width();
			dc->LineTo(lpt);
			
		}
		break;
		
	case gosFX::Curve::e_LinearType:
		{
			CPoint lpt;	
			gosFX::LinearCurve *SCurve=(gosFX::LinearCurve *)curve;
			
			lpt.x=0;
			lpt.y=CurveToClient(SCurve->ComputeValue(0.0f,0.0f),crct.Height());
			if(DrawKeys)
			{
				dc->Rectangle(lpt.x-5,lpt.y-5,lpt.x+5,lpt.y+5);
			}
			
			dc->MoveTo(lpt);
			lpt.x=crct.Width();
			lpt.y=CurveToClient(SCurve->ComputeValue(1.0f,0.0f),crct.Height());
			dc->LineTo(lpt);
			
		}
		break;
		
	case gosFX::Curve::e_SplineType:
		{
			gosFX::SplineCurve *SCurve=(gosFX::SplineCurve *)curve;
			
			CPoint lpt;	
			
			if(DrawKeys)
			{
				lpt.x=0;
				lpt.y=CurveToClient(SCurve->ComputeValue(0.0f,0.0f),crct.Height());
				dc->Rectangle(lpt.x-5,lpt.y-5,lpt.x+5,lpt.y+5);
				lpt.x=crct.Width();
				lpt.y=CurveToClient(SCurve->ComputeValue(1.0f,0.0f),crct.Height());
				dc->Rectangle(lpt.x-5,lpt.y-5,lpt.x+5,lpt.y+5);
			}
			
			lpt.x=0;
			lpt.y=CurveToClient(SCurve->ComputeValue(0.0f,0.0f),crct.Height());
			
			float finc,linelen=1.0f/LINEDIVS;
			
			dc->MoveTo(lpt);
			
			for(finc=linelen;finc<=1.0f;finc+=linelen)
			{
				lpt.x=(long)(finc*crct.Width());
				lpt.y=CurveToClient(SCurve->ComputeValue(finc,0.0f),crct.Height());
				dc->LineTo(lpt);				
			}
			lpt.x=crct.Width();
			lpt.y=CurveToClient(SCurve->ComputeValue(1.0f,0.0f),crct.Height());
			dc->LineTo(lpt);				
			
		}
		break;
		
	case gosFX::Curve::e_ComplexType:
		{
			gosFX::ComplexCurve *SCurve=(gosFX::ComplexCurve *)curve;
			int key;
			float st,finc,linelen=1.0f/LINEDIVS,nextx=0.0f;
			CPoint lpt,pt;	
			if(DrawKeys)
			{
				for(key=0;key<SCurve->GetKeyCount();key++)
				{
					st=(*SCurve)[key].m_time;
					lpt.x=(long)(st*crct.Width());
					lpt.y=CurveToClient(SCurve->ComputeValue(st,0.0f),crct.Height());
					dc->Rectangle(lpt.x-5,lpt.y-5,lpt.x+5,lpt.y+5);
				}
			}
			lpt.x=0;
			lpt.y=CurveToClient(SCurve->ComputeValue(0.0,0.0f),crct.Height());
			
			key=1;
			for(finc=0.0f;finc<=1.0f;finc+=linelen)
			{
				if(key<SCurve->GetKeyCount())
				{
					st=(*SCurve)[key].m_time;
					
					if(finc>st)
					{
						finc=st;
						key++;
					}
					else
					{
						if(st-finc<linelen) {finc=st; key++;}
					}
				}
				else
				{
					if((1.0f-finc)<=linelen) 
						finc=1.0f;
				}
				
				pt.x=(long)(finc*crct.Width());
				pt.y=CurveToClient(SCurve->ComputeValue(finc,0.0f),crct.Height());
				dc->MoveTo(lpt);
				dc->LineTo(pt);
				lpt=pt;
				if(finc<1.0f && (finc+linelen)>1.0f) finc-=linelen; //cap end
			}
			
		}
		break;
		
		
	default:
		break;
}

}

void CCurveEditor::OnEditInsertmode()	{ CurrentEditMode=INSERT;	}
void CCurveEditor::OnEditMovemode()		{ CurrentEditMode=MOVE;	 }
void CCurveEditor::OnEditPanmode()		{ CurrentEditMode=PAN; }
void CCurveEditor::OnEditScalemode()	{ CurrentEditMode=SCALE; }
void CCurveEditor::OnEditZoommode()		{ CurrentEditMode=ZOOM; }

void CCurveEditor::OnUpdateEditInsertmode(CCmdUI* pCmdUI) 
{	pCmdUI->SetCheck(CurrentEditMode==INSERT?1:0); }

void CCurveEditor::OnUpdateEditMovemode(CCmdUI* pCmdUI) 
{	pCmdUI->SetCheck(CurrentEditMode==MOVE?1:0); }

void CCurveEditor::OnUpdateEditPanmode(CCmdUI* pCmdUI) 
{ pCmdUI->SetCheck(CurrentEditMode==PAN?1:0); }

void CCurveEditor::OnUpdateEditScalemode(CCmdUI* pCmdUI) 
{	pCmdUI->SetCheck(CurrentEditMode==SCALE?1:0); }

void CCurveEditor::OnUpdateEditZoommode(CCmdUI* pCmdUI) 
{	pCmdUI->SetCheck(CurrentEditMode==ZOOM?1:0); }

void CCurveEditor::OnEditEditvaraincecurve()  
{ 
	EditVar=!EditVar; 
	SelectedKey=-1; 
	ReDraw();
}

void CCurveEditor::OnUpdateEditEditvaraincecurve(CCmdUI* pCmdUI) 
{ pCmdUI->SetCheck(EditVar?1:0); }

void CCurveEditor::OnLButtonUp(UINT nFlags, CPoint point) 
{
	switch(CurrentEditMode)
	{
	case INSERT:
		break;
	case MOVE:
		ReleaseCapture();
		break;
	case PAN: 
		ReleaseCapture();
		break;
	case SCALE: 
		ReleaseCapture();
		break;
	case ZOOM: 
		ReleaseCapture();
		break;
	}
	
	CWnd::OnLButtonUp(nFlags, point);
}

void CCurveEditor::OnMouseMove(UINT nFlags, CPoint point) 
{
	if(nFlags&MK_LBUTTON)
	{
		CRect rct;
		GetClientRect(&rct);
		
		
		switch(CurrentEditMode)
		{
		case INSERT:
			break;
		case MOVE:
			{
				gosFX::Curve *Curve=EditVar?VCurve:DCurve;
				
				if(Curve)
				{
					Check_Object(Curve);
					
					Stuff::DynamicMemoryStream stream;

					if(CurrentCSet)
						CurrentCSet->PushState();
					
					Stuff::Scalar newy;
					
					newy=ClientToCurve(point.y,rct.Height());
					switch(Curve->m_type)
					{
					case gosFX::Curve::e_ConstantType:
						{
							gosFX::ConstantCurve *SCurve=(gosFX::ConstantCurve *)Curve;
							SCurve->SetCurve(newy);
						}
						break;
						
					case gosFX::Curve::e_LinearType:
						{
							gosFX::LinearCurve *SCurve=(gosFX::LinearCurve *)Curve;
							Stuff::Scalar v0,v1;
							v0=SCurve->ComputeValue(0.0f,0.0f);
							v1=SCurve->ComputeValue(1.0f,0.0f);
							
							if(SelectedKey<0)
								v0=newy;
							else
								v1=newy;
							
							SCurve->SetCurve(v0,v1);
						}
						break;
						
					case gosFX::Curve::e_SplineType:
						{
							gosFX::SplineCurve *SCurve=(gosFX::SplineCurve *)Curve;
							Stuff::Scalar v0,v1,s0,s1;
							v0=SCurve->ComputeValue(0.0f,0.0f);
							v1=SCurve->ComputeValue(1.0f,0.0f);
							s0=SCurve->ComputeSlope(0.0f);
							s1=SCurve->ComputeSlope(1.0f);
							
							if(EditHan)
							{
								float cx=(float)point.x/rct.Width();
								if(cx<0.00000001f || fabs(cx-1.0f)<0.00000001f) return;
								if(SelectedKey<0)
									s0=(newy-SCurve->ComputeValue(0.0f,0.0f))/cx;
								else
									s1=-((newy-SCurve->ComputeValue(1.0f,0.0f))/(1.0f-cx));
							}
							else
							{
								if(SelectedKey<0)
									v0=newy;
								else
									v1=newy;
							}		
							SCurve->SetCurve(v0,s0,v1,s1);
						}
						break;
						
					case gosFX::Curve::e_ComplexType:
						if(SelectedKey>=0)
						{
							gosFX::ComplexCurve *SCurve=(gosFX::ComplexCurve *)Curve;
							if(EditHan)
							{
								Stuff::Scalar val,tme,ntme;
								tme=(*SCurve)[SCurve->GetKeyCount()-1].m_time;
								val=(*SCurve)[SCurve->GetKeyCount()-1].m_value;
								ntme=((float)point.x/rct.Width());
								if(point.x<(tme*rct.Width()) || (ntme-tme)<0.000001f) return;
								(*SCurve)[SCurve->GetKeyCount()-1].SetLinearKey(tme,val,newy,ntme-tme);
							}
							else
							{
								Stuff::Scalar tme,slp;
								
								int MinX,MaxX;
								if(SelectedKey==0)
									MinX=0;
								else
									MinX=(int)ceil((*SCurve)[SelectedKey-1].m_time*rct.Width())+1;
								
								if(SelectedKey==SCurve->GetKeyCount()-1)
									MaxX=rct.Width();
								else
									MaxX=(int)floor((*SCurve)[SelectedKey+1].m_time*rct.Width())-1;
								
								int xval=point.x;
								xval=xval<MinX?MinX:xval;
								xval=xval>MaxX?MaxX:xval;
								//							slp=(*SCurve)[SelectedKey].m_slope;
								slp=SCurve->ComputeValue(1.0f,0.0f);
								tme=(float)xval/rct.Width();
								if(tme<1.0f)
								{
								(*SCurve)[SelectedKey].SetLinearKey(tme,newy,slp,1.0f-tme);
								}
							}
						}
						break;
						
						
					default:
						break;
					} //end of cs type
					FixCurve(Curve);
					
					if(CurrentCSet)
						CurrentCSet->VerifyAndPop();
						
					theApp.ParamWin->DataPane->UpdateControls();
					ReDraw();
					
  }//end of If Curve
			}
			break;
		case PAN: 
			{
				float add;
				add=((point.y-OldPoint.y)*(EndY-StartY))/rct.Height();
				StartY+=add;
				EndY+=add;
				ReDraw();
			}
			break;
		case SCALE: 
			{
				
				gosFX::Curve *Curve=EditVar?VCurve:DCurve;
				
				if(Curve)
				{
					Check_Object(Curve);
					
					Stuff::DynamicMemoryStream stream;
					if(CurrentCSet)
						CurrentCSet->PushState();
					
					float perchange;
					perchange=(float)pow((EndY-StartY)/2,(float)(OldPoint.y-point.y)/rct.Height());
					Curve->AxisScale(perchange,0.0f);
					theApp.ParamWin->DataPane->UpdateControls();
					ReDraw();
					
				if(CurrentCSet)
						CurrentCSet->VerifyAndPop();
						
				}
				
			}
			
			break;
		case ZOOM: 
			{
				float perchange,add;
				perchange=(float)pow(4.0,(float)(point.y-OldPoint.y)/rct.Height());
				add=(EndY-StartY)*(1.0f-perchange);
				StartY+=add;
				EndY-=add;
				ReDraw();
			}
			break;
		}
		OldPoint=point;
	}
	CWnd::OnMouseMove(nFlags, point);
}

void CCurveEditor::OnLButtonDown(UINT nFlags, CPoint point) 
{
	OldPoint=point;
	
	switch(CurrentEditMode)
	{
	case INSERT:
		{
			
			CRect crct;
			GetClientRect(&crct);
			Stuff::DynamicMemoryStream stream;
					if(CurrentCSet)
						CurrentCSet->PushState();
			
			gosFX::Curve *Curve=EditVar?VCurve:DCurve;
			
			if(Curve && Curve->m_type==gosFX::Curve::e_ComplexType)
			{
				Check_Object(Curve);
				gosFX::ComplexCurve *SCurve=(gosFX::ComplexCurve *)Curve;
				SelectedKey=SCurve->InsertKey((float)point.x/crct.Width());
			}

			if(CurrentCSet)
						CurrentCSet->VerifyAndPop();
				
			theApp.Modified=true;
			
			ReDraw();
		}
		break;
	case MOVE:
		SetCapture();
		SelectedKey=GetClosestKey(point);
		ReDraw();
		theApp.Modified=true;
		
		break;
	case PAN: 
		SetCapture();
		break;
	case SCALE: 
		SetCapture();
		theApp.Modified=true;
		break;
	case ZOOM: 
		SetCapture();
		break;
	}
	
	CWnd::OnLButtonDown(nFlags, point);
}

void CCurveEditor::UpdateCurveList(HTREEITEM itm)
{
	
	if(itm==NULL) return;
	
	if(itm!=TVI_ROOT)
	{
		
		CurveSet *CSet=(CurveSet *)theApp.ParamWin->TreePane->m_PTree.GetItemData(itm);
		
		gosFX::Curve *curve;
		
		if(CSet)
			curve=CSet->Curve;
		else
			curve=NULL;
		
		if(curve)
		{
			Check_Object(curve);
			
			if(!DCurve && theApp.ParamWin->TreePane->m_PTree.GetSelectedItem()==itm)
			{
				CurrentCSet=CSet;
				
				DCurve=curve->GetSubCurve(0);
				VCurve=curve->GetSubCurve(1);
				EditHan=false;
			}
			else
				if(theApp.ParamWin->TreePane->m_PTree.GetCheck(itm))
				{
					Curves[NumCurves++]=curve->GetSubCurve(0);
					Verify(NumCurves<MAXCURVES);
				}
		}
		
	}
	else
	{	
		CurrentCSet=NULL;
		NumCurves=0;
		DCurve=VCurve=NULL;
		EditHan=false;
	}
	
	//Draw Children
	HTREEITEM nitm=theApp.ParamWin->TreePane->m_PTree.GetChildItem(itm);
	
	while(nitm!=NULL)
	{
		UpdateCurveList(nitm);
		nitm=theApp.ParamWin->TreePane->m_PTree.GetNextSiblingItem(nitm);
	}
	
	if(itm==TVI_ROOT) ReDraw();
	
}

int CCurveEditor::GetClosestKey(CPoint &pnt)
{
	
	int i;
	CRect crct;
	GetClientRect(&crct);
	int kx,ky;
	
	int dist,lval=crct.Width()*crct.Width()+crct.Height()*crct.Height();
	int lnum=0;
	
	gosFX::Curve *Curve=EditVar?VCurve:DCurve;
	if(!Curve) return -1;
	Check_Object(Curve);
	
	switch(Curve->m_type)
	{
	case gosFX::Curve::e_ConstantType:
		{
			gosFX::ConstantCurve *SCurve=(gosFX::ConstantCurve *)Curve;
			return -1;
		}
		break;
		
	case gosFX::Curve::e_LinearType:
		{
			gosFX::LinearCurve *SCurve=(gosFX::LinearCurve *)Curve;
			if(pnt.x<crct.Width()/2) return -1; else return 0;
		}
		break;
		
	case gosFX::Curve::e_SplineType:
		{
			gosFX::SplineCurve *SCurve=(gosFX::SplineCurve *)Curve;
			int tval,lval,tdist,dist;
			
			
			lval=0;
			tval=(int)(pnt.y-CurveToClient(SCurve->ComputeValue(0.0f,0.0f),crct.Height()));
			dist=pnt.x*pnt.x+tval*tval;
			
			tval=(int)(pnt.y-CurveToClient(SCurve->ComputeValue(1.0f,0.0f),crct.Height()));
			tdist=(pnt.x-crct.Width())*(pnt.x-crct.Width())+tval*tval;
			if(tdist<dist) {dist=tdist; lval=1;}
			
			tdist=(pnt.x-H1Pos.x)*(pnt.x-H1Pos.x)+(pnt.y-H1Pos.y)*(pnt.y-H1Pos.y);
			if(tdist<dist) {dist=tdist; lval=2;}
			
			tdist=(pnt.x-H2Pos.x)*(pnt.x-H2Pos.x)+(pnt.y-H2Pos.y)*(pnt.y-H2Pos.y);
			if(tdist<dist) {dist=tdist; lval=3;}
			
			switch(lval)
			{
			case 0: //left key
				EditHan=false;
				return -1;
				break;
			case 1: //right key
				EditHan=false;
				return 0;
				break;
			case 2: //left handle
				EditHan=true;
				return -1;
				break;
			case 3: //right handle
				EditHan=true;
				return 0;
				break;
			}
			
			
		}
		break;
		
	case gosFX::Curve::e_ComplexType:
		{
			gosFX::ComplexCurve *SCurve=(gosFX::ComplexCurve *)Curve;
			
			kx=crct.Width();
			ky=(int)CurveToClient(SCurve->ComputeValue(1.0f,0.0f),crct.Height());
			lval=(pnt.x-kx)*(pnt.x-kx)+(pnt.y-ky)*(pnt.y-ky);
			lnum=-1;
			
			for(i=0;i<SCurve->GetKeyCount();i++)
			{
				kx=(int)((*SCurve)[i].m_time*crct.Width());
				ky=(int)CurveToClient(SCurve->ComputeValue((*SCurve)[i].m_time,0.0f),crct.Height());
				dist=(pnt.x-kx)*(pnt.x-kx)+(pnt.y-ky)*(pnt.y-ky);
				if(dist<lval) {lval=dist; lnum=i; }
			}
			
			if(lnum<0)
			{
				EditHan=true;
				return SCurve->GetKeyCount()-1;
			}
			else
			{
				EditHan=false;
				return lnum;
			}
		}
		break;
		
		
	default:
		break;
	}
	return -1;	
}

void CCurveEditor::OnEditDeletevertex() 
{
	
	gosFX::Curve *Curve=EditVar?VCurve:DCurve;
	if(!Curve || Curve->m_type!=gosFX::Curve::e_ComplexType) return;	
	
	gosFX::ComplexCurve *SCurve=(gosFX::ComplexCurve *)Curve;
	if(SelectedKey>0)
	{
		
		float cfx,cfy;
		cfx=(*SCurve)[SelectedKey].m_time;
		cfy=SCurve->ComputeValue(cfx,0.0f);
		SCurve->DeleteKey(SelectedKey);
		CRect crct;
		GetClientRect(&crct);
		SelectedKey=GetClosestKey(CPoint((long)(cfx*crct.Width()),CurveToClient(cfy,crct.Height())));
		FixCurve(Curve);
	}	
	else
	{
		int i;
		for(i=1;i<SCurve->GetKeyCount();i++) SCurve->DeleteKey(i);
		FixCurve(Curve);
	}
	
	ReDraw();	
}


void CCurveEditor::FixCurve(gosFX::Curve *Curve)
{
	
	
	switch(Curve->m_type)
	{
		
		
	case gosFX::Curve::e_SplineType:
		{
			gosFX::SplineCurve *SCurve=(gosFX::SplineCurve *)Curve;
		}
		break;
		
	case gosFX::Curve::e_ComplexType:
		{
			gosFX::ComplexCurve *SCurve=(gosFX::ComplexCurve *)Curve;
			
			Stuff::Scalar cval,nval,ctme,ntme;
			if(SCurve->GetKeyCount()==1)
			{
				cval=(*SCurve)[0].m_value;
				nval=SCurve->ComputeValue(1.0f,0.0f);
				(*SCurve)[0].SetLinearKey(0.0f,cval,nval,1.0f);
				
			}
			else
			{
				cval=(*SCurve)[0].m_value;
				(*SCurve)[0].SetLinearKey(0.0f,cval,cval,0.01f);
			}	
			
			// Connect the Keys
			int key;
			for(key=0;key<SCurve->GetKeyCount()-1;key++)
			{
				ctme=(*SCurve)[key].m_time;
				ntme=(*SCurve)[key+1].m_time;
				cval=(*SCurve)[key].m_value;
				nval=(*SCurve)[key+1].m_value;
				(*SCurve)[key].SetLinearKey(ctme,cval,nval,ntme-ctme);
			}
			
			
		}
		break;
		
		
	default:
		break;
	}
	
	
	
	
	
}

void CCurveEditor::OnViewZoomtocurve() 
{
	
	gosFX::Curve *Curve=EditVar?VCurve:DCurve;
	
	if(Curve==NULL)
	{
		StartY=-100.0f;
		EndY=100.0f;
		return;
	}
	
	Curve->ExpensiveComputeRange(&StartY,&EndY);
	
	float aval;
	aval=(float)fabs(StartY);
	aval=(float)fabs(EndY)>aval?(float)fabs(EndY):aval;
	aval+=aval*0.2f;
	StartY=-aval;
	EndY=aval;
	/*
	if(StartY==EndY) 
	{ 
	StartY-=.5; 
	EndY+=.5; 
	}
	else
	{
	EndY+=(EndY-StartY)*0.1f;
	StartY-=(EndY-StartY)*0.1f;
	}
	*/	
	
	ReDraw();
}

void CCurveEditor::DrawTicks(CDC *dc)
{
	float cury,nexty;
	int Ccury,Cnexty;
	CRect rct;
	GetClientRect(&rct);
	
	dc->SelectObject(&TickPen);
	CString lab;
	CRect textrect;
	
	dc->SetTextColor(0xffffff);
	dc->SetBkColor(0x000000);
	
	if(StartY<0)
	{
		cury=-StartY;
		cury=(float)pow(10,(int)log10(cury));
		Ccury=CurveToClient(-cury,rct.Height());
		
		
		nexty=cury*0.1f;
		Cnexty=CurveToClient(-nexty,rct.Height());
		
		while((Ccury-Cnexty)>3 && Ccury>0 &&  -cury<EndY)
		{
			dc->MoveTo(0,Ccury);
			dc->LineTo(rct.Width(),Ccury);
			
			lab.Format("%5.3f",-cury);
			textrect.top=Ccury-10; textrect.left=0;
			textrect.bottom=Ccury+10; textrect.right=100;
			dc->DrawText(lab,&textrect,DT_LEFT   );	
			
			cury=nexty;
			Ccury=Cnexty;
			nexty=cury*0.1f;
			Cnexty=CurveToClient(-nexty,rct.Height());
		}
		
		
	}
	
	if(EndY>0)
	{
		cury=EndY;
		cury=(float)pow(10,(int)log10(cury));
		Ccury=CurveToClient(cury,rct.Height());
		
		
		nexty=cury*0.1f;
		Cnexty=CurveToClient(nexty,rct.Height());
		
		while((Cnexty-Ccury)>3 && Ccury<rct.Height() && cury>StartY)
		{
			dc->MoveTo(0,Ccury);
			dc->LineTo(rct.Width(),Ccury);
			
			lab.Format("%5.3f",cury);
			textrect.top=Ccury-10; textrect.left=0;
			textrect.bottom=Ccury+10; textrect.right=100;
			dc->DrawText(lab,&textrect,DT_LEFT   );	
			
			cury=nexty;
			Ccury=Cnexty;
			nexty=cury*0.1f;
			Cnexty=CurveToClient(nexty,rct.Height());
		}
		
		
	}
	
	dc->SelectObject(&ZeroPen);
	dc->MoveTo(0,CurveToClient(0,rct.Height()));
	dc->LineTo(rct.Width(),CurveToClient(0,rct.Height()));
	
}

BOOL CCurveEditor::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext) 
{
	// TODO: Add your specialized code here and/or call the base class
	
	return CWnd::Create(lpszClassName, lpszWindowName, dwStyle, rect, pParentWnd, nID, pContext);
}

int CCurveEditor::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CWnd::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	LOGBRUSH lb;
	
	
	lb.lbColor=0x00C800;
	lb.lbStyle=BS_SOLID;
	InactivePen.CreatePen(PS_SOLID,1,&lb);
	
	lb.lbColor=0x7f7f7f;
	lb.lbStyle=BS_SOLID;
	TickPen.CreatePen(PS_DASHDOT,1,&lb);
	
	lb.lbColor=0x00ffff;
	lb.lbStyle=BS_SOLID;
	ZeroPen.CreatePen(PS_DOT,1,&lb);
	
	EditablePen.CreatePen(PS_SOLID,1,0x0000ff);
	VarPen.CreatePen(PS_SOLID,1,0xff0000);
	SelVertPen.CreatePen(PS_SOLID,1,0x00ff00);
	BlackPen.CreatePen(PS_SOLID,1,(COLORREF)0x000000);
	HandlePen.CreatePen(PS_SOLID,1,(COLORREF)0xff00ff);
	BGBrush.CreateSolidBrush(0xC8C8C8);
	
	return 0;
}

void CCurveEditor::ReDrawInternal()
{
	if(theApp.ParamWin)
	{
		CDC *dc,*pDC=GetDC();

		theApp.AllowDraw=false;
		CDC bbufDC;

		bbufDC.CreateCompatibleDC(pDC);
		bbufDC.SelectObject(BackBuffer);
	
		CRect crct;
		GetClientRect(&crct);
		//		dc->SelectObject(&BGBrush);
		//		dc->Rectangle(crct);
/*
		static bool flip=false;
		flip=!flip;
		pDC->BitBlt(0,0,crct.Width(),crct.Height(),NULL,0,0,flip?BLACKNESS:WHITENESS);
*/
		dc=&bbufDC;
//		dc=pDC;
		dc->BitBlt(0,0,crct.Width(),crct.Height(),NULL,0,0,BLACKNESS);
		int i;
		
		dc->SelectObject(GetStockObject(NULL_BRUSH));
		
		DrawTicks(dc);
		
		dc->SelectObject(&InactivePen);
		for(i=0;i<NumCurves;i++)
			DrawCurve(dc,Curves[i]);
		
		if(EditVar)
			
		{
			if(DCurve)
			{
				dc->SelectObject(&EditablePen);
				DrawCurve(dc,DCurve);
			}
			if(VCurve)
			{
				dc->SelectObject(&VarPen);
				DrawCurve(dc,VCurve,true);
			}
		}
		else
		{
			if(VCurve)
			{
				dc->SelectObject(&VarPen);
				DrawCurve(dc,VCurve);
			}
			if(DCurve)
			{
				dc->SelectObject(&EditablePen);
				DrawCurve(dc,DCurve,true);
			}
		}
		
		gosFX::Curve *Curve=EditVar?VCurve:DCurve;
		
		//DrawHandles
		dc->SelectObject(&HandlePen);
		DrawHandles(dc,Curve);
		
		//OverDrawSelected Key
		dc->SelectObject(&SelVertPen);
		if(Curve)
		{
			Check_Object(Curve);
			switch(Curve->m_type)
			{
			case gosFX::Curve::e_ComplexType:
				{
					gosFX::ComplexCurve *SCurve=(gosFX::ComplexCurve *)Curve;
					if(SelectedKey>=SCurve->GetKeyCount()) 
						SelectedKey=-1;
					if(SelectedKey!=-1)
					{
						float st=(*SCurve)[SelectedKey].m_time;
						CPoint lpt;
						lpt.x=(long)(st*crct.Width());
						lpt.y=CurveToClient(SCurve->ComputeValue(st,0.0f),crct.Height());
						dc->Rectangle(lpt.x-5,lpt.y-5,lpt.x+5,lpt.y+5);
					}
				}
				break;
				
			default:
				if(SelectedKey<0)
				{
					CPoint lpt;
					lpt.x=0;
					lpt.y=CurveToClient(Curve->ExpensiveCompute(0.0f),crct.Height());
					dc->Rectangle(lpt.x-5,lpt.y-5,lpt.x+5,lpt.y+5);
				}
				else
				{
					CPoint lpt;
					lpt.x=crct.Width();
					lpt.y=CurveToClient(Curve->ExpensiveCompute(1.0f),crct.Height());
					dc->Rectangle(lpt.x-5,lpt.y-5,lpt.x+5,lpt.y+5);
				}
				break;
			}
		}
		theApp.AllowDraw=true;
		pDC->BitBlt(crct.left,crct.top,crct.Width(),crct.Height(),&bbufDC,0,0,SRCCOPY);
		ReleaseDC(pDC);
	}
	
}

void CCurveEditor::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	// TODO: Add your message handler code here and/or call default
	switch(nChar)
	{
	case 'E':
		OnEditMovemode();
		break ;
	case 'A':
		OnEditInsertmode();
		break ;
	case 'Z':
		OnEditZoommode();
		break ;
	case ' ':
		OnEditPanmode();
		break ;
	case 'S':
		OnEditScalemode();
		break ;
	case 'V':
		OnEditEditvaraincecurve();
		break ;
	case 'F':
		OnViewZoomtocurve();
		break ;
	case 'D':
		OnEditDeletevertex();
		break ;
	}
	
	CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}


void CCurveEditor::BuildBackBuffer()
{
BackBuffer.DeleteObject();
CRect crect;
GetClientRect(&crect);
CDC *pDC=GetDC();
BackBuffer.CreateCompatibleBitmap(pDC,crect.Width(),crect.Height());
ReleaseDC(pDC);
}

void CCurveEditor::OnSize(UINT nType, int cx, int cy) 
{
	CWnd::OnSize(nType, cx, cy);
BuildBackBuffer();	
	// TODO: Add your message handler code here
	
}
