#include "c4d_memory.h"
#include "c4d_basecontainer.h"
#include "c4d_baseobject.h"
#include "c4d_basetag.h"
#include "c4d_thread.h"
#include "c4d_tools.h"
#include "c4d_includes.h"
#include "c4d_baseselect.h"

Bool BaseObject::SetPhong(Bool on, Bool anglelimit, Real angle)
{
	KillTag(Tphong);
	if (!on) return TRUE;
	BaseTag *tag=MakeTag(Tphong);
	if (!tag) return FALSE;
	BaseContainer bc;
	bc.SetBool(PHONGTAG_PHONG_ANGLELIMIT,anglelimit);
	bc.SetReal(PHONGTAG_PHONG_ANGLE,angle);
	tag->SetData(bc,FALSE);
	return TRUE;
}

Real BaseObject::GetVisibility(Real parent)
{
	return C4DOS.Bo->GetVisibility(this,parent);
}

void BaseObject::SetIsoparm(LineObject *l)
{
	C4DOS.Bo->SetIsoparm(this,l);
}

Matrix BaseObject::GetMln(void)
{
	return C4DOS.Bo->GetMln(this);
}

Matrix BaseObject::GetMgn(void)
{
	return C4DOS.Bo->GetMgn(this);
}

LONG BaseObject::GetUniqueIP(void)
{
	return C4DOS.Bo->GetUniqueIP(this);
}

void BaseObject::SetUniqueIP(LONG ip)
{
	C4DOS.Bo->SetUniqueIP(this,ip);
}

LONG BaseObject::GetEditorMode(void)
{
	return C4DOS.Bo->GetMode(this,MODE_EDITOR);
}

void BaseObject::SetEditorMode(LONG mode)
{
	C4DOS.Bo->SetMode(this,MODE_EDITOR,mode);
}

LONG BaseObject::GetRenderMode(void)
{
	return C4DOS.Bo->GetMode(this,MODE_RENDER);
}

void BaseObject::SetRenderMode(LONG mode)
{
	C4DOS.Bo->SetMode(this,MODE_RENDER,mode);
}

Bool BaseObject::GetDeformMode(void)
{
	return C4DOS.Bo->GetMode(this,MODE_DEFORM)!=0;
}

void BaseObject::SetDeformMode(Bool mode)
{
	C4DOS.Bo->SetMode(this,MODE_DEFORM,mode);
}

void BaseObject::InsertTag(BaseTag *tp, BaseTag *pred)
{
	C4DOS.Bo->InsertTag(this,tp,pred);
}

void BaseObject::KillTag (LONG type, LONG nr)
{
	C4DOS.Bo->KillTag(this,type,nr);
}

BaseTag *BaseObject::GetFirstTag(void)
{
	return C4DOS.Bo->GetFirstTag(this);
}

Bool BaseObject::CopyTagsTo(BaseObject *dest, LONG visible, LONG variable, LONG hierarchical, AliasTrans *trans)
{
	return C4DOS.Bo->CopyTagsTo(this,dest,visible,variable,hierarchical,trans);
}

BaseTag *BaseObject::MakeTag(LONG type, BaseTag *pred)
{
	BaseTag *tag = BaseTag::Alloc(type);
	if (tag) InsertTag(tag,pred);
	return tag;
}

VariableTag *BaseObject::MakeVariableTag(LONG type, LONG anz, BaseTag *pred)
{
	BaseTag *tag = VariableTag::Alloc(type,anz);
	if (tag) InsertTag(tag,pred);
	return (VariableTag*)tag;
}

BaseSelect *PointObject::GetPointS(void)
{
	return C4DOS.Bo->PoGetPointS(this);
}

BaseSelect *PointObject::GetPointH(void)
{
	return C4DOS.Bo->PoGetPointH(this);
}

Bool PointObject::ResizeObject(LONG pcnt)
{
	return C4DOS.Bo->PoResizeObject(this,pcnt);
}

Real *PointObject::CalcVertexMap(BaseObject *modifier)
{
	return C4DOS.Bo->PoCalcVertexMap(this,modifier);
}

Bool SplineObject::ResizeObject(LONG pcnt, LONG scnt)
{
	return C4DOS.Bo->SpResizeObject(this,pcnt,scnt);
}

Bool LineObject::ResizeObject(LONG pcnt, LONG scnt)
{
	return C4DOS.Bo->LoResizeObject(this,pcnt,scnt);
}

BaseSelect *PolygonObject::GetEdgeS(void)
{
	return C4DOS.Bo->PyGetEdgeS(this);
}

BaseSelect *PolygonObject::GetEdgeH(void)
{
	return C4DOS.Bo->PyGetEdgeH(this);
}

BaseSelect *PolygonObject::GetPhongBreak(void)
{
	return C4DOS.Bo->PyGetPhongBreak(this);
}

BaseSelect *PolygonObject::GetPolygonS(void)
{
	return C4DOS.Bo->PyGetPolygonS(this);
}

BaseSelect *PolygonObject::GetPolygonH(void)
{
	return C4DOS.Bo->PyGetPolygonH(this);
}

Bool PolygonObject::ResizeObject(LONG pcnt, LONG vcnt)
{
	return C4DOS.Bo->PyResizeObject(this,pcnt,vcnt);
}

LONG SplineObject::GetInterpolationType(void)
{
	LONG type=GetDataInstance()->GetLong(SPLINEOBJECT_TYPE);
 	if (GetPointCount()>2 || type==Thermite)
		return type;
	else
		return Tlinear;
}

Bool SplineObject::IsClosed(void)
{
	return GetDataInstance()->GetBool(SPLINEOBJECT_CLOSED);
}

Vector SplineObject::GetSplinePoint(Real t, LONG segment, Vector *padr)
{
	return C4DOS.Bo->SpGetSplinePoint(this,t,segment,padr);
}

Vector SplineObject::GetSplineTangent(Real t, LONG segment, Vector *padr)
{
	return C4DOS.Bo->SpGetSplineTangent(this,t,segment,padr);
}

Bool SplineObject::InitLength(LONG segment, Vector *padr)
{
	return C4DOS.Bo->SpInitLength(this,segment,padr);
}

void SplineObject::FreeLength(void)
{
	C4DOS.Bo->SpFreeLength(this);
}

Real SplineObject::UniformToNatural(Real t)
{
	return C4DOS.Bo->SpUniformToNatural(this,t);
}

Real SplineObject::GetLength(void)
{
	return C4DOS.Bo->SpGetLength(this);
}

Real SplineObject::GetSegmentLength(LONG a, LONG b)
{
	return C4DOS.Bo->SpGetSegmentLength(this,a,b);
}

LineObject *SplineObject::GetLineObject(BaseDocument *doc, Real lod, BaseThread *thread)
{
	return C4DOS.Bo->SpGetLineObject(this,doc,lod,thread);
}

Bool SplineObject::SetDefaultCoeff(void)
{
	return C4DOS.Bo->SpSetDefaultCoeff(this);
}

SplineObject *BaseObject::GetRealSpline(void)
{
	return C4DOS.Bo->SpGetRealSpline(this);
}

Safety::Safety(void)
{
	os=NULL;
}

Safety::~Safety(void)
{
	C4DOS.Bo->OsFree(os,TRUE);
}

Bool Safety::Init(BaseObject *op)
{
	os=C4DOS.Bo->OsAlloc(op);
	return os!=NULL;
}

void Safety::Detach(void)
{
	C4DOS.Bo->OsFree(os,FALSE);
}

LONG CameraObject::GetProjection(void)
{
	return C4DOS.Bo->CoGetProjection(this);
}

Real CameraObject::GetFocus(void)
{
	return C4DOS.Bo->CoGetFocus(this);
}

Real CameraObject::GetZoom(void)
{
	return C4DOS.Bo->CoGetZoom(this);
}

Vector CameraObject::GetOffset(void)
{
	return C4DOS.Bo->CoGetOffset(this);
}

Real CameraObject::GetAperture(void)
{
	return C4DOS.Bo->CoGetAperture(this);
}

Bool CameraObject::SetProjection(LONG projection)
{
	return C4DOS.Bo->CoSetProjection(this,projection);
}

Bool CameraObject::SetFocus(Real v)
{
	return C4DOS.Bo->CoSetFocus(this,v);
}

Bool CameraObject::SetAperture(Real v)
{
	return C4DOS.Bo->CoSetAperture(this,v);
}

Bool CameraObject::SetZoom(Real v)
{
	return C4DOS.Bo->CoSetZoom(this,v);
}

Bool CameraObject::SetOffset(const Vector &offset)
{
	return C4DOS.Bo->CoSetOffset(this,offset);
}

BaseObject *GeneratePrimitive(BaseDocument *doc, LONG type, const BaseContainer &bc, Real lod, Bool isoparm, BaseThread *bt)
{
	return C4DOS.Bo->GenPrimitive(doc,type,&bc,lod,isoparm,bt);
}

BaseObject *GenerateSplinePrimitive(BaseDocument *doc, LONG type, const BaseContainer &bc, Real lod, BaseThread *bt)
{
	return C4DOS.Bo->GenSplinePrimitive(doc,type,&bc,lod,bt);
}

LONG CalcLOD(LONG val, Real lod, LONG min, LONG max)
{
	val=LONG(Real(val)*lod+0.001);
	return LCut(val,min,max);
}

Bool DisjointMesh(PointObject *op)
{
	Bool		same;
	LONG		pcnt=op->GetPointCount(),i,npcnt=0;
	Vector	*padr=op->GetPoint(),*opadr=NULL,*sttpadr=NULL,*xadr=NULL;

	if (pcnt==0) return TRUE;

	if (op->GetType()==Opolygon)
	{
		StickTextureTag *stt=(StickTextureTag*)op->GetTag(Tsticktexture);
		LONG		vcnt=ToPoly(op)->GetPolygonCount();
		Polygon	*vadr=ToPoly(op)->GetPolygon();

		for (i=0; i<vcnt; i++)
		{
			if (vadr[i].c==vadr[i].d)
				npcnt+=3;
			else
				npcnt+=4;
		}

		if (stt && !stt->Record(op,FALSE)) goto Error;
		if (stt && stt->GetDataAddress() && stt->GetDataCount()==pcnt)
		{
			sttpadr=(Vector*)GeAlloc(sizeof(Vector)*pcnt); if (!sttpadr) goto Error;
			CopyMem(stt->GetDataAddress(),sttpadr,sizeof(Vector)*pcnt);
		}

		opadr=(Vector*)GeAlloc(sizeof(Vector)*pcnt); if (!opadr) goto Error;
		CopyMem(padr,opadr,sizeof(Vector)*pcnt);

		if (!ToPoly(op)->ResizeObject(npcnt,NOTOK)) goto Error;
		if (stt && !stt->Record(op,TRUE)) goto Error;

		xadr=stt?(Vector*)stt->GetDataAddress():NULL;
		padr=op->GetPoint();
		npcnt=0;
		for (i=0; i<vcnt; i++)
		{
			same=vadr[i].c!=vadr[i].d;

			if (sttpadr)
			{
				xadr[npcnt+0] = sttpadr[vadr[i].a];
				xadr[npcnt+1] = sttpadr[vadr[i].b];
				xadr[npcnt+2] = sttpadr[vadr[i].c];
				if (same)
					xadr[npcnt+3] = sttpadr[vadr[i].d];
			}

			padr[npcnt] = opadr[vadr[i].a]; vadr[i].a=npcnt++;
			padr[npcnt] = opadr[vadr[i].b]; vadr[i].b=npcnt++;
			padr[npcnt] = opadr[vadr[i].c]; vadr[i].c=npcnt++;

			if (same)
			{
				padr[npcnt] = opadr[vadr[i].d]; vadr[i].d=npcnt++;
			}
			else
				vadr[i].d=vadr[i].c;
		}

		GeFree(sttpadr);
	}
	else if (op->GetType()==Oline)
	{
		Segment	*sadr=((LineObject*)op)->GetSegment();
		LONG		scnt=((LineObject*)op)->GetSegmentCount(),off,j;

		for (i=0; i<scnt; i++)
		{
			if (sadr[i].closed)
				npcnt+=sadr[i].cnt*2;
			else
				npcnt+=(sadr[i].cnt-1)*2;
		}

		opadr=(Vector*)GeAlloc(sizeof(Vector)*pcnt);
		if (!opadr) return FALSE;

		CopyMem(padr,opadr,sizeof(Vector)*pcnt);

		if (!((LineObject*)op)->ResizeObject(npcnt,npcnt/2))
		{
			GeFree(opadr);
			return FALSE;
		}

		padr=op->GetPoint();
		sadr=((LineObject*)op)->GetSegment();
		npcnt=0;
		off=0;
		for (i=0; i<scnt; i++)
		{
			for (j=0; j+1<sadr[i].cnt; j++)
			{
				padr[npcnt++] = opadr[off+j];
				padr[npcnt++] = opadr[off+j+1];
			}
			if (sadr[i].closed)
			{
				padr[npcnt++] = opadr[off+j];
				padr[npcnt++] = opadr[off];
			}

			off+=sadr[i].cnt;
		}

		for (i=0; i<npcnt/2; i++)
		{
			sadr[i].closed = FALSE;
			sadr[i].cnt    = 2;
		}
	}
	GeFree(opadr);
	return TRUE;

Error:
	GeFree(opadr);
	GeFree(sttpadr);
	return FALSE;
}

BaseObject *PluginObject::GetAndCheckHierarchyClone(HierarchyHelp *hh, BaseObject *op, LONG flags, Bool *dirty, AliasTrans *trans, Bool allchilds)
{
	BaseObject *res=NULL,*tp=NULL,*dp=NULL;

	*dirty = *dirty || CheckCache(hh) || IsDirty(DIRTY_DATA);
	if (!(*dirty))
	{
		NewDependenceList();

		if (!allchilds)
			GetHierarchyClone(hh,op,flags,dirty,trans);
		else
		{
			for (tp=op;tp;tp=tp->GetNext())
				GetHierarchyClone(hh,tp,flags,dirty,trans);
		}
	}
	*dirty = *dirty || !CompareDependenceList();

	if (!(*dirty))
	{
		TouchDependenceList();
		return GetCache(hh);
	}

	NewDependenceList();
	if (!allchilds)
		res = GetHierarchyClone(hh,op,flags,NULL,trans);
	else
	{
		res = BaseObject::Alloc(Onull); if (!res) return NULL;
		res->SetName(op->GetName());
		for (tp=op;tp;tp=tp->GetNext())
		{
			dp = GetHierarchyClone(hh,tp,flags,NULL,trans);
			if (dp) dp->InsertUnderLast(res);
		}
	}
	return res;
}

Bool PluginObject::AddTexture(const Filename &fn, RootTextureString *priv)
{
	return C4DOS.Bo->AddTexture(this,&fn,priv);
}

void PluginObject::NewDependenceList(void)
{
	C4DOS.Bo->NewDependenceList(this);
}

Bool PluginObject::CompareDependenceList(void)
{
	return C4DOS.Bo->CmpDependenceList(this);
}

void PluginObject::TouchDependenceList(void)
{
	C4DOS.Bo->TouchDependenceList(this);
}

void PluginObject::AddDependence(HierarchyHelp *hh, BaseObject *op)
{
	C4DOS.Bo->AddDependence(this,hh,op);
}

BaseObject *PluginObject::GetHierarchyClone(HierarchyHelp *hh, BaseObject *op, LONG flags, Bool *dirty, AliasTrans *trans)
{
	return C4DOS.Bo->GetHierarchyClone(this,hh,op,flags,dirty,trans);
}

Neighbor::Neighbor(void)
{
	ee=NULL;
}

Neighbor::~Neighbor(void)
{
	C4DOS.Nb->Free(ee);
}

Bool Neighbor::Init(LONG pcnt, Polygon *vadr, LONG vcnt, BaseSelect *bs)
{
	C4DOS.Nb->Free(ee);
	if (!vadr) return TRUE;
	ee=C4DOS.Nb->Alloc(pcnt,vadr,vcnt,bs);
	return ee!=NULL;
}

LONG Neighbor::GetEdgeCount(void)
{
	if (!ee) return 0;
	return C4DOS.Nb->GetEdgeCount(ee);
}

LONG Neighbor::GetNeighbor(LONG a, LONG b, LONG poly)
{
  LONG l1, l2;
  GetEdgePolys(a, b, &l1, &l2);
  if (l1 == poly) return l2;
  else if (l2 == poly) return l1;
  return NOTOK;
}


SplineObject *FitCurve(Vector *padr, LONG pcnt, Real error, BaseThread *bt)
{
	return C4DOS.Bo->FitCurve(padr,pcnt,error,bt);
}

Bool Triangulate(Vector *padr, LONG pcnt, Polygon **vadr, LONG *vcnt)
{
	return C4DOS.Bo->Triangulate(padr,pcnt,vadr,vcnt);
}

Bool TriangulateStandard(Vector *padr, LONG pcnt, LONG *list, LONG lanz, Polygon *&vadr, LONG &vcnt, BaseThread *thread)
{
	return C4DOS.Bo->TriangulateStandard(padr,pcnt,list,lanz,vadr,vcnt,thread);
}

Bool TriangulateRegular(Vector *pinp, LONG pinp_cnt, LONG *list, LONG lanz, Vector *&padr, LONG &pcnt, Polygon *&vadr, LONG &vcnt, Real regular_width, BaseThread *thread)
{
	return C4DOS.Bo->TriangulateRegular(pinp,pinp_cnt,list,lanz,padr,pcnt,vadr,vcnt,regular_width,thread);
}

BaseObject *GenerateText(BaseContainer *cp, BaseThread *bt, Bool separate)
{	
	return C4DOS.Bo->GenerateText(cp,bt,separate);
}

PolygonObject *LineObject::Triangulate(Real regular, BaseThread *bt)
{
	return C4DOS.Bo->TriangulateLine(this,regular,bt);
}

BaseSelect* PolygonObject::GetSelectedEdges(Neighbor *e, EdgeSelectionType ltype)
{
	BaseSelect *xsel = BaseSelect::Alloc();
  BaseSelect *ope = NULL;
	if (ltype==EDGES_HIDDEN)
		ope=GetEdgeH();
	else if (ltype==EDGES_SELECTION)
		ope=GetEdgeS();
	else if (ltype==EDGES_PHONG)
		ope=GetPhongBreak();

	if (!xsel || !ope) return NULL;
  Polygon* p = GetPolygon();
  if (!p) return NULL;

	LONG i,j,a,b,edge;
	for (i=0; i<ope->GetSegments(); i++)
	{
		if (!ope->GetRange(i,&a,&b)) continue;
		for (j = a; j <= b; j++)
		{
      if ((j & 3) == 2 && p[j >> 2].c == p[j >> 2].d) continue;

			edge = e->GetPolyInfo(j>>2)->edge[j&3];
			if (edge != NOTOK)
				xsel->Select(edge);
		}
	}

	return xsel;
}

Bool PolygonObject::SetSelectedEdges(Neighbor *e, BaseSelect* pSel, EdgeSelectionType ltype)
{
  Polygon* pPolys = GetPolygon();
  if (!pPolys) return FALSE;

  BaseSelect *ope = NULL;
	if (ltype==EDGES_HIDDEN)
		ope=GetEdgeH();
	else if (ltype==EDGES_SELECTION)
		ope=GetEdgeS();
	else if (ltype==EDGES_PHONG)
		ope=GetPhongBreak();

  if (!ope) return FALSE;
  ope->FlushAll();

  LONG a;
  for (a = 0; a < GetPolygonCount(); a++)
  {
    PolyInfo* p = e->GetPolyInfo(a);
    if (!p) continue;

    if (pSel->IsSelected(p->edge[0])) ope->Select(a * 4);
    if (pSel->IsSelected(p->edge[1])) ope->Select(a * 4 + 1);
    if (pPolys[a].c != pPolys[a].d)
    {
      if (pSel->IsSelected(p->edge[2])) ope->Select(a * 4 + 2);
    }
    if (pSel->IsSelected(p->edge[3])) ope->Select(a * 4 + 3);
  }
  return TRUE;
}


BaseObject *GetVirtualLineObject(BaseObject *op, HierarchyHelp *hh, const Matrix &mloc, Bool keep_spline, Bool recurse, Matrix *mres, Bool *dirty)
{
	return C4DOS.Bo->GetVirtualLineObject(op,hh,mloc,keep_spline,recurse,mres,dirty);
}

void BaseObject::Touch(void)
{
	C4DOS.Bo->Touch(this);
}

UVWTag *GenerateUVW(BaseObject *op, const Matrix &opmg, TextureTag *tp, const Matrix &texopmg, BaseView *view)
{
	return C4DOS.Bo->GenerateUVW(op,opmg,tp,texopmg,view);
}

void CutReal(BaseContainer &data, LONG id, Real min, Real max)
{
	data.SetReal(id,FCut(data.GetReal(id),min,max));
}

void CutVector(BaseContainer &data, LONG id, Real min, Real max)
{
	Vector v = data.GetVector(id);
	data.SetVector(id,Vector(FCut(v.x,min,max),FCut(v.y,min,max),FCut(v.z,min,max)));
}

BaseObject *BaseObject::Alloc(LONG type)
{
	return C4DOS.Bo->Alloc(type,0);
}

void BaseObject::Free(BaseObject *&bl)
{
	C4DOS.Bl->Free(bl);
	bl=NULL;
}

CameraObject *CameraObject::Alloc()
{
	return (CameraObject*)C4DOS.Bo->Alloc(Ocamera,0);
}

void CameraObject::Free(CameraObject *&bl)
{
	C4DOS.Bl->Free(bl);
	bl=NULL;
}

LineObject *LineObject::Alloc(LONG pcnt, LONG scnt)
{
	LineObject *op = (LineObject*)BaseObject::Alloc(Oline);
	if (!op) return NULL;

	if (!op->MakeVariableTag(Tpoint,pcnt)) goto Error;
	if (!op->MakeVariableTag(Tsegment,scnt)) goto Error;
	op->Message(MSG_UPDATE);

	return op;
Error:
	blDelete(op);
	return NULL;
}

void LineObject::Free(LineObject *&bl)
{
	C4DOS.Bl->Free(bl);
	bl=NULL;
}

PolygonObject *PolygonObject::Alloc(LONG pcnt, LONG vcnt)
{
	PolygonObject *op = (PolygonObject*)BaseObject::Alloc(Opolygon);
	if (!op) return NULL;
	if (!op->ResizeObject(pcnt,vcnt)) blDelete(op);
	return op;
}

void PolygonObject::Free(PolygonObject *&bl)
{
	C4DOS.Bl->Free(bl);
	bl=NULL;
}

SplineObject *SplineObject::Alloc(LONG pcnt, LONG type)
{
	return C4DOS.Bo->AllocSplineObject(pcnt,type);
}

void SplineObject::Free(SplineObject *&bl)
{
	C4DOS.Bl->Free(bl);
	bl=NULL;
}

PluginObject *PluginObject::Alloc(LONG type)
{
	return (PluginObject*)C4DOS.Bo->Alloc(type,0);
}

void PluginObject::Free(PluginObject *&bl)
{
	C4DOS.Bl->Free(bl);
	bl=NULL;
}

void BaseObject::SetColorProperties(ObjectColorProperties *prop)
{
	C4DOS.Bo->SetColorProperties(this,prop);
}

void BaseObject::GetColorProperties(ObjectColorProperties *prop)
{
	C4DOS.Bo->GetColorProperties(this,prop);
}

BaseObject *BaseObject::GetCacheParent(void)
{
	return C4DOS.Bo->GetCacheParent(this); 
}

Bool CheckDisplayFilter(BaseObject *op, LONG filter)
{
	return C4DOS.Bo->CheckDisplayFilter(op,filter);
}

