// EdGUIObject.cpp: implementation of the EdGUIObject class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "MW4GameEd2.h"
#include "EdGUIObject.h"
#include "ObjPanelDlg.h"
#include "DrawInfo.h"
#include "GuiObjPanel.h"
#include "TNPanel.h"
#include "GuiObjectList.h"
#include "ObjManListCtrl.h"
#include "UndoManager.h"
#include "NameManager.h"
#include "GameInterface.h"
#include "ObjProps.h"

#include "DrawInfo.h"


void BaseEdObject::RotateBy(UnitQuaternion &rot)
{
	UnitQuaternion new_mat;
	new_mat.Multiply(rot,RotQuat);
	SetRot(new_mat);
}

void BaseEdObject::RotateAround(Point3D &pnt,UnitQuaternion &vct)
{
	LinearMatrix4D 	m1,m2;
	
	m1.Multiply(GetMat(),LinearMatrix4D(Point3D(-pnt.x,-pnt.y,-pnt.z)));
	m2.Multiply(m1,LinearMatrix4D(vct));
	m1.Multiply(m2,LinearMatrix4D(pnt));
	
	SetMat(m1);
}


LinearMatrix4D BaseEdObject::GetMat()
{
	LinearMatrix4D mat(RotQuat);
	mat.BuildTranslation(Pos);
	return mat;
}

void BaseEdObject::SetMat(const LinearMatrix4D &mat)
{
	RotQuat=mat;
	Pos=mat;
	UpdateMat();
}

void BaseEdObject::Copy( BaseEdObject &obj)
{
	RotQuat=obj.RotQuat;
	Pos=obj.Pos;
}

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

EdGUIObject::EdGUIObject(CString name,Point3D l)
{
	LineCount=0;
	LineList=NULL;
	HoverElevation=0.0f;
	SetPos(l);
	ScreenDiv=100;
	RadiusSqu=0.0f;
	
	Selected=false;
	Visual=true;
	Hidden=false;
	InMap=false;
	Frozen=false;
	
	DrawPen=DrawInfo::HelperPen;
	SelPen=DrawInfo::SelectPen;
	Parent=NULL;
	ReferenceCount=0;
	
	// TestData
	LineCount=0;
	LineList=NULL;
	SetName(name);
}

void EdGUIObject::SetPen(DrawInfo &dinf) 
{
	if(IsSelected()) 
		dinf.SetPen(SelPen); 
	else 
		dinf.SetPen(DrawPen); 
}

void EdGUIObject::BuildRadius()
{
	if(!IsVisual()) return;
	
	int cl;
	
	Scalar gdist=0,dist=0;
	Point3D p1,p2,loc;
	loc=GetPos();
	
	for(cl=0;cl<GetLineCount();cl++)
	{
		GetLine(0,&p1,&p2);
		dist=(p1.x-loc.x)*(p1.x-loc.x)+(p1.z-loc.z)*(p1.z-loc.z);
		gdist=dist>gdist?dist:gdist;
		dist=(p2.x-loc.x)*(p2.x-loc.x)+(p2.z-loc.z)*(p2.z-loc.z);
		gdist=dist>gdist?dist:gdist;
	}
	
	RadiusSqu=gdist;
	
}	

bool EdGUIObject::InRadius( Point3D &pnt)
{
	if(IsHidden()) return false;
	Point3D loc=GetPos();
	Scalar dist=(pnt.x-loc.x)*(pnt.x-loc.x)+(pnt.z-loc.z)*(pnt.z-loc.z);
	return dist<=RadiusSqu;
}


EdGUIObject::~EdGUIObject()
{
//	Verify(ReferenceCount==0);
	NameManager::Instance->RemoveString(Name);
	if(LineList) 
		delete LineList;
}

void EdGUIObject::GetLine(int idx,Point3D *start,Point3D *end)
{
	Verify(idx<LineCount);
	
	LinearMatrix4D mat=GetMat();
	
	*start=LineList[idx].Begin;
	*start*=mat;
	start->y=0.0f;
	
	*end=LineList[idx].End;
	*end*=mat;
	end->y=0.0f;
	
}

Scalar EdGUIObject::GetRadius() 
{
	return (Scalar)sqrt(RadiusSqu);
}

bool EdGUIObject::InsideBox( Point3D &tl, Point3D &br)
{
	if(IsHidden()) return false;
	Scalar  rad=GetRadius();
	Point3D loc=GetPos();
	
	if(tl.x<br.x && tl.z<br.z)
	{
		return (
			tl.x<=(loc.x-rad) && tl.z<=(loc.z-rad) &&
			br.x>=(loc.x+rad) && br.z>=(loc.z+rad)
			);
	}
	else
	{
		return (
			br.x<=(loc.x-rad) && br.z<=(loc.z-rad) &&
			tl.x>=(loc.x+rad) && tl.z>=(loc.z+rad)
			);
	}
}

bool EdGUIObject::IntersectsBox( Point3D &tl, Point3D &br)
{
	if(IsHidden()) return false;
	Scalar  rad=GetRadius();
	Point3D loc=GetPos();
	

	if(tl.x<br.x && tl.z<br.z)
	{
		return !( (loc.z+rad)<tl.z || (loc.z-rad)>br.z || (loc.x+rad)<tl.x || (loc.x-rad)>br.x) ;
	}
	else
	{
		return !( (loc.z+rad)<br.z || (loc.z-rad)>tl.z || (loc.x+rad)<br.x || (loc.x-rad)>tl.x) ;

	}

}

bool EdGUIObject::InDisplayRegion(DrawInfo &dinf)
{
Point3D tl,br;
dinf.vinf.GetViewLimits(&tl,&br);
return IntersectsBox(tl,br);
}



EdGUIObject *EdGUIObject::Clone()
{
	EdGUIObject *obj=new EdGUIObject;
	obj->Copy(*this);
	return obj;
}

void EdGUIObject::Copy( EdGUIObject &obj)
{
	BaseEdObject::Copy(obj);
	LineCount=obj.LineCount;
	Selected=obj.Selected;
	RadiusSqu=obj.RadiusSqu;
	SetName(obj.Name);
	
	if(LineList) {delete LineList; LineList=NULL;}
	
	if(LineCount>0)
		LineList=new GUILine[LineCount];
	for(int i=0;i<LineCount;i++) LineList[i]=obj.LineList[i];
	
	
	Parent=NULL;
	HoverElevation=obj.HoverElevation;
	SnapToGround=obj.SnapToGround;
	Modified=true;
	Visual=obj.Visual;
	Hidden=false;
	InMap=false;
	DrawPen=obj.DrawPen;
	SelPen=obj.SelPen;
	ScreenDiv=obj.ScreenDiv;
	ReferenceCount=0;
}


void EdGUIObject::Draw(DrawInfo &dinf)
{
	if(IsHidden() || !InDisplayRegion(dinf)) return;
	SetPen(dinf);
	
	int i;
	Point3D w_start,w_end;
	for(i=0;i<GetLineCount();i++)
	{
		GetLine(i,&w_start,&w_end);
		dinf.GetDC()->MoveTo(dinf.vinf.DataToScreen(w_start));
		dinf.GetDC()->LineTo(dinf.vinf.DataToScreen(w_end));
	}
	
}


void EdGUIObject::DrawTag(DrawInfo &dinf, unsigned long tag_flags)
{
	if(IsHidden() || !InDisplayRegion(dinf)) return;
	SetPen(dinf);
	
	
	CPoint sloc;
	TEXTMETRIC tm;
	dinf.GetDC()->GetTextMetrics(&tm);
	CSize tsize=dinf.GetDC()->GetTextExtent(GetName());
	bool xrev=false;;
	
	
	
	sloc=dinf.vinf.DataToScreen(GetPos());
	CRect textrect;
	
	dinf.GetDC()->MoveTo(sloc);
	
	
	sloc.x+=dinf.vinf.GetClientSize().cx/20;
	if((sloc.x+tsize.cx)>dinf.vinf.GetClientSize().cx) 	
	{
		sloc.x-=dinf.vinf.GetClientSize().cx/10;
		xrev=true;
	}
	
	sloc.y-=dinf.vinf.GetClientSize().cy/20;
	if(sloc.y<0) sloc.y+=dinf.vinf.GetClientSize().cy/10;
	
	dinf.GetDC()->LineTo(sloc);
	
	if(xrev)
		sloc.x-=dinf.vinf.GetClientSize().cx/40;
	else
		sloc.x+=dinf.vinf.GetClientSize().cx/40;
	
	dinf.GetDC()->LineTo(sloc);
	
	
	
	
	if(xrev)
		sloc.x-=tsize.cx;
	sloc.y-=tm.tmHeight/2;
	textrect.left=sloc.x;
	textrect.top=sloc.y;
	textrect.right=textrect.left+200;
	textrect.bottom=textrect.top+tm.tmHeight*5;
	
	//	dinf.GetDC()->DrawText(gobj->GetName(),&textrect,DT_LEFT);
	CString org_tag(GetTag(tag_flags));
	
	int y=sloc.y;
	
	int lastloc=0,nlloc=org_tag.Find('\n',lastloc);
	while (nlloc > 0)
	{
		dinf.GetDC()->ExtTextOut(sloc.x,y,NULL,NULL,org_tag.Mid(lastloc,nlloc-lastloc),NULL);
		lastloc=nlloc+1;
		y += 16;
		nlloc=org_tag.Find('\n',lastloc);
	}
	
}


int EdGUIObject::DeSelect(Point3D &pnt)	
{
	if(IsHidden() || IsMultiZone() ) return 0;
	if(InRadius(pnt)) DeSelect();
	return IsSelected()?1:0;
}

int EdGUIObject::SelectionDepth(Point3D &pnt)	
{
	if(IsHidden()) return 0;
	return (InRadius(pnt) && IsSelected())?1:0;
}

int EdGUIObject::ObjectDepth(Point3D &pnt)	
{
	if(IsHidden()) return 0;
	return InRadius(pnt)?1:0;
}

int EdGUIObject::Select(Point3D &pnt)	
{
	if(IsHidden()) return 0;
	if(InRadius(pnt)) 
	{
		Select();
		return 	IsSelected()?1:0; 
	}
	else
		return 0;
}

int EdGUIObject::ToggleSelect(Point3D &pnt)	
{
	if(IsHidden()) return 0;
	if(InRadius(pnt)) ToggleSelect();
	return IsSelected()?1:0;
}

int EdGUIObject::Select(Point3D &topleft,Point3D &bottomright)	
{
	if(IsHidden()) return 0;
	if(InsideBox(topleft,bottomright)) Select();
	return IsSelected()?1:0;
}


int EdGUIObject::AddToList(CObjManListCtrl *list,unsigned long tag_flags)
{
	if(IsHidden()) return -1;
	LVITEM lvitm;
	int ret;
	ZeroMemory(&lvitm,sizeof(lvitm));
	
	lvitm.mask=LVIF_IMAGE|LVIF_PARAM|LVIF_TEXT;
	lvitm.iItem=list->GetItemCount();
	lvitm.iSubItem=0;
	lvitm.pszText=GetName().GetBuffer(0);
	lvitm.iImage=GetUnSelIcon();
	lvitm.lParam=(DWORD)this;
	ret=list->InsertItem(&lvitm);
	
	
	ZeroMemory(&lvitm,sizeof(lvitm));
	lvitm.mask=LVIF_TEXT;
	lvitm.iItem=ret;
	
	CString str;
	
	if(tag_flags&TAG_TYPE)
	{
		lvitm.iSubItem=list->GetColWithID(TAGI_TYPE);
		str=GetTypeName();
		lvitm.pszText=str.GetBuffer(0);
		list->SetItem(&lvitm);
	}
	
	
	if(tag_flags&TAG_HOVER_ELEVATION)
	{
		lvitm.iSubItem=list->GetColWithID(TAGI_HOVER_ELEVATION);
		str.Format("%0.2fm",GetHoverElevation());
		lvitm.pszText=str.GetBuffer(0);
		list->SetItem(&lvitm);
	}
	
	if(tag_flags&TAG_MODIFIED)
	{
		lvitm.iSubItem=list->GetColWithID(TAGI_MODIFIED);
		str.Format("%s",IsModified()?"Yes":"No");
		lvitm.pszText=str.GetBuffer(0);
		list->SetItem(&lvitm);
	}
	
	
	list->SetItemState(ret,IsSelected()?LVIS_SELECTED:0,LVIS_SELECTED);
	return ret;
}


void EdGUIObject::DrawSelectedObjects(DrawInfo &dinf,bool drawTags, unsigned long tag_flags)
{
	if(IsHidden()) return ;
	
	if(IsSelected()) 
	{
		Draw(dinf);
		if(drawTags) DrawTag(dinf,tag_flags);
	}
	
}

void EdGUIObject::DrawUnSelectedObjects(DrawInfo &dinf,bool drawTags, unsigned long tag_flags)
{
	if(IsHidden()) return ;
	
	if(!IsSelected()) 
	{
		Draw(dinf);
		if(drawTags) DrawTag(dinf,tag_flags);
	}
	
}

void EdGUIObject::OffsetSelection(Point3D &vct) 
{
	if(IsHidden() || !IsSelected()) return;
	OffsetBy(vct);
}

void EdGUIObject::OffsetBy( Point3D &pnt)
{
	if(!IsHidden()) 
	{
		if(GetSnapToGround()) HoverElevation+=pnt.y;
		BaseEdObject::OffsetBy(pnt);
	}
	
}



void EdGUIObject::RotateSelection(UnitQuaternion &vct)
{
	if(IsHidden() || !IsSelected()) return;
	RotateBy(vct);
}



bool EdGUIObject::GetSelectionExtents(Point3D *tl,Point3D *br)
{
	if(IsSelected()) 
	{
		GetExtents(tl,br);
		return true;
	}
	else
		return false;
}

void EdGUIObject::GetExtents(Point3D *tl,Point3D *br)
{
	Scalar rad=GetRadius();
	*br=*tl=GetPos();
	tl->x-=rad;
	tl->y-=rad;
	tl->z-=rad;
	
	br->x+=rad;
	br->y+=rad;
	br->z+=rad;
}


void EdGUIObject::RotateSelectionAround(Point3D &pnt,UnitQuaternion &vct)
{
	if(IsSelected()) RotateAround(pnt,vct);
}

int EdGUIObject::GetScreenRadius(ViewWindowInfo &vinf)
{
	
	if(IsVisual())
	{
		Point3D pnt(GetRadius(),0.0f,0.0f);
		CPoint spnt=vinf.DataToScreen(pnt);
		return spnt.x;
	}
	else
	{
		CSize csz;
		int size,rad;
		
		csz=vinf.GetClientSize();
		
		size=csz.cx>csz.cy?csz.cx:csz.cy;
		
		rad=size/ScreenDiv;
		
		// Calulate Object Radius Based on how it was drawn;
		float oval;	
		CPoint spnt;
		Point3D gpnt;
		spnt.x=spnt.y=0;
		gpnt=vinf.ScreenToData(spnt);
		oval=gpnt.x;
		spnt.x=rad;
		gpnt=vinf.ScreenToData(spnt);
		RadiusSqu=(float)fabs(gpnt.x-oval);
		RadiusSqu*=RadiusSqu;
		
		return rad;
	}
	
}


bool EdGUIObject::SelectNext(Point3D &pnt,bool found)
{
	if(!InRadius(pnt)) return false;
	
	if(found)
	{
		if(!IsSelected()) {Select(); return true;}
	}
	else
		if(IsSelected()) {DeSelect(); return true;}
		
		return false;
}

EdGUIObject *EdGUIObject::SelectClosestInternal(Point3D &pnt,Scalar *crat)	
{
	if(IsHidden() || IsSelected()) return false;
	
	Point3D loc=GetPos();
	Scalar dist=(pnt.x-loc.x)*(pnt.x-loc.x)+(pnt.z-loc.z)*(pnt.z-loc.z);
	
	if(dist<=RadiusSqu)
	{	// If inside
		Scalar mycrat=1.0f-(dist/RadiusSqu);
		if(mycrat>*crat) 
		{	// Closer than the Other
			*crat=mycrat;
			return this;
		}
	}
	
	return NULL;
}

bool EdGUIObject::SelectClosest(Point3D &pnt)
{
	Scalar crat=-1.0f;
	EdGUIObject *gobj=SelectClosestInternal(pnt,&crat);
	if(gobj) {gobj->Select(); return true;}
	return false;
}

void EdGUIObject::UpdateMat()
{
	
	if(SnapToGround)
	{
		Point3D trans=GetPos();
		trans.y=ComputeTerrainElevation(trans.x,trans.z)+HoverElevation;
		SetPosNoUpdate(trans);
	}
	
	BaseEdObject::UpdateMat();
	
	Modify();
}

void EdGUIObject::SaveTextInfo(NotationFile *not_file)
{
	if((HoverElevation==0.0f) && GetSnapToGround() && ((GUIObjectList *)GetMyMission()==Parent)) return ;
	
	Page *page=not_file->SetPage((char *)(LPCSTR)GetName());
	page->SetEntry("HoverElevation",HoverElevation);
	page->SetEntry("SnapToGround",GetSnapToGround());
	page->SetEntry("Type","Helper");
	
}



void EdGUIObject::AddPanel(CObjPanelDlg *pnl,UndoCommand **cmd)
{
	pnl->AddPanel(new CTNPanel(this,cmd,pnl));
	pnl->AddPanel(new GUIObjPanel(this,cmd,pnl));
}

void EdGUIObject::SetInfo(Page *page)	
{
	page->GetEntry("HoverElevation",&HoverElevation);
	page->GetEntry("SnapToGround",&SnapToGround);
}

CString EdGUIObject::GetTag(unsigned long tag_flags)
{
	CString rv,ns;
	
	if ((tag_flags & TAG_NAME) > 0)
	{
		rv += GetName();
		rv += "\n";
	}
	
	if ((tag_flags & TAG_HOVER_ELEVATION) > 0)
	{
		ns.Format("Hover: %.2f\n",HoverElevation);
		rv+=ns;
	}
	
	if ((tag_flags & TAG_MODIFIED) > 0)
	{
		if (IsModified() == true)
		{
			rv += "**MODIFIED**\n";
		}
		else
		{
			rv += "Not Modified\n";
		}
	}
	
	return rv;
}

void EdGUIObject::MoveToGroup(EdGUIObject *gobj) 
{
	Verify(Parent!=NULL);
	GUIObjectList *group=Parent->PlaceInGroup(this);
	if(group!=NULL)
		group->MoveToGroup(gobj);
}

void EdGUIObject::CopySelectionToList(GUIObjectList *list)
{
	
	if(IsSelected())
	{
		EdGUIObject *gobj=Clone();
		if(gobj)
		{
		list->AddObject(gobj);
		gobj->RemoveFromMap();
		}
	}
	
}

void  EdGUIObject::SetName( CString &name)
{
	CString tnme=name;
	for(int i=0;i<tnme.GetLength();i++)
	if(!isdigit(tnme[i]) && !isalpha(tnme[i]) && tnme[i]==' ' && tnme[i]=='_')
	{
	tnme.SetAt(i,'_');
	}

	if(tnme.Compare(Name))
	{
		if(Name!="" && !NameManager::Instance->IsUnique(Name)) NameManager::Instance->RemoveString(Name);
		Name=NameManager::Instance->MakeUnique(tnme);
		Modify();
	}

}


ObjectManager *EdGUIObject::GetMyMission()
{
	if(Parent) return Parent->GetMyMission();
	return NULL;
}

void EdGUIObject::SnapObjectToGround(bool overrideflag)
{
	if(SnapToGround || overrideflag)
	{
	bool oldsnap=SnapToGround;
	SnapToGround=true;
	UpdateMat();
	SnapToGround=oldsnap;
	}
}

void EdGUIObject::GetMultiProps(CObjProps *props)
{
	props->SetModTagFrom(this);
}

void EdGUIObject::SetMultiProps(CObjProps *props,UndoCommand **cmd)
{
	props->SetData(this,cmd);
}

void EdGUIObject::SavePos(UndoCommand **cmd)
{
	MoveCommand *mcom=new MoveCommand(this);
	if(*cmd==NULL) *cmd=mcom; else (*cmd)->Attach(mcom);
}

void EdGUIObject::SaveRot(UndoCommand **cmd)
{
	RotateCommand *mcom=new RotateCommand(this);
	if(*cmd==NULL) *cmd=mcom; else (*cmd)->Attach(mcom);
}

void EdGUIObject::SaveGroup(UndoCommand **cmd)
{
	GroupCommand *mcom=new GroupCommand(this);
	if(*cmd==NULL) *cmd=mcom; else (*cmd)->Attach(mcom);
	
}

void EdGUIObject::SavePosIfSelected(UndoCommand **cmd)
{
	if(IsSelected() && !IsHidden())
	{
		SavePos(cmd);
	}
}

void EdGUIObject::SaveRotIfSelected(UndoCommand **cmd)
{
	if(IsSelected() && !IsHidden())
	{
		SaveRot(cmd);
	}
}

bool EdGUIObject::UnGroupSelected(UndoCommand **cmd)
{
	if(IsSelected())
	{
		Verify(Parent!=NULL);
		SaveGroup(cmd);
		GUIObjectList *par=GetParent();
		GUIObjectList *grand_par=par->GetParent();
		
		if(grand_par!=NULL)
		{
			par->RemoveObject(this);
			grand_par->AddObject(this);
		} 
		return true;
	}
	return false;
}

void EdGUIObject::SetSnapToGround(bool snap,bool preservepos) 
{
if(preservepos) 
{
	Point3D pos=GetPos();
	HoverElevation=pos.y;
}

SnapToGround=snap; 
UpdateMat();
}

EdGUIObject *EdGUIObject::GetObjectByName(CString name)
{
if(!name.CompareNoCase(GetName())) return this;
return NULL;
}


// Helper Functions
int _cdecl CompareGUIObjs(const void *elem1, const void *elem2)
{
	EdGUIObject *obj1,*obj2;
	obj1=*((EdGUIObject **)elem1);
	obj2=*((EdGUIObject **)elem2);
	return obj1->GetName().CompareNoCase(obj2->GetName());
}


