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

#include "stdafx.h"
#include "MW4GameEd2.h"
#include "GUINode.h"
#include "DrawInfo.h"
#include "GUIPath.h"
#include "GameInterface.h"
#include <MW4\Path.hpp>
#include <Adept\Entity.hpp>
#include <Adept\EntityClassData.hpp>
#include <Adept\Resource.hpp>
#include <Adept\Map.hpp>

using namespace Adept;
using namespace MechWarrior4;

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



//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Path

GUIPath::GUIPath(CString &name):GUIObjectList(name)
{
	MyPath=NULL;
	SetName(name);
	Build();	
	
	AddToMap();
}

void GUIPath::Build()
{
	Adept::Resource res((LPCSTR)"Misc\\Path\\Path.Instance");
	res.LoadData();
	Check_Object(Connection::Local);
	ReplicatorID base_id = Connection::Local->GetNextReplicatorID();
	MyPath = Cast_Object(Path*,Entity::CreateEntity(&res,&base_id));
	Check_Object(MyPath);
	MyPath->instanceName=(LPCSTR)GetName();

}

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

void GUIPath::Copy(GUIPath &obj)
{
	GUIObjectList::Copy(obj);

	if(MyPath)
	{
		RemoveFromMap();
		delete Cast_Object(Stuff::Plug *,MyPath);
		MyPath=NULL;
	}
	
	Build();

	Check_Object(MyPath);

	for(int i=0;i<GetObjectCount();i++)
	{
		MyPath->pathPoints.push_back(GetObject(i)->GetPos());
	}

		Verify(MyPath->pathPoints.size()==GetObjectCount());

}

GUIPath::GUIPath():GUIObjectList("Path")
{
	MyPath=NULL;
	InMap=false;
}

void GUIPath::AssociateTo(MechWarrior4::Path *path)
{
	MyPath=path;
	Check_Object(MyPath);
	
	CString name=(char *)MyPath->instanceName;
	InMap=true;	
	EdGUIObject::SetName(name);
	CString node_name=GetName()+"Node";
	
	int i,size=MyPath->pathPoints.size();
	
	for(i=0;i<size;i++)
	{
		AddObject(new GUINode(node_name,MyPath->pathPoints[i]));
	}
	
}


void GUIPath::SetName(CString &name)
{
	EdGUIObject::SetName(name);
	if(InMap && MyPath!=NULL)
	{
		Check_Object(MyPath);
		RemoveEntityFromNameTable(MyPath);
		MyPath->instanceName=(LPCSTR)GetName();	
		AddEntityToNameTable(MyPath);
	}
	
	for(int i=0;i<GetObjectCount();i++) GetObject(i)->SetName(GetName()+"Node");
}


EdGUIObject *GUIPath::AddNodeAtEnd(Point3D &pnt)
{
	CString node_name=GetName()+"Node";
	GUINode *ret;
	AddObject(ret=new GUINode(node_name,pnt));
	if(MyPath!=NULL)
	{
		Check_Object(MyPath);
		MyPath->pathPoints.push_back(pnt);
		Verify(MyPath->pathPoints.size()==GetObjectCount());
	}
	return ret;
}

void GUIPath::Draw(DrawInfo &dinf)
{
	if(IsHidden()) return;
	SetPen(dinf);
	
	if(GetObjectCount()>0)
	{
		dinf.GetDC()->MoveTo(dinf.vinf.DataToScreen(GetObject(0)->GetPos()));
		for(int i=1;i<GetObjectCount();i++) 
			dinf.GetDC()->LineTo(dinf.vinf.DataToScreen(GetObject(i)->GetPos()));
	}
	
	GUIObjectList::Draw(dinf);
	
}

bool GUIPath::DeleteSelected(UndoCommand **delcom)
{
	bool ret=GUIObjectList::DeleteSelected(delcom);
	SyncData();
	return ret;
}

EdGUIObject *GUIPath::InsertNodeAt(int idx,Point3D &pnt)
{
	if(IsHidden()) return NULL;
	GUINode *ret;
	InsertObjectAt(idx,ret=new GUINode(GetName()+"Node",pnt));
	return ret;
}

void GUIPath::InsertObjectAt(int idx,EdGUIObject *gobj)
{
	GUIObjectList::InsertObjectAt(idx,gobj);
	if(MyPath!=NULL)
	{
		Check_Object(MyPath);
		MyPath->pathPoints.push_back(gobj->GetPos());
		Verify(MyPath->pathPoints.size()==GetObjectCount());
		SyncDataToNodes();
	}
}

void GUIPath::SaveTextInfo(NotationFile *not_file)
{
	Page *page=not_file->SetPage((char *)(LPCSTR)GetName());
	page->SetEntry("Type","Path");
}


GUIPath::~GUIPath()
{
	DeleteAllObjects();
	
	if(MyPath)
	{
		RemoveFromMap();
		delete Cast_Object(Stuff::Plug *,MyPath);
	}
}


EdGUIObject *GUIPath::AddNodeIfSelected(Point3D &pnt,bool insert)
{
	if(IsHidden()) return NULL;
	if(!IsSelected()) return NULL;
	
	if(GetObjectCount()<=1 || !insert) 
	{
		return AddNodeAtEnd(pnt);
	}
	
	EdGUIObject *ret;
	
	Point3D cp,np;
	Scalar lowdist,d;
	int lowpoint,i;
	
	lowdist=(Scalar)sqrt(GetObject(0)->DistSquTo(pnt));
	lowpoint=0;
	
	
	np=GetObject(0)->GetPos();
	
	for(i=1;i<GetObjectCount();i++)
	{
		cp=np;
		np=GetObject(i)->GetPos();
		d=DistToPoint(cp,np,pnt);
		
		if(d<lowdist)
		{
			lowdist=d;
			lowpoint=i;
		}
	}
	
	d=(Scalar)sqrt(GetObject(GetObjectCount()-1)->DistSquTo(pnt));
	
	if(d<=lowdist)
	{
		ret=AddNodeAtEnd(pnt);
	}
	else
	{
		ret=InsertNodeAt(lowpoint,pnt);
	}
	
	return ret;
	
}

void GUIPath::SyncNodesToData()
{
	Check_Object(MyPath);
	int i,size=MyPath->pathPoints.size();
	Verify(size==GetObjectCount());
	
	GUINode *gobj;		
	for(i=0;i<size;i++)
	{
		gobj=(GUINode *)GetObject(i);
		gobj->SetPos(MyPath->pathPoints[i]);
	}
	
	for(i=0;i<GetObjectCount();i++) GetObject(i)->SetName(GetName()+"Node");
	
}

void GUIPath::SyncDataToNodes()
{
	Check_Object(MyPath);
	int i,size=MyPath->pathPoints.size();
	Verify(size==GetObjectCount());
	
	GUINode *gobj;		
	for(i=0;i<size;i++)
	{
		gobj=(GUINode *)GetObject(i);
		MyPath->pathPoints[i]=gobj->GetPos();
	}
	
}

void GUIPath::OffsetSelection(Point3D &vct)
{
	if(IsHidden()) return ;
	GUIObjectList::OffsetSelection(vct);
	SyncDataToNodes();
}

void GUIPath::RemoveObject(int idx)
{
	GUIObjectList::RemoveObject(idx);
	
	Check_Object(MyPath);
	MyPath->ErasePoint(idx);
	SyncDataToNodes();
}

void GUIPath::RemoveFromMap()
{
	if(InMap)
		RemoveEntityFromMap(MyPath);
	GUIObjectList::RemoveFromMap();
}

void GUIPath::AddToMap()
{
	if(!InMap)
		AddEntityToMap(MyPath,GetMat());
	GUIObjectList::AddToMap();
}

HTREEITEM GUIPath::AddToBranch(CTreeCtrl *tree,HTREEITEM itm)
{
	return EdGUIObject::AddToBranch(tree,itm);
}


//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Bound

GUIBound::GUIBound(CString name):GUIObjectList(name)
{
}

void GUIBound::SetBoundData(DynamicArrayOf<Stuff::Vector2DOf<Stuff::Scalar> > &BDat)
{
	DeleteAllObjects();
	MyBound=BDat;
	
	int i;
	Point3D pnt;
	pnt.y=0.0f;
	CString node_name=GetName()+"_Node";
	
	for(i=0;i<MyBound.GetLength();i++)
	{
		pnt.x=MyBound[i].x;
		pnt.z=MyBound[i].y;
		AddObject(new GUINode(node_name,pnt));
	}
}

bool GUIBound::DeleteSelected(UndoCommand **delcom)
{
	if(GetObjectCount()<=3) return false;
	bool ret=GUIObjectList::DeleteSelected(delcom);
	SyncData();
	return ret;
}



void GUIBound::Draw(DrawInfo &dinf)
{
	if(IsHidden()) return;
	SetPen(dinf);
	
	if(GetObjectCount()>0)
	{
		dinf.GetDC()->MoveTo(dinf.vinf.DataToScreen(GetObject(0)->GetPos()));
		for(int i=1;i<GetObjectCount();i++) 
			dinf.GetDC()->LineTo(dinf.vinf.DataToScreen(GetObject(i)->GetPos()));
		
		dinf.GetDC()->LineTo(dinf.vinf.DataToScreen(GetObject(0)->GetPos()));
	}
	
	GUIObjectList::Draw(dinf);
}

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


EdGUIObject *GUIBound::AddNodeIfSelected(Point3D &pnt,bool insert)
{
	if(IsHidden()) return NULL;
	if(!IsSelected()) return NULL;
	
	Point3D cp,np;
	Scalar lowdist,d;
	int lowpoint,i;
	
	cp=GetObject(0)->GetPos();
	np=GetObject(1)->GetPos();
	lowdist=DistToPoint(cp,np,pnt);
	lowpoint=1;
	
	for(i=2;i<GetObjectCount();i++)
	{
		cp=np;
		np=GetObject(i)->GetPos();
		d=DistToPoint(cp,np,pnt);
		
		if(d<lowdist)
		{
			lowdist=d;
			lowpoint=i;
		}
	}
	
	cp=np;
	np=GetObject(0)->GetPos();
	d=DistToPoint(cp,np,pnt);
	
	if(d<lowdist)
	{
		lowdist=d;
		lowpoint=0;
	}
	
	
	return InsertNodeAt(lowpoint,pnt);
	
}

void GUIBound::RemoveObject(int idx)
{
	GUIObjectList::RemoveObject(idx);
	
	MyBound.SetLength(MyBound.GetLength()-1);
	SyncDataToNodes();
}


EdGUIObject *GUIBound::InsertNodeAt(int idx,Point3D &pnt)
{
	if(IsHidden()) return NULL;
	GUINode *ret;
	CString node_name=GetName()+"_Node";
	InsertObjectAt(idx,ret=new GUINode(node_name,pnt));
	return ret;
}

void GUIBound::OffsetSelection(Point3D &vct)
{
	if(IsHidden()) return;
	GUIObjectList::OffsetSelection(vct);
	if(!IsConcave())
	{
		
		Point3D pnt2;
		pnt2.x=-vct.x;
		pnt2.y=-vct.y;
		pnt2.z=-vct.z;
		GUIObjectList::OffsetSelection(pnt2);
	}
	
	if(!IsConcave())
	{
		Point3D tl,br;
		GetExtents(&tl,&br);
		ResetTo(tl,br);
	}
	
	SyncDataToNodes();
}

bool GUIBound::IsConcave()
{
	int i;
	
	Point3D p1,p2,p3;
	p1=GetObject(GetObjectCount()-1)->GetPos();
	p2=GetObject(GetObjectCount()-2)->GetPos();
	p2.y=p1.y=0;
	
	Vector3D v1,v2,rvct;
	int sign=-1;
	
	for(i=0;i<GetObjectCount();i++)
	{
		p3=p2;
		p2=p1;
		p1=GetObject(i)->GetPos();
		p1.y=0;
		/*
		p1.x/=(float)fabs(p1.x);
		p2.x/=(float)fabs(p1.x);
		p3.x/=(float)fabs(p1.x);
		p1.y/=(float)fabs(p1.x);
		p2.y/=(float)fabs(p1.x);
		p3.y/=(float)fabs(p1.x);
*/
		v1.Subtract(p3,p2);
		v2.Subtract(p1,p2);
		rvct.Cross(v1,v2);
		
		if(rvct.y!=0.0f)
		{
		
		if(sign==-1)
			{
			sign=rvct.y>0?1:0;
			}

		if((sign==1 && rvct.y<0) || (sign==0 && rvct.y>0) ) 
			return false;
		}
		
		
	}
	
	return true;
}



void GUIBound::SyncNodesToData()
{
	int i,size=MyBound.GetLength();
	Verify(size==GetObjectCount());
	
	GUINode *gobj;		
	for(i=0;i<size;i++)
	{
		Point3D pnt;
		pnt.y=0.0f;
		pnt.x=MyBound[i].x;
		pnt.z=MyBound[i].y;
		gobj=(GUINode *)GetObject(i);
		gobj->SetPos(pnt);
	}
	
	
}

void GUIBound::SyncDataToNodes()
{
	int i,size=MyBound.GetLength();
	Verify(size==GetObjectCount());
	
	Point3D pnt;
	for(i=0;i<size;i++)
	{
		pnt=GetObject(i)->GetPos();
		MyBound[i].x=pnt.x;
		MyBound[i].y=pnt.z;
	}
	
}

void GUIBound::ResetTo(Point3D tl,Point3D br)
{
	MyBound.SetLength(4);
	
	MyBound[0].x=tl.x;
	MyBound[0].y=tl.z;
	
	MyBound[3].x=br.x;
	MyBound[3].y=tl.z;
	
	MyBound[2].x=br.x;
	MyBound[2].y=br.z;
	
	MyBound[1].x=tl.x;
	MyBound[1].y=br.z;
	
	SetBoundData(MyBound);
}


void GUIBound::SaveTextInfo(NotationFile *not_file)
{
	Page *page=not_file->SetPage((char *)(LPCSTR)GetName());
	page->SetEntry("Type","Bound");
}

GUIBound::~GUIBound()
{
	DeleteAllObjects();
}

void GUIBound::InsertObjectAt(int idx,EdGUIObject *gobj)
{
	GUIObjectList::InsertObjectAt(idx,gobj);
	MyBound.SetLength(MyBound.GetLength()+1);
	SyncDataToNodes();
}

// >>>>>>>>>>>>>>>>>>>>>>>>>>>>> Global Helper Functions


