//
//	Copyright © 2005 Nemetschek North America 
//	All Rights Reserved.
//
// Created by Vladislav Stanev

#include "StdAfx.h"
//#include "VWPolygon2DObj.h"

using namespace VWFC::Math;
using namespace VWFC::VWObjects;

VWPolygon2DObj::VWPolygon2DObj()
{
	fhObject		= ::GS_CreatePolyshape( gCBP );
	VWFC_ASSERT( fhObject );
	if ( fhObject == NULL )
		THROW_VWFC_EXCEPTION( VWCannotCreateObjectException, 0, "cannot create polygon2D" );
}

VWPolygon2DObj::VWPolygon2DObj(const VWPolygon2D& poly)
{
	fhObject		= ::GS_CreatePolyshape( gCBP );
	size_t	len		= poly.GetVertexCount();
	for(size_t i=0; i<len; i++) {
		VWPoint2D	pt	= poly.GetVertexAt( i );
		switch( poly.GetVertexType( i ) ) {
			case eVertexType_Corner:	this->AddCornerVertex( pt, false );	break;
			case eVertexType_Bezier:	this->AddBezierVertex( pt, false ); break;
			case eVertexType_Cubic:		this->AddCubicVertex( pt, false ); break;
			case eVertexType_Arc:		this->AddArcVertex( pt, poly.GetArcVertexRadius(i), false ); break;
			default:	VWFC_ASSERT( false );	break;
		}
	}

	// hide vertices if that is desire
	bool	bHaveHiddenVerts		= false;
	for(size_t i=0; i<len; i++) {
		bool	bVis	= poly.GetVertexVisible( i );
		if ( ! bVis ) {
			bHaveHiddenVerts	= true;
			break;
		}
	}

	if ( bHaveHiddenVerts ) {
		for(size_t i=0; i<len; i++) {
			this->SetVertexVisible( i, poly.GetVertexVisible( i ) );
		}

		// if the poly is not closed override the last vertex visibility to false
		if ( ! poly.IsClosed() )
			this->SetVertexVisible( len-1, false );
	}
	else {
		::GS_SetPolyShapeClose( gCBP, fhObject, poly.IsClosed() );
	}

	VWFC_ASSERT( fhObject );
	if ( fhObject == NULL )
		THROW_VWFC_EXCEPTION( VWCannotCreateObjectException, 0, "cannot create polygon2D" );
}

VWPolygon2DObj::VWPolygon2DObj(MCObjectHandle hPoly2D)
{
	VWFC_ASSERT( VWPolygon2DObj::IsPoly2DObject( hPoly2D ) );
	if ( ! VWPolygon2DObj::IsPoly2DObject( hPoly2D ) )
		THROW_VWFC_EXCEPTION( VWBadObjectTypeException, 0, "bad handle type when creating" );
	fhObject		= hPoly2D;
	VWFC_ASSERT( fhObject != nil );
}

VWPolygon2DObj::VWPolygon2DObj(const VWPolygon2DObj& src)
{
	fhObject		= src.fhObject;
}

VWPolygon2DObj::VWPolygon2DObj(const VWRectangle2D srcRect)
{
	fhObject	= ::GS_CreatePolyshape( gCBP );

	this->AddCornerVertex(srcRect.GetTopLeft());
	this->AddCornerVertex(srcRect.GetRight(), srcRect.GetTop());
	this->AddCornerVertex(srcRect.GetBottomRight());
	this->AddCornerVertex(srcRect.GetLeft(), srcRect.GetBottom());

	::GS_SetPolyShapeClose( gCBP, fhObject, true );

	
	VWFC_ASSERT( fhObject );
	if ( fhObject == NULL )
		THROW_VWFC_EXCEPTION( VWCannotCreateObjectException, 0, "cannot create polygon2D" );


}

VWPolygon2DObj::~VWPolygon2DObj()
{
}

VWPolygon2DObj& VWPolygon2DObj::operator=(const VWPolygon2DObj& src)
{
	fhObject		= src.fhObject;
	return *this;
}

bool VWPolygon2DObj::IsPoly2DObject(MCObjectHandle hObj)
{
	short	type	= ::GS_GetObjectType( gCBP, hObj );
	return type == kPolygonNode || type == kPolylineNode || type == kBoxNode;
}

bool VWPolygon2DObj::IsPolygon2DObject(MCObjectHandle hObj)
{
	short	type	= ::GS_GetObjectType( gCBP, hObj );
	return type == kPolygonNode || type == kBoxNode;
}

bool VWPolygon2DObj::IsPolyline2DObject(MCObjectHandle hObj)
{
	short	type	= ::GS_GetObjectType( gCBP, hObj );
	return type == kPolylineNode;
}

void VWPolygon2DObj::AddVertex(double x, double y, bool bRecalcBounds /*=true*/)
{
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	if ( type == kBoxNode ) {
		WorldRect	rect;
		::GS_GetObjectBounds( gCBP, fhObject, rect );
		rect.Unite( WorldPt( x, y ) );
		::GS_SetObjectBounds( gCBP, fhObject, rect );
	}
	else {
		::GS_AddVertex( gCBP, fhObject, WorldPt(x,y), vtCorner, 0, bRecalcBounds );
	}
}

void VWPolygon2DObj::AddVertex(const VWPoint2D& pt, bool bRecalcBounds/*=true*/)
{
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	if ( type == kBoxNode ) {
		WorldRect	rect;
		::GS_GetObjectBounds( gCBP, fhObject, rect );
		rect.Unite( pt );
		::GS_SetObjectBounds( gCBP, fhObject, rect );
	}
	else {
		::GS_AddVertex( gCBP, fhObject, pt, vtCorner, 0, bRecalcBounds );
	}
}

void VWPolygon2DObj::AddCornerVertex(double x, double y, bool bRecalcBounds /*=true*/ )
{
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	if ( type == kBoxNode ) {
		WorldRect	rect;
		::GS_GetObjectBounds( gCBP, fhObject, rect );
		rect.Unite( WorldPt( x, y ) );
		::GS_SetObjectBounds( gCBP, fhObject, rect );
	}
	else {
		::GS_AddVertex( gCBP, fhObject, WorldPt(x,y), vtCorner, 0, bRecalcBounds );
	}
}

void VWPolygon2DObj::AddCornerVertex(const VWPoint2D& pt, bool bRecalcBounds /*=true*/ )
{
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	if ( type == kBoxNode ) {
		WorldRect	rect;
		::GS_GetObjectBounds( gCBP, fhObject, rect );
		rect.Unite( pt );
		::GS_SetObjectBounds( gCBP, fhObject, rect );
	}
	else {
		::GS_AddVertex( gCBP, fhObject, pt, vtCorner, 0, bRecalcBounds );
	}
}

void VWPolygon2DObj::InsertCornerVertex(size_t beforeIndex, const VWPoint2D& pt, bool bRecalcBounds /*=true*/)
{
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	VWFC_ASSERT( type != kBoxNode );

	short	bIndex	= DemoteTo<short>( kVStanev, beforeIndex );
	::GS_InsertVertex( gCBP, fhObject, pt, bIndex+1, vtCorner, 0, bRecalcBounds );
}

void VWPolygon2DObj::SetCornerVertex(size_t index, const VWPoint2D& pt, bool bRecalcBounds /*=true*/)
{
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	if ( type == kBoxNode ) {
		WorldRect	rect;
		::GS_GetObjectBounds( gCBP, fhObject, rect );
		rect.Unite( pt );
		::GS_SetObjectBounds( gCBP, fhObject, rect );
	}
	else {
		short	ind	= DemoteTo<short>( kVStanev, index );
		::GS_SetVertex( gCBP, fhObject, ind+1, pt, vtCorner, 0, bRecalcBounds );
	}
}

void VWPolygon2DObj::AddBezierVertex(const VWPoint2D& pt, bool bRecalcBounds /*=true*/)
{
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	VWFC_ASSERT( type != kBoxNode );

	::GS_AddVertex( gCBP, fhObject, pt, vtBezier, 0, bRecalcBounds );
}

void VWPolygon2DObj::InsertBezierVertex(size_t beforeIndex, const VWPoint2D& pt, bool bRecalcBounds /*=true*/)
{
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	VWFC_ASSERT( type != kBoxNode );

	short	bIndex	= DemoteTo<short>( kVStanev, beforeIndex );
	::GS_InsertVertex( gCBP, fhObject, pt, bIndex+1, vtBezier, 0, bRecalcBounds );
}

void VWPolygon2DObj::SetBezierVertex(size_t index, const VWPoint2D& pt, bool bRecalcBounds /*=true*/)
{
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	VWFC_ASSERT( type != kBoxNode );

	short	ind	= DemoteTo<short>( kVStanev, index );
	::GS_SetVertex( gCBP, fhObject, ind+1, pt, vtBezier, 0, bRecalcBounds );
}

void VWPolygon2DObj::AddCubicVertex(const VWPoint2D& pt, bool bRecalcBounds /*=true*/)
{
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	VWFC_ASSERT( type != kBoxNode );

	::GS_AddVertex( gCBP, fhObject, pt, vtCubic, 0, bRecalcBounds );
}

void VWPolygon2DObj::InsertCubicVertex(size_t beforeIndex, const VWPoint2D& pt, bool bRecalcBounds /*=true*/)
{
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	VWFC_ASSERT( type != kBoxNode );

	short	bIndex	= DemoteTo<short>( kVStanev, beforeIndex );
	::GS_InsertVertex( gCBP, fhObject, pt, bIndex+1, vtCubic, 0, bRecalcBounds );
}

void VWPolygon2DObj::SetCubicVertex(size_t index, const VWPoint2D& pt, bool bRecalcBounds /*=true*/)
{
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	VWFC_ASSERT( type != kBoxNode );

	short	ind	= DemoteTo<short>( kVStanev, index );
	::GS_SetVertex( gCBP, fhObject, ind+1, pt, vtCubic, 0, bRecalcBounds );
}

void VWPolygon2DObj::AddArcVertex(const VWPoint2D& pt, double dRadius, bool bRecalcBounds /*=true*/)
{
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	VWFC_ASSERT( type != kBoxNode );

	::GS_AddVertex( gCBP, fhObject, pt, vtArc, dRadius, bRecalcBounds );
}

void VWPolygon2DObj::InsertArcVertex(size_t beforeIndex, const VWPoint2D& pt, double dRadius, bool bRecalcBounds /*=true*/)
{
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	VWFC_ASSERT( type != kBoxNode );

	short	bIndex	= DemoteTo<short>( kVStanev, beforeIndex );
	::GS_InsertVertex( gCBP, fhObject, pt, bIndex+1, vtArc, dRadius, bRecalcBounds );
}

void VWPolygon2DObj::SetArcVertex(size_t index, const VWPoint2D& pt, double dRadius, bool bRecalcBounds /*=true*/)
{
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	VWFC_ASSERT( type != kBoxNode );

	short	ind	= DemoteTo<short>( kVStanev, index );
	::GS_SetVertex( gCBP, fhObject, ind+1, pt, vtArc, dRadius, bRecalcBounds );
}

size_t VWPolygon2DObj::GetVertexCount() const
{
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	if ( type == kBoxNode )
		return 4;

	return (size_t) ::GS_CountVertices( gCBP, fhObject );
}

void VWPolygon2DObj::DeleteVertex(size_t vertIndex)
{
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	VWFC_ASSERT( type != kBoxNode );

	short	index	= DemoteTo<short>( kVStanev, vertIndex );
	::GS_DeleteVertex( gCBP, fhObject, index );
}

VWPoint2D VWPolygon2DObj::GetVertexPoint(size_t index) const
{
	WorldPt		vertex;
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	if ( type == kBoxNode ) {
		WorldRect	rect;
		::GS_GetObjectBounds( gCBP, fhObject, rect );
		switch( index ) {
			case 0: vertex	= rect.TopLeft();	break;
			case 1: vertex	= rect.TopRight();	break;
			case 2: vertex	= rect.BotRight();	break;
			case 3: vertex	= rect.BotLeft();	break;
			default:	VWFC_ASSERT( false );	break;
		}
	}
	else {
		short		ind		= DemoteTo<short>( kVStanev, index );
		VertexType	type;
		WorldCoord	radius;
		::GS_GetVertex( gCBP, fhObject, ind+1, vertex, type, radius );
	}
	return vertex;
}

double VWPolygon2DObj::GetVertexRadius(size_t index) const
{
	short	objType		= ::GS_GetObjectType( gCBP, fhObject );
	VWFC_ASSERT( objType != kBoxNode );

	short		ind		= DemoteTo<short>( kVStanev, index );
	WorldPt		vertex;
	VertexType	type;
	WorldCoord	radius;
	::GS_GetVertex( gCBP, fhObject, ind+1, vertex, type, radius );
	VWFC_ASSERT( type == vtArc );
	return radius;
}

bool VWPolygon2DObj::IsVertexCorner(size_t index) const
{
	short	objType		= ::GS_GetObjectType( gCBP, fhObject );
	if ( objType == kBoxNode )
		return true;

	short		ind		= DemoteTo<short>( kVStanev, index );
	WorldPt		vertex;
	VertexType	type;
	WorldCoord	radius;
	::GS_GetVertex( gCBP, fhObject, ind+1, vertex, type, radius );
	return type == vtCorner;
}

bool VWPolygon2DObj::IsVertexBezier(size_t index) const
{
	short	objType		= ::GS_GetObjectType( gCBP, fhObject );
	if ( objType == kBoxNode )
		return false;

	short		ind		= DemoteTo<short>( kVStanev, index );
	WorldPt		vertex;
	VertexType	type;
	WorldCoord	radius;
	::GS_GetVertex( gCBP, fhObject, ind+1, vertex, type, radius );
	return type == vtBezier;
}

bool VWPolygon2DObj::IsVertexCubic(size_t index) const
{
	short	objType		= ::GS_GetObjectType( gCBP, fhObject );
	if ( objType == kBoxNode )
		return false;

	short		ind		= DemoteTo<short>( kVStanev, index );
	WorldPt		vertex;
	VertexType	type;
	WorldCoord	radius;
	::GS_GetVertex( gCBP, fhObject, ind+1, vertex, type, radius );
	return type == vtCubic;
}

bool VWPolygon2DObj::IsVertexArc(size_t index) const
{
	short	objType		= ::GS_GetObjectType( gCBP, fhObject );
	if ( objType == kBoxNode )
		return false;

	short		ind		= DemoteTo<short>( kVStanev, index );
	WorldPt		vertex;
	VertexType	type;
	WorldCoord	radius;
	::GS_GetVertex( gCBP, fhObject, ind+1, vertex, type, radius );
	return type == vtArc;
}

// Identical to GetVertex except that it returns the Bezier control point for cubic spline sections.
VWPoint2D VWPolygon2DObj::GetControlVertex(size_t index) const
{
	short	objType		= ::GS_GetObjectType( gCBP, fhObject );
	VWFC_ASSERT( objType != kBoxNode );

	short		ind		= DemoteTo<short>( kVStanev, index );
	WorldPt		vertex;
	VertexType	type;
	WorldCoord	radius;
	::GS_GetControlVertex( gCBP, fhObject, ind+1, vertex, type, radius );
	return vertex;
}

bool VWPolygon2DObj::IsVertexVisible(size_t index) const
{
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	if ( type == kBoxNode )
		return true;

	short		ind		= DemoteTo<short>( kVStanev, index );
	Boolean		vis		= false;
	::GS_GetVertexVis( gCBP, fhObject, ind, vis );
	return vis ? true : false;
}

void VWPolygon2DObj::SetVertexVisible(size_t index, bool bVisible)
{
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	if ( type != kBoxNode ) {
		short		ind		= DemoteTo<short>( kVStanev, index );
		::GS_SetVertexVis( gCBP, fhObject, ind, bVisible );
	}
}

bool VWPolygon2DObj::IsClosed() const
{
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	if ( type == kBoxNode )
		return true;

	return ::GS_GetPolyShapeClose( gCBP, fhObject ) ? true : false;
}

void VWPolygon2DObj::SetClosed(bool bClosed)
{
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	if ( type != kBoxNode ) {
		::GS_SetPolyShapeClose( gCBP, fhObject, bClosed );
	}
}

void VWPolygon2DObj::GetPolygon(VWPolygon2D& outPoly) const
{
	outPoly.ClearVertices();
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	if ( type == kBoxNode ) {
		WorldRect	rect;
		::GS_GetObjectBounds( gCBP, fhObject, rect );

		outPoly.AddVertex( rect.TopLeft() );
		outPoly.AddVertex( rect.TopRight() );
		outPoly.AddVertex( rect.BotRight() );
		outPoly.AddVertex( rect.BotLeft() );
	}
	else {
		short	len	= ::GS_CountVertices( gCBP, fhObject );
		for(short i=0; i<len; i++) {
			WorldPt		vertex;
			VertexType	type;
			WorldCoord	radius;
			::GS_GetVertex( gCBP, fhObject, i+1, vertex, type, radius );
			Boolean		vis		= false;
			::GS_GetVertexVis( gCBP, fhObject, i, vis );

			outPoly.AddVertex( vertex, (EVertexType) type, radius );
			outPoly.SetVertexVisible( i, vis );
		}

		outPoly.SetClosed( ::GS_GetPolyShapeClose( gCBP, fhObject ) );
	}
}

void VWPolygon2DObj::GetPolygon(VWPolygon3D& outPoly) const
{
	outPoly.ClearVertices();
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	if ( type == kBoxNode ) {
		WorldRect	rect;
		::GS_GetObjectBounds( gCBP, fhObject, rect );

		WorldPt		vertex;
		vertex				= rect.TopLeft();
		outPoly.AddVertex( vertex.x, vertex.y, 0 );
		vertex				= rect.TopRight();
		outPoly.AddVertex( vertex.x, vertex.y, 0 );
		vertex				= rect.BotRight();
		outPoly.AddVertex( vertex.x, vertex.y, 0 );
		vertex				= rect.BotLeft();
		outPoly.AddVertex( vertex.x, vertex.y, 0 );
	}
	else {
		short	len	= ::GS_CountVertices( gCBP, fhObject );
		for(short i=0; i<len; i++) {
			WorldPt		vertex;
			VertexType	type;
			WorldCoord	radius;
			::GS_GetVertex( gCBP, fhObject, i+1, vertex, type, radius );

			outPoly.AddVertex( vertex.x, vertex.y, 0 );
		}

		outPoly.SetClosed( ::GS_GetPolyShapeClose( gCBP, fhObject ) );
	}
}

void VWPolygon2DObj::ConvertToPolyline()
{
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	if ( type != kPolylineNode )
		fhObject	= ::GS_ConvertToPolyline( gCBP, fhObject, false );
}

void VWPolygon2DObj::ConvertToPolygon(short res)
{
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	if ( type != kPolygonNode ) {
		if ( res == 0 )
			fhObject	= ::GS_ConvertToPolygon( gCBP, fhObject, false );
		else
			fhObject	= ::GS_ConvertToPolygonN( gCBP, fhObject, false, res );
	}
}

void VWPolygon2DObj::AddHoleObject(const VWPolygon2D& poly)
{
	this->ConvertToPolyline();

	VWPolygon2DObj	holeObj( poly );
	holeObj.ConvertToPolyline();
	::GS_AttachAuxObject( gCBP, (MCObjectHandle) holeObj, fhObject );
}

void VWPolygon2DObj::AddHoleObject(const VWPolygon2DObj& poly)
{
	this->ConvertToPolyline();

	VWPolygon2DObj	holeObj( poly );
	holeObj.ConvertToPolyline();
	::GS_AttachAuxObject( gCBP, (MCObjectHandle) holeObj, fhObject );
}

size_t VWPolygon2DObj::GetHoleObjectsCount() const
{
	// only polylines can have openings
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	if ( type != kPolylineNode )
		return 0;

	size_t			count	= 0;
	MCObjectHandle	hHole	= ::GS_FindAuxObject( gCBP, fhObject, kPolylineNode );
	while( hHole ) {
		count++;
		MCObjectHandle	hNextAux	= ::GS_NextObject( gCBP, hHole );
		hHole						= ::GS_NextAuxObject( gCBP, hNextAux, kPolylineNode );
	}
	return count;
}

MCObjectHandle VWPolygon2DObj::GetHoleObjectAt(size_t at)
{
	MCObjectHandle	hHole	= NULL;

	// only polylines can have openings
	short	type	= ::GS_GetObjectType( gCBP, fhObject );
	if ( type == kPolylineNode ) {
		size_t		count	= 0;
		hHole				= ::GS_FindAuxObject( gCBP, fhObject, kPolylineNode );
		while( hHole ) {
			if ( at == count )
				break;
			count++;
			MCObjectHandle	hNextAux	= ::GS_NextObject( gCBP, hHole );
			hHole						= ::GS_NextAuxObject( gCBP, hNextAux, kPolylineNode );
		}
	}

	return hHole;
}
